Design Principles for Assigning Responsibilities

Chapter 2 • Object-Oriented Software Engineering 85 By implementing this rule, the object possesses the knowledge of conditions under which a method can or cannot be invoked. Hence, the question is which object is responsible to know this rule? The needs-to-know responsibility has implications for the future upgrades of modifications. Changes in the business rules require changes in the code of the corresponding objects. More on anticipating change in Chapter 5 below. Apparently, we have built an undue complexity into the Controller while striving to preserve high degree of specialization for all other objects. This implies low cohesion in the design; poor cohesion is equivalent to low degree of specialization of some objects. The reader should by no means get impression that the design in Figure 2-11 is the only one possible. Some variations are shown in Figure 2-12. For example, in variation a the Checker sets the key validity as a flag in the Key object, rather than reporting it as the method call return value. The Key is now passed around and LockCtrl, LightCtrl, and others can check for themselves the validity flag and decide what to do. The result: business logic is moved from the Controller into the objects that operate the devices. Personally, I feel uneasy with this solution where the correct functioning of the system depends on a flag in the Key object. A more elegant solution is presented in Section 5.1 below. Although the variation in Figure 2-12b is exaggerated, I have seen similar designs. It not only assigns an awkward method name, checkIfDaylightAndIfNotThenSetLit , but «destroy» prompt: try again opt k := create sk := getNext logTransactionk, val setOpentrue : Controller : Checker : KeyStorage : LockCtrl : Logger : PhotoObsrv dl := isDaylight alt [else] enterKey k : Key val := checkKeyk loop : LightCtrl : AlarmCtrl setLittrue soundAlarm val == true dl == false compare [for all stored keys] numOfTrials++ opt numOfTrials == maxNumOfTrials «destroy» prompt: try again opt k := create sk := getNext logTransactionk, val setOpentrue : Controller : Checker : KeyStorage : LockCtrl : Logger : PhotoObsrv dl := isDaylight alt [else] enterKey k : Key val := checkKeyk loop : LightCtrl : AlarmCtrl setLittrue soundAlarm val == true dl == false compare [for all stored keys] numOfTrials++ opt numOfTrials == maxNumOfTrials Figure 2-11: Sequence diagram for the system function “enter key.” Several UML interaction frames are shown, such as “ loop,” “alt” alternative fragments, of which only the one with a condition true will execute, and “ opt” optional, the fragment executes if the condition is true. Ivan Marsic • Rutgers University 86 worse, it imparts the knowledge encoded in the name onto the caller. Anyone examining this diagram can infer that the caller rigidly controls the callee’s work. The caller is tightly coupled to the callee since it knows the business logic of the callee. A better solution is in Figure 2-12c. Note that the system GUI design is missing, but that is OK since the GUI can be designed independently of the system’s business logic.

2.4.2 Class Diagram

