computer science 340 software design & testing...
TRANSCRIPT
1
Computer Science 340Software Design & Testing
Inheritance
Template Method PatternFactory Method Pattern
2
Class Reuse• Two forms of class reuse:
– Class implementation inheritance• “extends” in Java• Different than “implements”, which is “interface
inheritance”– Object composition
A
B
AB
Inheritance
Composition
3
Class Inheritance
• This class is like that class except for these differences …
– Specialize superclass behavior by overriding methods• Totally replace a superclass operation• Add pre/post processing before/after
superclass operation– Extend superclass by adding new
variables/operations
A
B
4
Class Inheritance
A
B
• Inheritance establishes a subtyping relationship between subclass and superclass, thus enabling polymorphism
– Polymorphism = Subtyping + Dynamic method binding
– Subclass instances may be used anywhere superclass instances are expected• Liskov Substitution Principle
5
Object Composition• Client class creates an internal instance of existing class
and invokes its functionality through regular method calls
• Generally results in looser coupling than the inheritance relationship– With inheritance, changes to A are more likely to break B than
with composition
• No subtyping relationship is established between the two classes, thus preventing polymorphism
• Extra levels of indirection in method calls can reduce efficiency, but this usually isn�t a problem
AB
6
Choosing Between Composition Inheritance
• What type of relationship is being modeled?– B “has-a” A => composition– B “uses” A => composition– B “is-a” A => inheritance
• Specialization
7
Choosing Between Composition and Inheritance
• �Favor object composition over class inheritance.� [Design Patterns, pg. 20]
• Composition is:– More flexible than inheritance
– Leads to lower coupling than inheritance– Allows control over which delegate
features are exposed, and what the API looks like
– Often more complex than inheritance
8
Choosing Between Composition and Inheritance
• Inheritance:– Supports polymorphism, while composition
does not– Is easier if you want to expose many of the
superclass� features• Although Intellij and Eclipse both have a handy �Generate Delegate Methods� option for exposing delegate features if you�re using composition
9
Dynamic Inheritance
• Dynamic inheritance is when you have objects that change class over time
• Dynamic inheritance can be implemented using a combination of composition and interface inheritance
10
Dynamic Inheritance
• Example: Vocations– This inheritance-based design is inflexible because once a
person has been created, their vocation cannot change
Person
Engineer Doctor Lawyer SalesPerson
11
Dynamic Inheritance• Example: Vocations
– This design uses a combination of composition (with delegation) and interface inheritance to allow a person’s vocation to change over their lifetime
<<Interface>>
IPerson
Engineer Doctor Lawyer SalesPerson
Person
<<Interface>>
IPerson
12
Dynamic Inheritance
• Another example of dynamic inheritance
Employee
CommissionedEmployee
SalariedEmployee
HourlyEmployee
<<Interface>>
IEmployee
HourlyEmployee
CommissionedEmployee
SalariedEmployee
Employee
<<Interface>>
IEmployee
13
Multiple Inheritance• Multiple inheritance is when an object is a member
of multiple classes at once
Instructor Student
PhdStudent
14
Multiple Inheritance
<<Interface>>
IInstructor
Instructor
PhdStudent
<<Interface>>
IInstructor
<<Interface>>
IStudent
Student
<<Interface>>
IStudent• What if you need multiple inheritance, but your programming language does not support it (like Java)?
• This design uses a combination of composition and interface inheritance to allow a PhdStudent to be both an Instructor and a Student
• PhdStudent delegates method calls to Instructor and Student
15
Multiple Inheritance• Hybrid approach
• Use implementation inheritance for one super-class, and interface inheritance + composition for the other super-classes
• Which one should I use for implementation inheritance? The one that most closely matches the ”is-a” rule.
PhdStudent
Student
<<Interface>>
IInstructor
Instructor
<<Interface>>
IInstructor
16
Inheritance-Based Reuse• Inheritance is a more tightly coupled relationship than composition
– Inheritance is sometimes called “white box reuse”, and composition is called “black box reuse”
• Unwise programmers, when they realize they need to subclass an existing class, make all the “private” features “protected”, make all the methods “virtual”, and say, “There, now it’s a base class!”.
• This approach results in extremely high coupling between a base class and its subclasses, and results in a fragile base class (i.e., a base class that is difficult to change without breaking its subclasses)
• To minimize coupling, the “subclass interface” between a base class and its subclasses should be carefully designed
• Information hiding is still a good practice, even between super- and sub-classes
17
Designing the Subclass Interface
• Subclasses need to:
– Access base class features in ways not available through the public interface
– Specialize base class behavior
Public Interface
Subclass Interface
Base Class
Sub Class
Client Client
18
Designing the Subclass Interface• Base classes should typically
keep their variables private, and provide protected methods that allow subclasses to access or modify their state only in necessary, controlled ways
• Make a variable protected only if there is a good reason to do so
Public Interface
Subclass Interface
Base Class
Sub Class
Client Client
19
Designing the Subclass Interface• Keep methods private when
subclasses don’t need to access or override them
• Base classes should define polymorphic methods for aspects of their behavior that subclasses can specialize
– Virtual methods in C++– All non-final methods in Java
• Make a method polymorphic only if you expect subclasses to specialize it
• Specifically prevent specialization of methods that subclasses should not override
– Non-virtual methods in C++– “final” methods in Java
Public Interface
Subclass Interface
Base Class
Sub Class
Client Client
20
Designing the Subclass Interface• Keeping the subclass interface
as simple as possible has two positive outcomes:
– There is more freedom to change the internal implementation of the base class without breaking subclasses (i.e., base classes are less fragile)
– It is easier for subclass authors to understand how to specialize the base class (i.e., they have less freedom, but more guidance)
Public Interface
Subclass Interface
Base Class
Sub Class
Client Client
21
Patterns for Designing the Subclass Interface
• Template Method pattern• Factory Method pattern
22
Template Method Pattern• Code that is duplicated in multiple places should be centralized
in one place (i.e., avoid duplication)– Composition: Put common code in a method on a class to which
multiple clients will delegate– Inheritance: Put the common code in a method on a super-class,
and make the clients sub-classes (i.e., clients inherit common code)
• What if an algorithm is duplicated in several places, but the copies are SIMILAR rather than IDENTICAL?
• Use the Template Method pattern– Put the common algorithm in a super-class– Clients inherit common code from super-class– Some steps of the algorithm are delegated to subclasses through
polymorphic method calls– Subclasses customize the algorithm by implementing the delegated
steps
Template Method Pattern
23
24
/*** An abstract class that is common to several games in* which players play against the others, but only one is* playing at a given time.*/
abstract class Game {
protected int playersCount;
/* A template method : */public final void playOneGame(int playersCount) {this.playersCount = playersCount;initializeGame();int j = 0;while (!endOfGame()) {
makePlay(j);j = (j + 1) % playersCount;
}printWinner();
}
abstract void initializeGame();abstract void makePlay(int player);abstract boolean endOfGame();abstract void printWinner();
}
//Now we can extend this class in order //to implement actual games:
classMonopoly extends Game {
/* Specific declarations for the Monopoly game. */
/* Implementation of abstract methods */void initializeGame() {
// Initialize players// Initialize money
}
void makePlay(int player) {// Process one turn of player
}
boolean endOfGame() {// Return true if game is over // according to Monopoly rules
}
void printWinner() {// Display who won
}
/* Specific methods for the Monopoly game. */// ...
}
Refactoring with the Template Method Pattern
1. Make the similar code as similar as possible in the classes that duplicate it
– Similar code diverges unnecessarily over time
2. Create a common base class for all classes with similar code
3. Put one copy of the similar code/method(s) in the parent (this becomes the template method)
4. Identify and extract what needs to vary by subclass– Create abstract methods for the code that needs to vary
– Replace the code that needs to vary with calls to the abstract methods in the template method
5. Override the abstract methods in the subclasses with their version of the varying code (pulled out of their copy of the template method override(s))
6. Delete the template method override(s) in the subclasses 25
26
Factory Method Pattern
• Factory Method pattern– A super-class contains useful functionality that can be
inherited by sub-classes
– The super-class needs to instantiate an object to do its work, but it is a general class and has many potential uses. Therefore, it doesn’t know the concrete class of the object it needs so it can’t instantiate it
– Instantiation of the object is delegated to sub-classes, which do know which concrete class to instantiate
Factory Method Pattern
27
28
interface Vehicle{public void drive();
}
class Car implements Vehicle{
@Overridepublic void drive(){System.out.println("Driving a
car...");}
}
class Bus implements Vehicle{
@Overridepublic void drive(){System.out.println("Driving a
Bus...");}
}
abstract class VehicleDriver{
public void driveVehicle(){Vehicle v = makeVehicle();v.drive();
}
public abstract Vehicle makeVehicle();}
class CarDriver extends VehicleDriver{
@Overridepublic Vehicle makeVehicle(){return new Car();
}}
class BusDriver extends VehicleDriver{
@Overridepublic Vehicle makeVehicle(){return new Bus();
}}
29
public class FactoryMethodPattern {
public static void main(String[] args) {handleVehicle(new CarDriver());handleVehicle(new BusDriver());
}
static void handleVehicle(VehicleDriver vDriver){System.out.println("Handling a new vehicle.");vDriver.driveVehicle();
}}
Handling a new vehicle. Driving a car...Handling a new vehicle. Driving a Bus...