Software Design Principles August 19, 2005. Software design principles The single-responsibility...
-
Upload
dwayne-denis-cobb -
Category
Documents
-
view
217 -
download
1
Transcript of Software Design Principles August 19, 2005. Software design principles The single-responsibility...
Software design principlesSoftware design principles
The single-responsibility principleThe single-responsibility principle The open-closed principle The open-closed principle The Liskov substitution principleThe Liskov substitution principle The dependency inversion The dependency inversion
principleprinciple The interface segregation The interface segregation
principleprinciple
The single-responsibility principleThe single-responsibility principle
““A class should have only one A class should have only one reason to change.” -- Robert reason to change.” -- Robert MartinMartin
A responsibility = a reason to A responsibility = a reason to changechange
Separate coupled responsibilities Separate coupled responsibilities into separate classesinto separate classes
The single-responsibility principleThe single-responsibility principle
Example: Example: – Often we need to sort students by their name, or Often we need to sort students by their name, or
ssn. So one may make Class Student implement ssn. So one may make Class Student implement the Java Comparable interface.the Java Comparable interface.
class Student implements Comparable {class Student implements Comparable {……int compareTo(Object o) { … }int compareTo(Object o) { … }……
};};– Student is a business entity, it does not know in Student is a business entity, it does not know in
what order it should be sorted since the order of what order it should be sorted since the order of sorting is imposed by the client of Student.sorting is imposed by the client of Student.
– Worse: every time students need to be ordered Worse: every time students need to be ordered differently, we have to recompile Student and all differently, we have to recompile Student and all its client.its client.
– Cause of the problems: we bundled two separate Cause of the problems: we bundled two separate responsibilities (i.e., student as a business entity responsibilities (i.e., student as a business entity with ordering) into one class – a violation of SRPwith ordering) into one class – a violation of SRP
The single-responsibility The single-responsibility principleprinciple
AClientop() { ;}
RegisterAdd(Course d, Student
s);
Comparable
int compareTo()
StudentSSN
NamegetSSN()
getName()major
getMajor()int compareTo()
It invokesCollections.sort(aListofStudents);
When a new requirement needs to sort students in a different order, Student, Register, and AClient all need to be recompiled, even Register has nothing to do with any ordering of Students.
The single-responsibility The single-responsibility principleprinciple
ClientAop() { ;}
RegisterAdd(Course d, Student
s);Comparator
int compare(Object o1, Object o2)
StudentSSN
NamegetSSN()
getName()major
getMajor()
It invokesCollections.sort(aListofStudents, StudentBySSN);
The solution is to separate the two responsibilities into two separate classes and use another version of Collections.sort().
StudentByNameint compare(Object o1, Object
o2)
StudentBySSNint compare(Object o1, Object
o2)
ClientBop() { ;}
The single-responsibility The single-responsibility principleprinciple
GUI
Computational
GeometryApplication
Rectangle
+draw():void+area():integer
Class Rectangle may be forced to make changes from two different unrelated sources. One is from the Computational Geometry Application (CGA). E.g., adding area function for length and width of type double. The other is from Graphical Application (GA). E.g., add draw() in Windows XP to the existing draw in X Windows. A change from either of the two source would still cause the other application to recompile.
GraphicalApplication
The single-responsibility The single-responsibility principleprinciple
GUI
Computational
GeometryApplication
GraphicRectangle
+draw():void
GraphicalApplication
GeometricRectangle
+area():double
• Package CGA is no longer dependent on graphical side of Rectangle and thus it becomes independent of package GUI. Any change caused by graphical application no longer requires CGA to be recompiled.• However, any changes from the CGA side may cause GA to be recompiled.
The single-responsibility The single-responsibility principleprinciple
GUI
Computational
GeometryApplication
GraphicRectangle
+draw():void
GraphicalApplication
GeometricRectangle
+area():double
Rectangle-double length-double width+getLength():dobule+getWidth():dobule
Class Rectangle contains the most primitive attributes and operations of rectangles. Classes GeometricRectangle and GraphicRectangle are independent of each other. A change from either side of CGA or GA, it would not cause the other side to be recompiled. NOTE: this does not violate LSP, since Rectangle does not have any client.
The open-closed principleThe open-closed principle
““Software entities (classes, modules, Software entities (classes, modules, functions, etc,) should be functions, etc,) should be open for open for extensionextension, but , but closed for closed for modificationmodification.” – R. Martin.” – R. Martin
To make a class open for extension, To make a class open for extension, closed for modification, program the closed for modification, program the class to interfaces (or abstract class to interfaces (or abstract classes), not implementation classes), not implementation (concrete classes).(concrete classes).
The open-closed principleThe open-closed principle
Employee+int EmpType
Faculty+getOffice()
void printEmpRoster(Employee[] emps) { for (int i; i<emps.size(); i++) { if (emps[i].empType == FACULTY) printfFaculty((Faculty)emps[i]); else if (emps[i].empType ==STAFF) printStaff((Staff)emps[i]); else if (emps[i].empType == SECRETARY) printSecretary((Secretary)emps[i]); }}
Staff+getDept()
Secretary+getTypeSpeed()
Engineer+getEngTYpe()
What if we need to add Engineer??
The open-closed principleThe open-closed principle
Employee+printInfo()
Faculty+printInfo()
void printEmpRoster(Employee[] emps) { for (int i; i<emps.size(); i++) { emps[i].printInfo(); }}
Staff+printInfo()
Secretary+printInfo
Engineer+printInfo()
When Engineer is added, printEmpRoster() does not even need to recompile. PrintEmpRoster() is open to extension, closed for modification.
The open-closed principleThe open-closed principle
Three versions of SORTThree versions of SORT– sort(List list)sort(List list)
Elements of list must implement Elements of list must implement ComparableComparable interfaceinterface
– sort(List list, StringComparator sc)sort(List list, StringComparator sc) Elements of list are not required to implement Elements of list are not required to implement
ComparableComparable StringComparator orders objects of String onlyStringComparator orders objects of String only
– Sort(List list, Comparator comp)Sort(List list, Comparator comp) Elements of list are not required to implement Elements of list are not required to implement
ComparableComparable ComparatorComparator may compare objects of any type. may compare objects of any type. Open to extension since it can sort objects of any Open to extension since it can sort objects of any
type at any order specified in the second type at any order specified in the second parameter.parameter.
The Liskov substitution principleThe Liskov substitution principle
““Subtypes must be substitutable for their Subtypes must be substitutable for their base types.” – R. Martinbase types.” – R. Martin
Demand no more, promise no lessDemand no more, promise no less– Demand no moreDemand no more: the subclass would accept any : the subclass would accept any
arguments that the superclass would accept.arguments that the superclass would accept.– Promise no lessPromise no less: Any assumption that is valid when : Any assumption that is valid when
the superclass is used must be valid when the the superclass is used must be valid when the subclass is used.subclass is used.
Interface inheritanceInterface inheritance – The LSP should be – The LSP should be conformed to.conformed to.
Implementation inheritanceImplementation inheritance – use – use composition instead of inheritance (in Java) composition instead of inheritance (in Java) or use private base classes (in C++).or use private base classes (in C++).
The Liskov substitution principleThe Liskov substitution principle
Implementation inheritanceImplementation inheritance – When you use List to When you use List to implementimplement Queue (in Java), use Queue (in Java), use
composition, not inheritance.composition, not inheritance.– The intention is that you use only List’s implementationThe intention is that you use only List’s implementation
List+insert()+delete()+find()
Queue+enqueue()+dequeue()+isEmpty()
<foundation>
List+insert()+delete()+find()
Queue+enqueue()+dequeue()+isEmpty()
List+insert()+delete()+find()
Queue+enqueue()+dequeue()+isEmpty()
MyList
The Liskov substitution principleThe Liskov substitution principle
Rectangle-int width;-int height+getWidth()+setWidth()+getHeight()+setHeight()+area();
class Square extends Rectangle { public void setWidth(int width) { super.setWidth(width); super.setHeight(width); } public void setHeight(int height) { super.setHeight(height); super.setWidth(height); }}void clientOfRectangle(Rectangle r) { r.setWidth(10); r.setHeight(20); print(r.area());}Rectangle r = new Square(…);clientOfRectangle(r); // what would be printed?
Square+getWidth()+setWidth()+getHeight()+setHeight()
IS-A
The Liskov substitution principleThe Liskov substitution principle
Rectangle and SquareRectangle and Square– Invariant of Rectangle: width and Invariant of Rectangle: width and
height are independent of each height are independent of each other (which can be expected from other (which can be expected from the setWidth and setHeight the setWidth and setHeight operations)operations)
– Square violates the width-height-Square violates the width-height-independence invariant of Rectangleindependence invariant of Rectangle
The Liskov substitution The Liskov substitution principleprinciple
Employee+printInfo()
Faculty+printInfo()
Staff+printInfo()
Secretary+printInfo
Engineer+printInfo()
There are cases in which There are cases in which the substitutability may not be the substitutability may not be neededneeded
Specialization: we found that Faculty, Staff, Secretary Specialization: we found that Faculty, Staff, Secretary and Engineer all have the same set of attributes and and Engineer all have the same set of attributes and operations, so we created the Employee as a operations, so we created the Employee as a placeholderplaceholder for those common properties. for those common properties.
For the system, there is no client for EmployeeFor the system, there is no client for Employee Thus, the four subclasses do not need to be Thus, the four subclasses do not need to be
substitutablesubstitutable Actually, there is no way we can tell whether they are or Actually, there is no way we can tell whether they are or
not. not.
Specialization
The dependency inversion The dependency inversion principleprinciple
““Abstraction should not depend Abstraction should not depend on details. Details should depend on details. Details should depend on abstraction.” – R. Martinon abstraction.” – R. Martin
High-level concepts are more High-level concepts are more stable than low-level stable than low-level implementationimplementation
The dependency inversion The dependency inversion principleprinciple
Policy Layer
Mechanism Layer
Utility LayerThe upper-level layer is
dependent upon lower-level layers.
High-level modules make calls to low-level modules.
The dependency inversion The dependency inversion principleprinciple
Policy
Policy Layer
<<interface>>
PolicyService
Mechanism
Mechanism Layer
<<interface>>
MechanismService
Utility
UtilityLayer
Dependency Inversion: Lower-level layers is dependent upon upper-level
layers.
Ownership Inversion: The client (upper-level layer) owns the interface, not the lower-level
layers
The interface segregation The interface segregation principleprinciple
““Clients should not be forced to Clients should not be forced to depend on methods that they do depend on methods that they do not use.” – R. Martin not use.” – R. Martin
When we bundle functions for When we bundle functions for different clients into one different clients into one interface/class, we create interface/class, we create unnecessary coupling among the unnecessary coupling among the clients.clients.– When one client causes the interface When one client causes the interface
to change, all other clients are forced to change, all other clients are forced to recompile.to recompile.
Software design principles - Software design principles - summarysummary
The single-responsibility principleThe single-responsibility principle– There is only one source that may the class to There is only one source that may the class to
changechange The open-closed principle The open-closed principle
– Open to extension, closed for modificationOpen to extension, closed for modification The Liskov substitution principleThe Liskov substitution principle
– A subclass must substitutable for its base classA subclass must substitutable for its base class The dependency inversion principleThe dependency inversion principle
– Low-level (implementation, utility) classes should be Low-level (implementation, utility) classes should be dependent on high-level (conceptual, policy) classesdependent on high-level (conceptual, policy) classes
The interface segregation principleThe interface segregation principle– A client should not be forced to depend on methods A client should not be forced to depend on methods
it does not use.it does not use.