Software Design Principles August 19 2005 Software design

  • Slides: 23
Download presentation
Software Design Principles August 19, 2005

Software Design Principles August 19, 2005

Software design principles The single-responsibility principle n The open-closed principle n The Liskov substitution

Software design principles The single-responsibility principle n The open-closed principle n The Liskov substitution principle n The dependency inversion principle n The interface segregation principle n

The single-responsibility principle “A class should have only one reason to change. ” --

The single-responsibility principle “A class should have only one reason to change. ” -- Robert Martin n A responsibility = a reason to change n Separate coupled responsibilities into separate classes n

The single-responsibility principle n Example: – Often we need to sort students by their

The single-responsibility principle n Example: – Often we need to sort students by their name, or ssn. So one may make Class Student implement the Java Comparable interface. class Student implements Comparable { … int compare. To(Object o) { … }; – Student is a business entity, it does not know in what order it should be sorted since the order of sorting is imposed by the client of Student. – Worse: every time students need to be ordered differently, we have to recompile Student and all its client. – Cause of the problems: we bundled two separate responsibilities (i. e. , student as a business entity with ordering) into one class – a violation of SRP

The single-responsibility principle Register Add(Course d, Student s); Student Comparable SSN Name get. SSN()

The single-responsibility principle Register Add(Course d, Student s); Student Comparable SSN Name get. SSN() get. Name() major get. Major() int compare. To() 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. int compare. To() AClient op() { ; } It invokes Collections. sort(a. Listof. Students);

The single-responsibility principle Register Comparator Add(Course d, Student s); int compare(Object o 1, Object

The single-responsibility principle Register Comparator Add(Course d, Student s); int compare(Object o 1, Object o 2) Student SSN Name get. SSN() get. Name() major get. Major() Student. By. Name Client. A int compare(Object o 1, Object o 2) op() { ; } Student. By. SSN int compare(Object o 1, Object o 2) The solution is to separate the two responsibilities into two separate classes and use another version of Collections. sort(). Client. B op() { ; } It invokes Collections. sort(a. Listof. Students, Student. By. SSN);

The single-responsibility principle Computational Geometry Application Rectangle +draw(): void +area(): integer Graphical Application GUI

The single-responsibility principle Computational Geometry Application Rectangle +draw(): void +area(): integer Graphical Application GUI 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.

The single-responsibility principle Computational Geometry Application Graphical Application Geometric Rectangle Graphic Rectangle +area(): double

The single-responsibility principle Computational Geometry Application Graphical Application Geometric Rectangle Graphic Rectangle +area(): double +draw(): void GUI • 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 principle Computational Geometry Application Geometric Rectangle +area(): double Graphical Application Rectangle -double

The single-responsibility principle Computational Geometry Application Geometric Rectangle +area(): double Graphical Application Rectangle -double length -double width +get. Length(): dobule +get. Width(): dobule Graphic Rectangle +draw(): void GUI Class Rectangle contains the most primitive attributes and operations of rectangles. Classes Geometric. Rectangle and Graphic. Rectangle 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 principle “Software entities (classes, modules, functions, etc, ) should be open for

The open-closed principle “Software entities (classes, modules, functions, etc, ) should be open for extension, but closed for modification. ” – R. Martin n To make a class open for extension, closed for modification, program the class to interfaces (or abstract classes), not implementation (concrete classes). n

The open-closed principle Employee +int Emp. Type Faculty +get. Office() Staff +get. Dept() Secretary

The open-closed principle Employee +int Emp. Type Faculty +get. Office() Staff +get. Dept() Secretary +get. Type. Speed() void print. Emp. Roster(Employee[] emps) { for (int i; i<emps. size(); i++) { if (emps[i]. emp. Type == FACULTY) printf. Faculty((Faculty)emps[i]); else if (emps[i]. emp. Type ==STAFF) print. Staff((Staff)emps[i]); else if (emps[i]. emp. Type == SECRETARY) print. Secretary((Secretary)emps[i]); } } Engineer +get. Eng. TYpe() What if we need to add Engineer? ?

The open-closed principle Employee +print. Info() Faculty +print. Info() Staff +print. Info() Secretary +print.

The open-closed principle Employee +print. Info() Faculty +print. Info() Staff +print. Info() Secretary +print. Info Engineer +print. Info() void print. Emp. Roster(Employee[] emps) { for (int i; i<emps. size(); i++) { emps[i]. print. Info(); } } When Engineer is added, print. Emp. Roster() does not even need to recompile. Print. Emp. Roster() is open to extension, closed for modification.

The open-closed principle n Three versions of SORT – sort(List list) n Elements of

The open-closed principle n Three versions of SORT – sort(List list) n Elements of list must implement Comparable interface – sort(List list, String. Comparator sc) n n Elements of list are not required to implement Comparable String. Comparator orders objects of String only – Sort(List list, Comparator comp) n n n Elements of list are not required to implement Comparable Comparator may compare objects of any type. Open to extension since it can sort objects of any type at any order specified in the second parameter.

The Liskov substitution principle n n “Subtypes must be substitutable for their base types.

The Liskov substitution principle n n “Subtypes must be substitutable for their base types. ” – R. Martin Demand no more, promise no less – Demand no more: the subclass would accept any arguments that the superclass would accept. – Promise no less: Any assumption that is valid when the superclass is used must be valid when the subclass is used. n n Interface inheritance – The LSP should be conformed to. Implementation inheritance – use composition instead of inheritance (in Java) or use private base classes (in C++).

The Liskov substitution principle n Implementation inheritance – When you use List to implement

The Liskov substitution principle n Implementation inheritance – When you use List to implement Queue (in Java), use composition, not inheritance. – The intention is that you use only List’s implementation List +insert() +delete() +find() Queue +enqueue() +dequeue() +is. Empty() <foundation> List +insert() +delete() +find() Queue +enqueue() +dequeue() +is. Empty() List +insert() +delete() +find() My. List Queue +enqueue() +dequeue() +is. Empty()

The Liskov substitution principle class Square extends Rectangle { public void set. Width(int width)

The Liskov substitution principle class Square extends Rectangle { public void set. Width(int width) { super. set. Width(width); super. set. Height(width); } public void set. Height(int height) { super. set. Height(height); super. set. Width(height); } } void client. Of. Rectangle(Rectangle r) { r. set. Width(10); r. set. Height(20); print(r. area()); } Rectangle r = new Square(…); client. Of. Rectangle(r); // what would be printed? Rectangle -int width; -int height +get. Width() +set. Width() +get. Height() +set. Height() +area(); IS-A Square +get. Width() +set. Width() +get. Height() +set. Height()

The Liskov substitution principle n Rectangle and Square – Invariant of Rectangle: width and

The Liskov substitution principle n Rectangle and Square – Invariant of Rectangle: width and height are independent of each other (which can be expected from the set. Width and set. Height operations) – Square violates the width-heightindependence invariant of Rectangle

The Liskov substitution principle n n n There are cases in which the substitutability

The Liskov substitution principle n n n There are cases in which the substitutability may not be needed Generalization: we found that Faculty, Staff, Secretary and Engineer all have the same set of attributes and operations, so we created the Employee as a placeholder for those common properties. For the system, there is no client for Employee Thus, the four subclasses do not need to be substitutable Actually, there is no way we can tell whether they are or not. Employee +print. Info() Faculty +print. Info() Staff +print. Info() Secretary +print. Info Engineer +print. Info() generalization

The dependency inversion principle “Abstraction should not depend on details. Details should depend on

The dependency inversion principle “Abstraction should not depend on details. Details should depend on abstraction. ” – R. Martin n High-level concepts are more stable than low-level implementation n

The dependency inversion principle Policy Layer High-level modules make calls to low-level modules. Mechanism

The dependency inversion principle Policy Layer High-level modules make calls to low-level modules. Mechanism Layer Utility Layer The upper-level layer is dependent upon lower-level layers.

The dependency inversion principle Policy Layer <<interface>> Policy Service Dependency Inversion: Lower-level layers is

The dependency inversion principle Policy Layer <<interface>> Policy Service Dependency Inversion: Lower-level layers is dependent upon upper-level layers. Mechanism Layer Utility <<interface>> Mechanism Service Ownership Inversion: The client (upper-level layer) owns the interface, not the lower-level layers Utility Layer

The interface segregation principle “Clients should not be forced to depend on methods that

The interface segregation principle “Clients should not be forced to depend on methods that they do not use. ” – R. Martin n When we bundle functions for different clients into one interface/class, we create unnecessary coupling among the clients. n – When one client causes the interface to change, all other clients are forced to recompile.

Software design principles - summary n The single-responsibility principle – There is only one

Software design principles - summary n The single-responsibility principle – There is only one source that may the class to change n The open-closed principle – Open to extension, closed for modification n The Liskov substitution principle – A subclass must substitutable for its base class n The dependency inversion principle – Low-level (implementation, utility) classes should be dependent on high-level (conceptual, policy) classes n The interface segregation principle – A client should not be forced to depend on methods it does not use.