meeting -*- Outline -*- May want to photocopy for the class the StickSync POS domain model from Fig 12.9 (p. 176), use cases (pp. 50-53), SSDs (Fig 9.5), and contracts (pp. 185-186). * GRASP: Designing Objects with Responsibilities (Larman, Ch 16) See also Riel's book (chapter 3 especially). Now we come to the key part of object design, designing classes and methods to fulfill responsibilities. GRASP is a learning aid designed by Larman stands for General Responsibility Assignment Software Patterns. ** responsibilities and methods (16.1) ------------------------------------------ RESPONSIBILITIES (16.1) Def: A responsibility is "a contract or obligation". Two basic types of responsibilities an object may have: A. doing - something by itself: creating objects, calculating - telling others to do something initiating action coordinating activities B. knowing - some private data - about related objects - about things it can calculate ------------------------------------------ e.g., a Sale is responsible for creating SalesLineItems (doing) a Sale is responsible for knowing its total (knowing) Q: Is a responsibility the same thing as a method? no, methods are implemented to fulfill responsibilities, responsibilities are implemented using methods, either alone or several of them ** Patterns (16.3) ------------------------------------------ DESIGN PATTERNS (16.3) Def: a design pattern is a named problem/solution pair that can be applied in new contexts, with advice on how to apply it, and discussion of its trade-offs. ------------------------------------------ Having names facilitates communication among designers; you can argue about patterns using the names instead of explaining the concepts each time. ** GRASP Patterns (16.4) fundamental patterns (concepts) in design (what every OO designer knows) - information expert - creator - high cohesion - low coupling - controller *** Information Expert (16.6) ------------------------------------------ INFORMATION EXPERT (16.6) Problem: How should one assign responsibilities to objects? Solution: Assign a responsibility to the information expert -- the class that has the information necessary to fulfill the responsibility. Example: In the POS system, Who should be responsible for knowing the total of a sale? By Information Expert we should look for a class of objects that has the information needed. ------------------------------------------ advice: start assigning responsibilities by clearly stating the responsibility. Q: Where do we look for classes that have information? Look first in the design model. Then look in the domain model if necessary, adding classes, associations, and attributes to the design model as needed. Do this for this case, assuming we have no design model, but have the domain model of Fig. 12.9 Q: What information is needed to determine the grand total? So Sale is the information expert for this responsibility. so we have collaboration diagram design class diagram t := getTotal() |----------| |-------------| ---------------------| :Sale | | Sale | --> |----------| |-------------| | date | | time | |-------------| | getTotal() | |-------------| Q: what information is needed to determine the line item subtotal? So SalsesLineItem is the expert Q: What information is needed to determine the product price? So ProductSpecification is the expert (continued) collaboration diagram design class diagram |----------| |-------| 1*: st := getSubtotal() |----------| | |---------------| | :Sale |-------------------------|:Sales | | | SalesLineItem | |-------| --> * | LineItem |-| |---------------| |----------| | quantity | | |---------------| | | getSubtotal() | | |---------------| 1*.1: p := | getPrice()| | | v | |----------------------| |-----------------| | ProductSpecification | | :Product | |----------------------| | Specification | | description | |-----------------| | price | | itemID | |----------------------| | getPrice() | |----------------------| Q: What responsibilities did we assign? Now continuing the Information Expert pattern... ------------------------------------------ INFORMATION EXPERT (CONTINUED) Discussion: Information Expert is a frequently used guiding principle. Many partial experts that collaborate. All objects are "alive" or "smart" Should avoid having a single "god class" with many "dumb" objects doing its will. Contraindications: avoid if causes problems with coupling and cohesion. For example, who should the responsible for saving a Sale in a database? Instead design faor separation of major concerns (information hiding). Benefits: - Information hiding, hence low coupling. - Behavior spread out, encouraging more cohesive lightweight classes. Related: - Low Coupling - High Cohesion A.K.A: "Place responsibilities with data", "Do it Myself", ... ------------------------------------------ Draw picture of God class (a la Riel) ... Giving this responsibiltiy (for saving a Sale to the DB) to Sale makes Sale too big (incohesive) Q: what examples are there in your designs? *** Creator (16.7) ------------------------------------------ CREATOR (16.7) Problem: who should be responsible for creating new instances of some class? Solution: Assign class B responsibility for creating an instance of class A if: - B aggregates objects of class A - B contains A objects - B records instances of A objects - B closely uses A objects - B has the initializing data needed to create A objects If more than one applies, prefer a class that aggregates or contains A objects. Example: In the POS system, who should be responsible for creating a SalesLineItem instance? ------------------------------------------ ... By Creator, we look over class that aggregates, contains, etc. SalesLineItem instances. Q: What class is like that in the domain model? draw the object interaction diagram showing a Sale instance creating the SalesLineItem instance ------------------------------------------ Discussion: The basic idea is to find a creator that needs to be connected to the created object anyway. This supports low coupling. Contraindications: if the creation is complex, then use the Factory pattern. Benefits: - low coupling Related Patterns: - Low Coupling - Factory - Whole-Part ------------------------------------------ Note: aggregation involves things are in a strong whole-part or assembly-part relationship Q: what examples are there in your designs? *** Low Coupling (16.8) This is perhaps more of a principle rather than a design pattern. ------------------------------------------ LOW COUPLING (16.8) Problem: How to support low dependency, low change impact, and increased reuse? Solution: Assign a responsibility so that classes aren't strongly connected. "Coupling" is a measure how strongly one element is connected to, has knowledge of, or relies on other elements. High coupling causes: - changes to affect other classes - difficulty in understanding classes (in isolation) - difficulty in reuse, because more classes are needed Example: Who should create a Payment instance and associate it with a Sale? ------------------------------------------ ... draw the classes Payment, Register, and Sale Register "records" a Payment, so the Creator pattern suggests Register as a candidate for creating the Payment. Draw the interaction diagram (Fig. 16.9) showing p:Payment being passed to the instance of Sale. An alternative is to give responsibility for creating the payment to the Sale instance. Draw the interaction diagram (Fig. 16.10) showing that. Q: Which design has lower coupling? So, this patterns are just preferring the second solution. Advice: can consider low coupling in isolation from other principles such as expert and high cohesion. ------------------------------------------ Discussion: Low Coupling is an evaluative principle that applies when considering all design decisions. Common forms of coupling X to Y include - X has an attribute of Y - X calls methods of Y objects - X has a method that uses Y e.g., as a parameter, local variable, return type - X is a subclass of Y - X implements the interface Y Low coupling reduces impact of changes. A subclass is strongly coupled to its superclass(es). There will usually be some coupling, just need to gauge it, and understand it. But don't couple strongly with unstable elements. Reusable classes should have low coupling. Contraindications: high coupling to stable elements (the programming language, libraries) is okay. Don't spend extra time "future proofing". Focus on realistic points of high instability or evolution. Benefits: - not affected by other's changes - simple to understand in isolation - convenient to reuse Related Patterns: - Protected Variation ------------------------------------------ Protected Variation is all about hiding variation points using indirection, polymorphism, information hiding, etc. See section 22.4 on pp. 334ff.a Q: what examples are there in your designs? *** High Cohesion (16.9) This is another evaluative principle. ------------------------------------------ HIGH COHESION (16.9) Problem: How to keep complexity manageable? Solution: Assign a responsibility so that cohesion remains high. Functional cohesion is a measure of how strongly related and focused the responsibilities of an element are. A class with low cohesion does many unrelated things, or too much work. This causes classes to be - hard to comprehend - hard to reuse - hard to maintain - delicate; often affected by change Example: Who should create a Payment instance and associate it with a Sale? ------------------------------------------ ... Recall that Creator suggests Register to create a Payment, and send it to the Sale instance. This is okay, but if Register takes on too many responsibilities, it becomes incohesive. Advice: this principle has to be considered in conjunction with other principles such as expert and low coupling. ------------------------------------------ Discussion: This also applies to methods, subsystems, etc., not just classes. It is an evaluative principle, and should be considered all the time. A rule of thumb is that a class with high cohesion has relatively few methods, with highly related functionality. ------------------------------------------ If the description of the element can only be summarized in a statement with "if" or "and" then it's less cohesive. e.g., this class is responsible for knowing the total and controlling inventory (bad). vs. this class is responsible for knowing the total. Make each class/method a "specialist". Examples: - very low cohesion -- a class solely responsible for many things in very different functional areas e.g., Relational Database and RPC interfaces - low cohesion -- a class solely responsible for a complex task in one functional area e.g., Relational Database interface lots of methods, a lot of code - moderate cohesion -- a class has lightweight and sole responsibilities in a few different areas that are logically related to the class concept, but not to each other e.g., A SQLServer class interacts with the DB for all of SQL related tasks, involving both parsing and interpretation. - high cohesion -- a class has moderate responsibilities in one functional area and collaborates with other classes to fulfill its tasks. e.g., SQLInterpreter which handles interpretation of SQL code for DBs, by collaborating with other classes. Cohesion and coupling are related, a class with low cohesion is necessarily coupled to lots of others Goal: modular design, where - changes to one element don't affect others (low coupling), and - changes are likely to be contained in one element (high cohesion) ------------------------------------------ Contraindications: - simplify maintenance for non OO folk - performance in distributed settings Benefits: - clarity and ease of comprehension - maintenance and enhancement easier - low coupling is often supported - easier to reuse ------------------------------------------ reuse becuase it has less "junk" and is more expert Q: what examples are there in your designs? *** Controller (16.10) ------------------------------------------ CONTROLLER (16.10) Problem: Who should be responsible for handling an input system event? An input system event is generated by an external actor. The UI turns these into system operations in the application layer. Solution: Assign the responsibility for receiving or handling a system event to a class that: - represents the overall system, device, or subsystem (facade controller), or - represents a use case scenario (use-case or session controller). ------------------------------------------ Note: classes like "Window", "Widget", "Applet", etc. from the UI layer are not part of this list. These classes should *not* fulfill the tasks associated with system events, but merely pass them along to the application logic layer ------------------------------------------ A controller is a non-UI object responsible for handling a system event. Example: In the POS system, who should handle system events such as enterItem? ------------------------------------------ ... By the Controller pattern, some choices are Register instance -- represents the device ProcessSaleHandler -- handles everthing is the ProcessSale use-case ------------------------------------------ Discussion: A Controller is a facade that hides details of the domain layer (from the UI layer). Use a use-case controller if - state info must be kept between system events, - to identify out-of-sequence events, - if there are not "too many" kinds of system events A controller should delegate instead of doing the work itself. ------------------------------------------ UI objects should not actually handle the system events. Q: Why is it bad for the user interface to have responsibility for handling system events? - makes it hard to change the UI - incohesive - high coupling to unstable element (the UI) - harder to reuse Controller is a client-side object, so does't work well when the UI is a web client in a browser and there is server-side information involved. - in this case, create server-side use case controllers Advice: watch for bloated controllers (incohesive) Don't have one controller for everything (a god class) Don't have the controller doing everything (dumb objects, non-OO) Instead: - break up into several controllers - design the controller so it delegates more ------------------------------------------ Benefits: - separates application logic from UI leading to higher reuse - allows reasoning about state of interaction in a use-case Related Patterns: - Command, for message handling systems each message is a command object - Facade, a controller is a Facade - Layers - Pure Fabrication, controllers aren't part of domain model ------------------------------------------ Q: what examples are there in your designs? ** Object Design and CRC cards (16.11) Not part of the UML. CRC = Class-Responsibility-Collaborator Index cards, one for each class, write name of class, responsibilities, and collaborators ------------------------------------------ CRC CARDS (16.11) An index card for each design class: |------------------------------------| | Sale Collaborators | |------------------------------------| | Knows total SalesLineItem| | Aggregates SalesLineItems | | ... | |------------------------------------| Can use them to play out scenarios - pick up cards when active ------------------------------------------