object-oriented design principles - ntutyccheng/oop2007f/ood principles-v0.4.pdf · object-oriented...
TRANSCRIPT
1
Object-Oriented Design Principles
Chien-Tsun Chen (陳建村)Department of Computer Science and Information EngineeringNational Taipei University of Technology, Taipei 106, Taiwan
[email protected]. 04 2007
2
OOD principles roadmap: What distinguishes good and bad OO design?
Class
Inheritance
polymorphism
Program to an interface, not an implementation
Favor object composition over class inheritance
Separation of concern
Single Responsibility
Open-Closed
Liskov Substitution
Dependency-Inversion
Interface-Segregation
basic advanced
3
Reference
4
Outline
SRP—The Single Responsibility PrincipleOCP—The Open-Closed PrincipleLSP—The Liskov Substitution PrincipleDIP—The Dependency-Inversion PrincipleISP—The Interface-Segregation Principle
5
Example first: Designing a Student class
What responsibilities a Student class should have?
R1: Representing data (name, age, etc)R2: PersistenceR3: Reporting
6
You may end up with the following Student class
R1: Data model
R2: Persistence
R3: Reporting
Does anything wrong with the design?
7
What are the problems of the previous design?
Responsibility couplingViolating separation of concerns
There are many reasons to change the class
+ getName()
+ setName()...
+ save()+ load()
+ toHTML()+ toPDF()+ toWord()...
9
What is Single-Responsibility Principle (SRP)?
Definition: A class should have only one reason to change.
SRP avoids responsibility coupling
10
Applying SRP to the Student class
Data model
+ toHTML()
+ toPDF()
+ toWord()...
Persistence Reporting
11
But my OO text book tells me to simulate real world “things” as classes…
A Student class have to be stored.
It should be reported to users.
So, why these responsibilities cannot be assigned to the Student class?
12
Things in large
The original design is fine, ifThe application has few classes
Think about this:How to persist several different types of classes?
Student, Teacher, School, Test Result, etc.
13
Persistence layer
+CalculatePay()+Store()
Employee
Persistence subsystem
+CalculatePay()
Employee
Broker
Persistence subsystem
coupled persistence
separated persistence
14
Grain size of a class
A class should represents only one abstraction (or a responsibility)
Responsibility coupling harms:ReusabilityModifiabilityTestabilityUnderstandabilityetc
16
Problems of violation SRP in this example
The computational geometry application must include the GUI.If changes to the GraphicalApplicationcause the Rectangle to change, we may need to rebuild, retest, and redeply the ComputationalGeometryApplication.
22
Summary of the Single-Responsibility Principle
SRP: A class should have only one reason to change.
Conjoining responsibilities is something that we do naturally. Finding and separating those responsibilities from one another is much of what software design is really about.
separation of concern
23
Outline
SRP—The Single Responsibility PrincipleOCP—Then Open-Closed PrincipleLSP—The Liskov Substitution PrincipleDIP—The Dependency-Inversion PrincipleISP—The Interface-Segregation Principle
27
A single change to a program results in a cascade of changes to dependent modules
The design depends on a switch-case statement
28
Things to do when you want to add a new type of shape, e.g., Line
Create a new struct/class of LineModify the drawAllshapes methodswitch (s->itsType) {case square: …case circle: …case line: …
}
31
What is Open-Closed Principle (OCP)?
Definition: Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Extend systems by adding new code (the open part), not by changing old code that already works (the closed part).
32
The open and the closed parts
Closed for modification
Open for extension
Closed for modification1
2
3
33
Two primary attributes of the Open- Closed Principle
They are “Open for Extension”:The behavior of the module can be extended. We can make the module behave in new and different ways as the requirements of the application change, or to meet the needs of new applications.
They are “Closed for Modification”:The source code of such a module is inviolate. No one is allowed to make source code changes to it.
35
Abstraction is the Key to OCP (2/2)
Template Method Pattern: Base class is open and closed
Closed
Open
36
Question : What is abstraction?
Thinking about what you can do rather than who you are.
What: ListWho: ArrayList, LinkedList
37
How to achieve the open-closed principle?
Putting the hooks in:Finding hot spots (simulating change)
Using abstraction to gain explicit closureProgramming to interface
Using a data-driven (table-driven) approach to achieve closure
Using Configuration, Register, or Repository
38
Summary of the Open-Closed Principle
OCP: Software entities should be open for extension, but closed for modification.
The OCP is the foundation for building maintainable and reusable systems.
The primary mechanisms behind the OCP are abstraction and polymorphism.
39
Case Study 2
If FTP should be supported, what will you do ?
If you want to upgrade RMH from version A to B…
Bad class name
How to test TransmissionWizard?
40
Outline
SRP—The Single Responsibility PrincipleOCP—The Open-Closed PrincipleLSP—The Liskov Substitution PrincipleDIP—The Dependency-Inversion PrincipleISP—The Interface-Segregation Principle
41
What is the Liskov Substitution (LSP)?
Definition 1: Subtypes must be substitutable for their base types.
Shape myShape;…
myShape = new myCirlce();myShape.draw();…
myShape = new Square();myShape.draw();
+draw()
Shape
+draw()
Circle
+draw()
Square
42
What is the Liskov Substitution (LSP)?
Definition 2: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
Shape myShape;…
myShape = new myCirlce();myShape.draw();…
myShape = new Square();myShape.draw();
+draw()
Shape
+draw()
Circle
+draw()
Square
43
A drawing application violates LSP
+getTypeShape
+Draw+getCenter+getRadius
Circle
+Draw+getTopLeft
Square
44
Modifying the Shape class to satisfy LSP
+Draw()+getType
Shape
+Draw+getCenter+getRadius
Circle
+Draw+getTopLeft
Square
void DrawShape (const Shape& s) {s.Draw();
}
46
A more subtle violation of LSP (2/2)
Square s;g (s);
You will get an assertion violation exception:“Expect 20 but 16.”
Why?
47
What Went Wrong?
The behavior of a Square object is not consistent with the behavior of a Rectangle object. Behaviorally, a Square is not a Rectangle! And it is behavior that software is really all about.The LSP makes clear that in OOD the IS-A relationship pertains to behavior.
48
Applying Design by Contract to conform the LSP
Assertion:Precondition.Postcondition.Class invariant.
The rule for the preconditions and postconditions for derivatives:
when redefining a routine [in a subclass], you may only replace its precondition by a weaker one, and its postcondition by a stronger one.
49
Review the example: the contract is broken in the Square class
postcondition of Rectangle::SetWidth(double w)
The postcondition of Square::SetWidth(double w) is weaker than the postcondition of Rectangle::SetWidth(double w) above Rectangle
Square
assert ((itsWidth == w) && (itsHeight == w))
assert ((itsWidth == w) && (itsHeight == old.itsHeight))
50
Summary of the Liskov Substitution Principle
LSP: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it
The LSP (A.K.A. Subcontracting) is an important feature of all programs that conform to the Open-Closed principle.
52
+Article() : int+show()+setSelect(in location)+set_text()+get_text()
Article
+show()+setSelect(in position : int)+Sentence()+next_positionX()+next_positionY()
Sentence
+show()+set_word(in letter : Letter, in position : int)+Word()
Word
+Letter()
Letter
+setlocation(in location)+getlocation()+show()+changeLetter(in letter : Letter, in position : int)+setSelect()+Text()+get_selected()+get_textarea()
Text
1
*
Case Study 4
54
Outline
SRP—The Single Responsibility PrincipleOCP—The Open-Closed PrincipleLSP—The Liskov Substitution PrincipleDIP—The Dependency-Inversion PrincipleISP—The Interface-Segregation Principle
55
What is the Dependency-Inversion Principle?
Definition a: high-level modules should not depend on low-level modules. Both should depend on abstractions.Definition b: abstractions should not depend on details. Details should depend on abstractions.
This principle is at the heart of framework design
56
A naive layering structure violates the Dependency-Inversion Principle
Policy layer is sensitive to changes all the way down in the Utility layer. Dependency is transitive.
57
A more appropriate model: each of the upper- level layers declares an abstract interface for the services that it needs
the dependency has been inverted
The lower-level layers are realized form the abstract interface
The lower-level layers are realized form the abstract interface
PolicyLayer can be reused in any context that defines lower-level modules that conform to the PolicyServiceInterface
58
Not only the dependency, but also the ownership are inverted
We often think of utility libraries as owning their own interfaces.But when the DIP is applied, the clients tend to own the abstract interfaces and that their servers derive from them.
59
Dependency inversion can be applied wherever on class sends a message to another
Simple model of a Button and a Lamp
Dependency inversion applied to the Lamp
More testable
60
Summary of the Dependency- Inversion Principle
DIP: High- and low-level modules should depend on abstractions, not depend on each other.It is the inversion of dependencies that is the hallmark of good OO design. If a system’s dependencies are inverted, it has an OO design. Otherwise, it has a procedural design.
61
Outline
SRP—The Single Responsibility PrincipleOCP—The Open-Closed PrincipleLSP—The Liskov Substitution PrincipleDIP—The Dependency-Inversion PrincipleISP—The Interface-Segregation Principle
63
A possible design of the TimedDoor class
class abstract Door {public void lock();public void unLock();public boolean isDoorOpen();
}
class Timer {public void register(
int timeout,TimerClient client);
}
interface TimerClient {void timeout ();
}
Door has been polluted with a method that it does not require
We want to implement a TimedDoor class
The design violates Interface- Segregation Principle (ISP)
64
What is the Interface-Segregation Principle?
Definition: clients should not be forced to depend on methods that they do not use
This principle deals with the disadvantages of fat interfaces
65
Two direction of forces that cause changes in software
How changes to interfaces will affect their users
Client may also forces a change to the interface
66
Separate clients mean separate interfaces
If users of Timer require to register more than one time-out request, a false alarm bug may occur
Door interface &TimerClient interface
Door &TimerClient
TimerClientinterface
Door Client
The change will affect all the users of TimerClient. That is ok. But why should a bug in TimerClient have any affect on clients of Door that do not require timing?
67
Separation of interfaces through delegation and multiple inheritance
Door Timer adapter
Multiply inherited Timed Door
68
Summary of the Interface- Segregation Principle
ISP: Clients should not be forced to depend on methods that they do not use.
The ISP breaks the dependence of the clients on methods that they do not invoke, and it allows the clients to be independent of each other.
72
Narrow inheritance interface principle
Behavior that is spread over several methods in a class should be based on a minimal set of methods which have to be overriddenOtherwise clients deriving subclasses would have to overridden many methods to adapt that behaviorOverriding the smaller hook methods has to be
sufficient in order to adapt the more complex template methods
74
Using abstraction to gain explicit closure (1/2)
public void drawAll (Shape [] list) {
for (int i = 0; I < list.length; i++) { list[i].draw();
}}
+draw()
Shape
+draw()
Circle
+draw()
Square
drawAll conforms the Open-Closed Principle since adding new subclass of Shape have not to change its implementation
75
Using abstraction to gain explicit closure (2/2)
public void print (ArrayList list) {….
}
public void print (List list) {….
}
public List get () {
List list = new ArrayList();….
return list;}
public ArrayList get () {
ArrayList list = new ArrayList();….
return list;}
prefer doing soavoid doing so
77
Registering more than one time-out request may cause the Door alarms falsely
Consider the TimedDoor. When it detects that the Door has been opened, it sends the Register message to the Timer, requesting a time-out. However, before the time-out expires, the door closes, remains closed for a while, and then opens again. This causes us to register a new time-out request before the old one has expired. Finally, the first time-out request expires and the TimeOut function of the TimedDoor is invoked. The Door alarms falsely.
78
Change of requirement: Some users of Timer will register more than one time-out request
class Timer {public void register(
int timeout,TimerClient client);
}
interface TimerClient {void timeout();
}
Timer with ID
class Timer {public void register(
int timeout,int timeoutID,TimerClient client);
}
interface TimerClient {void timeout(int timeoutID);
}
The original Timer