Class diagram is created by simply reading it off of the interaction diagrams. The class diagram of our system is shown in Figure 2-13. Notice the differences and similarities with the domain model, Figure 2-7. Since class diagram gathers class operations and attributes in one place, it is easier to size up the relative complexity of classes in the system. The number of operations in a class correlates with the amount of responsibility handled by the class. Good OO designs distribute expertise and workload among many cooperating objects. If you notice that some classes have considerably greater number of operations than others, you should examine the possibility that there may be undiscovered classes or misplaced responsibilities. Look carefully at operation names and ask yourself questions such as: Is this something I would expect this class to do? Or, Is there a less obvious class that has not been defined? prompt: try again opt k := create sk := getNext logTransactionk, val setOpentrue : Controller : Checker : KeyStorage : LockCtrl : Logger : PhotoObsrv dl := isDaylight alt [else] enterKey k : Key val := checkKeyk loop : LightCtrl : AlarmCtrl setLittrue soundAlarm val == true dl == false compare [for all stored keys] numOfTrials++ opt numOfTrials == maxNumOfTrials : LightCtrl : PhotoSObs dl := isDaylight controlLight opt dl == false setLittrue : LightCtrl : PhotoSObs dl := isDaylight controlLight opt dl == false setLittrue c a checkIfDaylightAndIfNotThenSetLit : LightCtrl : PhotoSObs dl := isDaylight opt dl == false setLittrue The caller could be Controller or Checker checkIfDaylightAndIfNotThenSetLit : LightCtrl : PhotoSObs dl := isDaylight opt dl == false setLittrue The caller could be Controller or Checker b k := create sk := getNext : Controller : Checker : KeyStorage : LockCtrl k : Key checkKeyk loop setValidok controlLockk ok := isValid opt ok == true setOpentrue k := create sk := getNext : Controller : Checker : KeyStorage : LockCtrl k : Key checkKeyk loop setValidok controlLockk ok := isValid opt ok == true setOpentrue Figure 2-12: Variations on the design for the use case “Unlock,” shown in Figure 2-11. Chapter 2 • Object-Oriented Software Engineering 87 Class Relationships Class diagram both describes classes and shows the relationships among them. I already discussed object relationships in Section 1.4.1 above. Generally, the following types of relationships are possible: • Is-a relationship hollow triangle symbol ∆: A class is a “kind of” another class • Has-a relationship: A class “contains” another class - Composition relationship filled diamond symbol ♦ : The contained item is a integral part of the containing item, such as a leg in a desk - Aggregation relationship hollow diamond symbol ◊: The contained item is an element of a collection but it can also exist on its own, such as a desk in an office • Uses-a relationship arrow symbol ↓: A class “uses” another class • Creates relationship: A class “creates” another class In our particular case, Figure 2-13, there is an aggregation relationship between KeyStorage and Key; all other relationships happen to be of the “uses” type. The reader should also notice the visibility markers for class attributes and operations: + for public, global visibility; for protected visibility within the class and its descendant classes; and, − for private within-the-class- only visibility. Class diagram is static, unlike interaction diagrams, which are dynamic. Generic Object Roles As a result of having specific responsibilities, the members of object community usually develop some stereotype roles. KeyChecker + checkKeyk : Key : boolean KeyChecker + checkKeyk : Key : boolean Key – code_ : string – timestamp_ : long – doorLocation_ : string LightCtrl lit_ : boolean + isLit : boolean + setLitv : boolean KeyStorage + getNext : Key PhotoSObsrv + isDaylight : boolean Logger + logTransactionk : Key Controller numOfTrials_ : long maxNumOfTrials_ : long + enterKeyk : Key 1 1 1 1 1 sensor lightCtrl 1 checker alarmCtrl lockCtrl logger 1 1.. validKeys 1 AlarmCtrl + soundAlarm LockCtrl open_ : boolean + isOpen : boolean + setOpenv : boolean Figure 2-13: Class diagram for the home access system under development. Ivan Marsic • Rutgers University 88 • Structurer • Bridge Note that objects almost never play an exclusive role; several roles are usually imparted to different degree in each object. Object Communication Patterns Communication pattern is a message-sending relation imposed on a set of objects. As with any relation, it can be one-to-one or one-to-many and it can be deterministic or random. Some of these are illustrated in Figure 2-14. Object-oriented design, particularly design patterns, is further elaborated in Chapter 5 below.

2.4.3 Why Software Engineering Is Difficult 2

Another key cause is the lack of analytical methods for software design. Software engineers are aiming at optimal designs, but quantitative criteria for optimal software design are largely unknown. Optimality criteria appear to be mainly based upon judgment and experience.

2.5 Software Architecture

Bottom-up design approaches at the local level of objects, reviewed in the previous section, are insufficient to achieve optimal designs, particularly for large systems. A complementary approach are system-level macro-level, global design approaches which help us to “see the forest for the trees.” These approaches partition the system into logical units or follow some global organizational patterns. A A B B P P S 2 S 2 S 1 S 1 S N S N A A B B a b c Figure 2-14: Example object communication patterns. a One-to-one direct messages. b One-to-many untargeted messages. c Via a shared data element.