Last Time: Object Design Comp435 Object-Oriented Design Week 7 Computer Science PSU HBG The main idea RDD: Responsibility-Driven Design Identify responsibilities Assign them to classes and objects Responsibilities are an abstraction Concretely, implemented with classes, their methods and in terms of how they collaborate with other objects. 1 2 Last Time: Responsibilities Responsibilities for doing create an object perform calculations initiate operations on other objects control and coordinate activities Responsibility for knowing about private encapsulated data about related objects Last Time: The 9 GRASP Patterns The guiding principles to assign responsibilities Information Expert Creator Low Coupling Pure Fabrication Indirection Protected Variations 3 4 Last Time: Creator What object creates an object X? Doing responsibility Choose an object C, such that: C contains X C records X C closely uses X C has the initializing data for X The more, the better Last Time: Information Expert Most basic responsibility assignment principle Assign the responsibility to an object that has the information needed to fulfill it That which has the information does the work 5 6 1
Low Coupling Low Coupling Coupling How one class is connected to other classes How one class has knowledge of other classes How one class relies on other classes Assign responsibilities so that coupling remains low Consider classes Payment, Register, Sale We need to create a Payment instance and associate it with Sale Which class creates the payment object? Register Payment Sale 7 8 Low Coupling Solution 1 makepayment(x) 1: create(x) 2: addpayment(p) p:payment Solution 2 makepayment(x) 1: makepayment(x) 1.1: create(x) Which is better? :Payment 9 Low Coupling s of Coupling Class A has an attribute of class B An instance of A calls a method in class B or an instance of B A has a method that references instances of B, e.g., a local variable A is a direct or indirect subclass of B A implements an interface B 10 Summary Low Coupling Low coupling is a general principle to keep in mind during design Inherently generic classes should have especially low coupling Is extremely low coupling a good thing? Some coupling is necessary Avoid unnecessary coupling Cohesion How strongly related and focused the responsibilities of a class are A low cohesion class Does unrelated things Does too many things Why not have low cohesion in a class? 11 12 2
Who creates Payment objects? Option 1 makepayment(x) 1: create(x) p:payment 2: addpayment(p) Register Payment Sale Option 2 makepayment(x) 1: makepayment(x) 1.1: create(x) 13 Which is better? :Payment 14 Problem with Low Cohesion A class is solely responsible for many things in very different functional areas Example A class RDB-RPC-Interface responsible for Relational Databases (RDB) Remote Procedure Calls (RPC) Very different functional areas. May need to split in 2 classes. Problem with Low Cohesion A class has sole responsibility for a complex task in one functional area Example: A class RDBInterface Responsible for interacting with Relational Databases Methods are related, but too many of them May need to split into a family of lightweight classes 15 16 A class has moderate responsibilities In one functional area Collaborates with other classes to fulfill tasks A class RDBInterface, but only partially responsible for database interactions Summary A class with high cohesion Relatively small number of operations with highly related functionality Not too much work collaborates with other classes to share work 17 18 3
Benefits Ease of understanding Maintenance and enhancement are simplified Often results in low coupling Cohesive classes are easier to reuse Handling of System Events Who is responsible for handling System events? First object responsible for a system operation 19 20 In the SSD, a conceptual class System handles system events :Cashier makenewsale() :System This does not mean that a System software class is created during design Assigned to one or more controller classes Common candidates: Façade A class that represents the overall system, device or subsystem Ex: Register Use Case : A class that represents the use case or session Often named as ProcessSaleHandler ProcessSaleCoordinator ProcessSaleSession 21 22 System events in the POS system makenewsale(), enteritem(), endsale(), makepayment() In Layered model UI layer generates the event Window :??? enteritem(itemid, qty) UI Layer Domain Layer 23 Option 1 Represent the entire system or device Ex: Register Option 2 A handler for all events in a particular use case Ex: ProcessSaleHandler UI Layer Domain Layer Window enteritem( ) Window enteritem( ) :ProcessSaleHandler 24 4
Observations One controller for all events in one use case Keep info about the state of the use case Discover out-of-order events Ex: makepayment occurs before endsale Common Problem s are given too much responsibility Should delegate to other objects Should mostly coordinate/control Façade Abstraction of the overall physical unit Suitable when There are not too many events UI cannot redirect events to other objects Possible problem Bloated controllers 25 26 Use-case A different controller for each use case An artificial construct to suppoert the system Not a domain concept Ex: UseCase: ProcessSale ProcessSaleHandler Use when there are too many system events Separates their handling into manageable separate classes Provides basis for knowing the state of the current use case scenario UI Objects / UI Layer should not be responsible for handling system events Window 1: enteritem() 1: makelineitem() 1.1: makelineitem() 27 28 Client/Server Applications Client side UI + s UI sends request to controller forwards the request to a remote server Server side Often there are use case controllers How to handle alternatives based on type Conditional variation: hard to maintain Use polymorphic operators How to create pluggable components Replace a component without affecting the system Based on polymorphism, the abstraction can take many forms Many possible subclasses Many possible implementations of an interface Modern OO languages support all these mechanisms 29 30 5
Class Hierarchies How do we ensure correctness of class hierarchies? Superclass-subclass relationships Domain model Relationships between concepts An instance of X is also an instance of Y Design model Relationships between software entities Implemented through inheritance Inheritance in C++ class B : public A { Multiple inheritance supported Every member of A is inherited by B Instance field f defined in A Every object of class B has a field f; If a method m is defined in A, B inherits it B may declare new members B may override inherited members 31 32 Liskov Substitution Principle (LSP) Key principle: A subclass must be substitutable for its superclasses Example class B : public A {... A function or method foo(a* a) foo behaves correctly when the parameter is an instance of A foo should behave correctly when the parameter is an instance of B Without foo knowing about the existence of B 33 Liskov Substitution Principle (LSP) Protection against variability of subclasses If foo was written correctly with respect to A, it should be possible to substitute a B object for the A object without affecting correctness LSP is stronger than LSP If we add a new subclass of A, foo will still be correct If we add a new subclass of A, we don t need to recompile foo (language mechanism) 34 Classic Example class Rectangle { protected: double h,w; Point topleft; public: double setheight (double x) { h=x; double setwidth (double x) { w=x; double getheight () { return h; double getwidth () { return w; double area() { return h*w; Suppose we have written a lot of code that uses Rectangle 35 Adding a Square At some point we decide that we need a square A square is a kind of Rectangle class Square : public Rectangle { With polymorphism, we don t have to recompile existing code E.g., void m(rectangle x) { does not have to be recompiled But there are problems 36 6
Problems Square doesn t really need both w and h Wasted memory (but relatively minor thing) Square inherits setheight and setwidth Incorrect behavior One possible solution Guards in client code Ex: rectangle r; r.setheight(5); if (r instanceof Square) r.setwidth(5); Makes the client code very fragile; bad idea!!! Another solution Override setheight and setwidth in Square class Square : public Rectangle { public void setheight(double x) { h=x; w=x; public void setwidth(double x) { h=x; w=x;... 37 38 More Problems void foo (Rectangle r) { r.setheight(5); r.setwidth(4); assert (r.area() == 20); Valid code, with only Rectangle objects But now it may break. The programmer of foo is justified in writing this code There is something wrong with square 39 LSP If foo was written correctly wrt to a superclass, it should be possible to substitute a subclass instance for the superclass instance and still be correct Square violates LSP Postcondition for Rectangle.setHeight: h == x and w == w old Postcondition for Square.setHeight: h == x and w == x The subclass postcondition does not imply the superclass postcondition 40 Ensuring LSP One way to ensure that LSP is not violated Considering preconditions and postconditions Whenever a subclass overrides a method of a superclass The precondition for the superclass should imply the precondition for the subclass The postcondition for the subclass should imply the postcondition for the superclass LSP is about behavior If foo ever gets a Square object and calls setheight on it, the behavior does not conform to the behavior of setheight in Rectangle Due to postconditions Inheritance should be more than just is-akind-of relationship: it should guarantee conformance of behavior Often used term: behavioral subtyping 41 42 7
The role of LSP Should be used as a warning flag It may be OK to violate it But the violation should be examined carefully Depends on the client of the hierarchy If we have a program in which height and width are never changed, it may be OK to have a Square as a subclass of Rectangle 43 Another Example class Polygon { public: void move (int x,int y) { void addvertex (Point p) { int numvert() {return vertices.size(); private: Set vertices; class Triangle :: public Polygon { public void addvertex(point p) { Needs to override addvertex to do nothing Postcondition for addvertex In Polygon: vertices = 1 + vertices old In Triangle: vertices = vertices old = 3 44 Possible Solution LSP is violated for Triangle Also for subclasses Rectangle, Hexagon, etc. One possible solution Create two subclasses of Polygon Subclass FixedSidedPolygon» with its own subclasses Triangle, Rectangle, etc. Subclass VariableSidedPolygon» in which addvertex is defined» addvertex is not defined in Polygon Indirection Another popular mechanism for protection from variations Where to assign a responsibility How to avoid direct coupling between 2 objects How to decouple object for low coupling Simple idea: use an intermediary Add a level of indirection Assign responsibility to an intermediate object 45 46 Indirection Pure Fabrication : Tax calculator adapters :TaxMasterAdapter t = gettotal taxes = gettaxes (s) xxx...... «actor» :TaxMasterSystem Assigning responsibility only to domain objects may results in poor cohesion high coupling low reuse potential Solution: use an artificially made-up class To assign highly cohesive set of responsibilities 47 48 8
Pure Fabrication Want to save Sale instance in database Information Expert Assign the responsibility to Sale Problem Sale becomes incohesive Sale needs to be coupled with the database Sale becomes hard to reuse... Solution Use an artificial class solely responsible for saving Protected Variations Protecting one part of the program from changes in another part Protected variations Open-closed principle Common mechanisms Encapsulation Abstraction Indirection 49 50 Protected Variations More ambitious mechanisms At run time, clients use a lookup service to find a server (protects against location and implementation changes) External rules are read and executed by an interpreter (flexibility wrt rule changes) 51 9