Principles of OO Design Ing. Libor Buš PhD. Department of Software Engineering Faculty of Information Technology Czech Technical University in Prague MI-DPO WS 2010/11, Lecture 1 Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti 2010, Libor Buš
Agenda Review of Basic OO Terms UML Notation Principles of OO Design SRP OCP LSP ISP DIP 2
Review of Basic OO Terms Class = Attributes + Methods Object class instance Encapsulation hiding impl. details from class clients Minimize the Accessibility of Classes and Members! Composition new functionality is obtained by delegating functionality to one of the objects being composed by reference or by value 3
Review of Basic OO Terms Interface named collection of public services Inheritance new functionality is obtained by extending the implementation of an existing object Polymorfism clients treats subclass as superclass Favor Composition Over Inheritance! 4
UML Notation
UML Class Diagram 6
UML Sequence Diagram 7
Principles of OO Design
Symptoms of Poor Design Rigidity design is hard to change Fragility design is easy to break Immobility design is hard to reuse Viscosity hard to do the right thing Needless Complexity overdesign Needless Repetition mouse abuse Opacity disorganized expression 9
SRP: The Single-Responsibility Principle Class responsibility reason to change A class should have only one reason to change. Example: interface Modem { public void dial(string no); public void hangup(); public void send(char c); public char recv(); } connection management data communication 10
SRP: Example <<interface>> Data Channel + send(char) + recv() : char <<interface>> Connection + dial(pno:string) + hangup() Modem Implementation 11
OCP: The Open-Closed Principle try to design modules that never need to be changed extend behavior by adding new code, do not modify existing code Software entities should be open for extension, but closed for modification. abstraction is the key 12
OCP: The Open-Closed Principle Client is not open and closed: Client Server Client is both open and closed Client <<interface>> Client Interface Server 13
OCP: Example Bad Design enum ShapeType {circle, square}; struct Shape { ShapeType itstype; }; struct Circle { ShapeType itstype; double itsradius; Point itscenter; }; struct Square { ShapeType itstype; double itsside; Point itstopleft; }; 14
OCP: Example Bad Design typedef struct Shape *ShapePointer; void DrawAllShapes(ShapePointer list[], int n) { int i; for (i=0; i<n; i++) { struct Shape* s = list[i]; switch (s->itstype) { case square: DrawSquare((struct Square*)s); break; case circle: DrawCircle((struct Circle*)s); break; } } } 15
OCP: Example Bad Design Rigidity Addition of Triangle causes recompilation Fragility many other switch/case or if/else statements which are hard to maintain Immobility Reusing DrawAllShapes requires bringing along Square and Circle 16
OCP: Example OO solution class Shape { public: virtual void Draw() const = 0; }; class Square : public Shape { public: virtual void Draw() const; }; class Circle : public Shape { public: virtual void Draw() const; }; void DrawAllShapes( vector<shape*>& list) { } for( vector<shape*>::iterator i=list.begin(); i!=list.end(); i++) (*i)->draw(); 17
OCP: Usual Violations Global variables Public members Typecasts (RTTI) 18
LSP: The Liskov Substitution Principle Subtypes must be substitutable for their base types. DrawAllShapes should work with any subclass of the Shape superclass. Violation of LSP is latent violation of OCP. 19
LSP: Example class Rectangle { public: void SetWidth(double w) {itswidth=w;} void SetHeight(double h) {itsheight=w;} double GetHeight() const {return itsheight;} double GetWidth() const {return itswidth;} private: double itswidth; double itsheight; }; 20
LSP: Example Rectangle Square void f(rectangle& r) { r.setwidth(32); } void Square::SetWidth(double w) { Rectangle::SetWidth(w); Rectangle::SetHeight(w); } void Square::SetHeight(double h) { Rectangle::SetHeight(h); Rectangle::SetWidth(h); } Rectangle::SetWidth is not virtual -> calls Rectangle::SetWidth for Square 21
LSP: Example Let s fix that: class Rectangle { virtual void SetWidth(double w) {itswidth=w;} virtual void SetHeight(double h) {itsheight=w;} }; However LSP is still violated! void g(rectangle& r) { r.setwidth(5); r.setheight(4); assert(r.getwidth() * r.getheight()) == 20); } 22
LSP: Usual Violations Using RTTI to select function based on type void DrawShape(const Shape& s) { if (typeid(s) == typeid(square)) DrawSquare(static_cast<Square&>(s)); else if (typeid(s) == typeid(circle)) DrawCircle(static_cast<Circle&>(s)); } Degenerate functions in derivatives Throwing exceptions from derivatives 23
LSP: Relation with OCP All subclasses must conform to the behavior that clients expect of the base classes they use A subtype must have no more constraints than its base type. LSP is prime enabler of OCP: substitutability of subtypes allows a module to be extensible without modifications 24
DIP: Dependency-Inversion Principle a) High-level modules should not depend on low-level modules. Both should depend on abstractions. b) Abstractions should not depend on details. Details should depend on abstractions. 25
DIP: Naïve Layering Dependency is transitive Policy Layer Mechanism Layer Utility Layer 26
DIP: Inverted Layers Policy Policy Layer <<interface>> Policy Service Interface Mechanism Policy Layer <<interface>> Policy Service Interface Utility Utility Layer 27
DIP: Example Naïve model of Button and Lamp + poll() Button Lamp + turnon() + turnoff() public class Button { private Lamp itslamp; public void poll() { if( /* some condition */ ) itslamp.turnon(); } } 28
DIP: Example Dependency inversion applied to the Lamp + poll() Button <<interface>> ButtonServer + turnon() + turnoff() Lamp 29
ISP: Interface Segregation Principle Classes with fat interfaces are not cohesive Clients should not be forced to depend on methods that they do not use. Interface separation Through delegation Through multiple inheritace 30
ISP: Example Transaction + execute() Deposit Transaction Withdrawal Transaction Transfer Transaction <<interface>> UI + requestdepositamount() + requestwithdrawalamount() + requesttransferamount() + informinsufficientfunds() 31
ISP: Example Transaction + execute() Deposit Transaction Withdrawal Transaction Transfer Transaction <<interface>> Deposit UI + requestdepositamount() <<interface>> Withdrawal UI + requestwithdrawalamount() + informinsufficientfunds() <<interface>> Transfer UI + requesttransferamount() <<interface>> UI + requestdepositamount() + requestwithdrawalamount() + requesttransferamount() + informinsufficientfunds() 32
References Books: R.C.Martin Agile Software Development Online Papers: R.C.Martin The Principles of OOD 33