Post on 29-Dec-2015
2
OO Design Goals
FlexibilityChanges must be localized
MaintainabilityModules requiring changes can be easily identified and changes can be made at their place and cost
ReusabilityModules should be able to be used in other contexts
4
Class design principles
ORR One Responsibility Rule LSP Liskov Substitution Principle LoD Law of Demeter OCP Open-Closed Principle ISP Interface Segregation Principle
5
ORR - One Responsibility Rule
A class has a single responsibility:
it does it all, it does it well, it does it only
- R. Martin
6
LSP – Liskov Substitution Principle
Functions that use pointers or references to base classes must be able to use objects of the derived classes without knowing it.
- R.Martin, 1996
Original formula: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T the behavior of P is unchanged when o1 is substituted by o2 then S is a subtype of T.
- Barbara Liskov, 1988
7
LSP violation: Rectangle & Square
Problem:Square s = new Square(5);s.setHeight(6); // s is not consistent
class Rectangle{ private int h; private int w; public Rectangle( int w, int h ) { this.h = h; this.w = w; } public void setHeight( int h ) { this.h = h; } public int getHeight() { return h; } }class Square extends Rectangle{ public Square( int s ) { super( s, s ); }}
Rectangle
Rectangle(w : int, h : int)setHeight(h : int) : voidgetHeight() : intsetWidth(w : int) : voidgetWidth() : int
Square
Square(s : int)
8
class Square extends Rectangle{ public Square( int s ) { super( s, s ); } public void setSize( int s ) { super.setHeight(s); super.setWidth(s); } public void setHeight( int h ) { setSize(h); } public void setWidth( int w ) { setSize(w); }}
Rectangle
Rectangle(w : int, h : int)setHeight(h : int) : voidgetHeight() : intsetWidth(w : int) : voidgetWidth() : int
Square
Square(s : int)setSize(s : int) : voidsetHeight(h : int) : voidsetWidth(w : int) : void
Problem:void f( Rectangle r ) throws Exception { r.setHeight(4); r.setWidth(5); if( r.getHight() * r.getWidth() != 20 ) throw new Exception( “Bug!” );}
9
LSP: The real problem
• ?
• Square object is not a Rectangle object!
• Why?
• Because of behavior of a Square is not consistent with the behavior of a Rectangle!
• and …
• it is behavior that software is really all about! IsA is a behavioral relationship.
10
LoD – Law of Demeter
Original formula: Only talk to your immediate friends. - Ian Holland, 1987
immediate friends of method f :• methods of class of f and other argument classes of f • methods of immediate part classes of class of f • methods of classes of objects that are created in f .
A method should have limited knowledge of an object model.
- D. Rumbaugh
11
Booch about The LoD
“The basic effect of applying this Law is the creation of loosely coupled classes, whose implementation secrets are encapsulated. Such classes are fairly unencumbered, meaning that to understand the meaning of one class, you need not understand the details of many other classes.”
12
LoD violation
Problem: public void getTimeOfBirth() {
long time = p.getDateOfBirth().getTime();
}
Do not reveal a class secret!
Application
getTimeOfBirth()
Person
getDateOfBirth() : java.util.Date
java.util.Date
getTime()
-dateOfBirth
13
LoD-compliant design
A
foo()
C
m()
B
call_foo()
-a
-b
Solution: void m() { this.b.call_foo(); } // hide the class secret!
14
OCP – Open-Closed Principle
Software entities (classes, modules, functions, etc) should be open for extension, but closed for modification.
- B. Meyer, 1988
15
OCP violation: Shapes
Shape
$ SQUARE : int = 1$ CIRCLE : int = 2
getType()
Circle
getType()
Square
getType()
void drawShapes( Shape[] shapes ) { for( int i = 0; i < shapes.length; ++i ) { if( shape[i].getType == Shape.SQUARE ) { drawSquare( (Square)shape[i] ); } else drawCircle( (Circle)shape[i] ); }}
The problem:You can't add a new shape without changing drawShapes() code.
16
OCP compliant solution
Circle
draw()
Square
draw()
Shape
draw(device : DrawDevice) : void
<<Interface>>DrawDevice
<<Interface>>
Triangle
draw()
void drawShapes( Shape[] shapes ) { for( int i=0; i < shapes.length; ++i ) { shape[i].draw( device ); }}
17
ISP – Interface Segregation Principle
Clients should not be forced to depend upon services they do not use.
- R.Martin, 1996
Hints: Avoid fat interfaces Separate clients mean separate interfaces
Violation cost: lack of flexibility
18
ISP violation: Security Door
Door
lock() : voidunlock() : voidis_open() : bool
Timer
register(timeout : int, client : TimerClient) : void
TimerClient
timeout()
<<Interface>>
Door has to sound an alarm if it is open for too long.Problem: Timeout method has to be public. But…there are Door clients that do NOT use timeout method and don’t have to. This approach leads to mistakes.
19
ISP-compliant Security Door
TimerClient
timeout()
<<Interface>>
Door
lock() : voidunlock() : voidis_open() : bool
TimedDoor
Timer
register(timeout : int, client : TimerClient) : void
Door clients still can use TimedDoor via Door interface Door clients will not be affected by changes made in Timer, TimerClient and TimedDoor
20
ISP violation: ATM Transactions
Adding new transactions causes all other transactions to recompile If any Transaction requires a change to UI, all of the other will be forced to recompile
Transaction
commit()rollback()
<<Interface>>
Deposit
Withdraw
Transfer
UI
getDepositAmount()getWithdrawAmount()getTransferAmount()
21
ISP-compliant solution
Transaction<<Interface>>
Withdraw
WithdrawUI<<Interface>>
Transfer
TransferUI<<Interface>>
Deposit
DepositUI<<Interface>>
UI
getDepositAmount()getWithdrawAmount()getTransferAmount()
22
Dependencies
MDP Minimal Dependencies Principle DIP Dependency Inversion Principle ADP Acyclic Dependencies Principle
23
DIP – Dependency Inversion Principle
High level modules should not depend upon low-level ones. Both should depend upon abstractions.
Abstractions should not depend upon implementation details.Details should depend upon abstractions.
- R.Martin, 1996
24
DIP violation: Copier
Keyboard
readChar()
Copy
copy()
Printer
printChar()
What if we need to support another kind of printer ?
25
DIP-compliant solution
Now we can easily add new writers and readers
Keyboard
readChar()
Printer
printChar()
Reader
readChar()
<<Interface>>Copy
copy()Writer
printChar()
<<Interface>>
26
ADP – Acyclic Dependencies Principle
The dependency structure between entities (classes, packages, functions) must be a Directed Acyclic Graph (DAG).
- R.Martin, 1996
Two entities having to know about each other can not be used separately. They work like a monolith and there is no benefit in separating them.
Increases maintainability
27
Example: cyclic dependenciesApplication
MyTasks TaskWindow
Task
Windows
MessageWindow
MyDialogs
Due to the dependency from MyDialogs to Application, MyTasks package depends upon the entire system.
28
Package design principles
CCP - Common Closure Principle REP - Reuse-Release Equivalence Principle CRP - Common Reuse Principle SDP - Stable Dependencies Principle SAP - Stable Abstractions Principle
29
CCP – Common Closure Principle
Classes within the package should be affected by the same kind of changes. Either all open to the kind of change or all closed to the kind of change.
- R.Martin, 1996
Produces packages where changes are very localized, and, therefore, number of releases is minimized.
30
REP - Reuse/Release Equivalence Principle
The unit of reuse is the unit of release. The unit is what UML refers to as a package.
- R.Martin, 1996
Classes should be grouped into packages according to how they will be reused To be effectively reused, packages must be given a release number
Makes updates convinient for reusers
31
CRP – Common Reuse Principle
The classes in a package are to be reused together. Reusers should depend upon the entire package, not just a part of it.
- R.Martin, 1996
ISP, scaled to packages
Reduces maintenance cost, increases reusability
My PackageAnother Package
class I'm NOT using
class i'm using
32
CRP violation: remote service
Problem: Every time the new version of Service is released, clients of ServiceAgent must expect that their code won’t work, even if changes do not affect ServiceAgent.
Service
Service
ServiceServer(from Service)
ServiceImpl(from Service)
Application
Main
ServiceAgent(from Service)
ServiceException(from Service)
33
CRP compliant design
Clients of ServiceAgent depend only upon things they really use.Benefit: Application can easily switch from local to remote service implementation.
serviceagent
server
local
ServiceServiceAgent
Main ServiceImpl
ServiceServer
application
34
CRP compliant design
serviceagent
server
localapplication
Service
Main
ServiceServer
ExceptionServiceAgent
CallbackServiceImpl
Clients of ServiceAgent depend only upon things they really use.Benefit: Changes in local and server packages do not affect application
35
SDP - Stable Dependencies Principle
A package should only depend upon packages that are more stable than itself. Stability is a measure of difficulty in changing a package.
- R.Martin, 1996
What makes a package hard to change?
Increases maintainability
36
Instability Definition
Instability of a package
I = Ce / ( Ca + Ce )where
Ce = efferent couplings (number of classes inside the package that depend upon classes outside the package )
Ca = afferent couplings (number of classes outside the package that depend upon classes within the package)
37
SAP – Stable Abstractions Principle
The abstractness of the package should be proportional to its stability.
- R.Martin, 1996
If all packages are maximally stable, the system would be unchangable . Therefore, some packages must be instable.
Increases maintainability
38
Abstractness Definition
Abstractness of a package
A = Na / Nwhere
Na = number of abstract classesN = total number of classes
39
Main sequence
Main Sequence – an instability versus abstractness graph Packages along the line from (0,1) to (1,0) have a good balance
Distance from the main sequence
D = | A + I – 1 |
Given this metric, a design can be analyzed for its overall conformance to the main sequence.
- R.Martin, 1994
41
Example: server
What happens if ResultSet is not an interface?
AbstractServer
MyServer
Client
Server ResultSet
Code
SomeServer SomeResultSet
I=0 A=1 D=0
I = 1/1 = 1A= 0D= 0
I=2/3 A=0 D=1/3