Grasp

Designing Objects with Responsibilities

«General Responsibility Assignment Software Principles»

Gerson Sunyé

Assign Responsibilities to Classes

After identifying your requirements and creating a domain model, add methods to the appropriate classes and define the messaging between the objects to fulfill the requirements.”

Responsibility-Driven Design (RDD)

A way of thinking about Design in terms of:
  • Responsibilities

  • Roles

  • Collaborations

What is a Responsibility?

A responsibility an obligation to perform a task or know information
— Rebecca Wirfs-Brock

Responsibilities

Doing responsibility of an object is seen as:
  • doing something itself: create an object, process data, do some computation/calculation

  • initiate and coordinate actions with other objects



Knowing responsibility of an object can be defined as:
  • private and public object data

  • related objects references

  • things it can derive

Responsibility – an obligation required of an object

Responsibility can be accomplished by:
  • a single object or

  • a group of object collaboratively accomplish a responsibility.

GRASP Principles

9 Principles

  1. Information Expert

  2. Creator

  3. Controller

  4. Low Coupling

  5. High Cohesion

  6. Indirection

  7. Polymorphism

  8. Pure Fabrication

  9. Protected Variations

Information Expert

Problem

What is a general principle for assigning responsibilities to objects?

Solution

Assign a responsibility to the information expert, that is, the class that has the information necessary to fulfill the responsibility.

  • They have all the information needed to perform operations, or in some cases they collaborate with others to fulfill their responsibilities.

Information Expert: Example

Who should be responsible for knowing the grand total of a sale?
  • Sale contains SalesLineItems, so it has the information necessary for total


Who should be responsible for knowing the line item subtotal?
  • SalesLineItem knows the quantity, so it has the information necessary for subtotal

  • ProductDescription knows the price

cd pos

Information Expert: Responsibilities

Design ClassResponsibility

Sale

knows sale total

SalesLineItem

knows line item subtotal

ProductDescription

knows product price

Information Expert: Solution

sd get total

Information Expert: Considerations

  • Basic Object-Oriented principle

  • Objects know things related to the information they have

    • The information necessary may be spread across several objects

  • Just because an object has information necessary does not mean it will have responsibility for action related to the information

Creator

Problem

Who should be responsible for creating a new instance of a class?

Solution

Assign class B the responsibility to create an instance of class A if one or more of the following is true:

  • B aggreates A objects

  • B contains A objects

  • B records instances of A objects

  • B closely uses A objects

  • B has the initializing data that will be passed to A when it is created (thus B is an Expert wrt creating A)

If more than option applies, prefer class B which aggregates or contains class B.

Creator: Example

Who creates Sales Line Items?
  • Since a Sale contains SalesLineItem objects, it should be responsible for creating Items.

cd pos

Creator: Solution

sd create sale items
cd sale items

The association is a composite aggregation.

Creator: Considerations

When not to use:
  • When creating requires complexity, such as using existing instances of a class, conditionally creating an instance from one class or another based upon some external property, etc.

  • In these cases, delegate creation to a helper class called a Concrete Factory or Abstract Factory

  • Related Design Patterns: Concrete Factory, Abstract Factory

Controller

Problem

Who should be responsible for handling a system event? (Or, what object receives and coordinates a system operation?)

Solution

Assign the responsibility for receiving and/or handling a system event to one of following choices:

  • Object that represents overall system, device or subsystem (façade controller)

  • Object that represents a use case scenario within which the system event occurs (a «UseCase»x Handler)

Controller: Roles

Input system event

vent generated by an external actor associated with a system operation

Controller

a non-UI object responsible for receiving or handling a system event

Controller: Example

  • During analysis, we can assign system operations to a class System

  • That does not mean there will be a System class at time of design

  • During design, a controller class is given responsibility for system operations


sd controller

Controller is a Façade

  • Controller is a façade into domain layer from interface layer

  • Often use same controller class for all system events of one use case so that one can maintain state information, e.g. events must occur in a certain order

  • Normally controller coordinates activity but delegates work to other objects rather than doing work itself

Façade and Use Case Controllers

Façade controller

represents the overall system — use when there are not many system events

Use case controllers

use different controller for each use case


use case controllers
Figure 1. Use Case Controllers

Controller: Considerations

Bloated controller:
  • Single class receiving all system events and there are many of them

  • Controller performs many tasks rather than delegating them

  • Controller has many attributes and maintains significant information about system which should have been distributed among other objects

  • Related Design Patterns: Command, Façade, Layers

Low Coupling

Problem

How to support low dependency, low change impact, and increased reuse?

Solution

Assign responsibilities so that (unnecessary) coupling remains low. Use this principle to evaluate alternatives.

Evaluating the Effect of Coupling

  • Coupling is measure of how strongly one element is connected to, has knowledge of, or depends on other elements

  • The greater the coupling, the greater the dependence between objects

  • Coupling is avoided because it goes against OO principles

Why is Low Coupling Good?

  • It reduces time, effort and defects involved in modifying software

  • A class with high coupling relies on many other classes and leads to different problems:

    • Changes in related classes force local changes

    • Harder to understand in isolation

    • Harder to reuse

Low Coupling: Example

Consider Payment, Register, Sale. Who should be responsible for creating a Payment and associating it with a Sale?
  • Since a Register records a Payment it should have this responsibility (cf Creator)

  • Register creates Payment p then sends p to a Sale (coupling of Register class to Payment class)

sd low coupling v1

Low Coupling: Alternate solution

  • Register requests Sale to create a Payment

sd low coupling v2

Choosing the Low Coupling Solution

Consider coupling in these two solutions:
  • In both cases a Sale needs to know about a Payment

  • However, a Register needs to know about a Payment in the first but not in the second solution

  • The second alternative leads to less coupling:

    • Avoids an association between Register and Payment, since Sale and Payment are already related

Low Coupling: Considerations

  • High coupling to stable or pervasive elements is not a problem (e.g. Java libraries)

  • High coupling is a problem only in areas where change is likely (e.g. Domain Classes)


Benefits:
Understandability

classes are easier to understand in isolation

Maintainability

changes in other components do not affect the class

Reusability

it is easy to reuse a single class than several intricate classes

High Cohesion

Problem

How to keep complexity manageable?

Solution

Assign the responsibility so that cohesion remains high. Use this principle to evaluate alternatives.

Evaluating the Effects of High Cohesion

  • Cohesion is a measure of how strongly related and focused the responsibilities of an element (class, subsystem, etc.) are

Why is High Cohesion Good?

Classes with High Cohesion are:
  • Easy to understand/comprehend

  • Easy to reuse

  • Easy to maintain

  • Less brittle

High Cohesion: Example

Consider Payment, Register, Sale again:


sd make payment v1
Register is taking on responsibility for system operation makePayment()
  • In isolation, there is no problem

  • But if we start assigning additional system operations to Register then will violate high cohesion

High Cohesion: Alternative Solution

  • Register delegates Payment creation to the Sale:

sd make payment v2
  • This second approach leads to higher cohesion for the Register class.

  • Note: this design supports both, low coupling and high cohesion

  • High cohesion, like low coupling, is an evaluative principle

Cohesion Levels

Very low cohesion

a class is responsible for many things in different functional areas

Low cohesion

a class has sole responsibility for a complex task in one functional area

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

High cohesion

a class has moderate responsibilities in one functional area and collaborates with other classes to fulfill tasks

High Cohesion: Considerations

  • Typically high cohesion leads to few methods with highly related functionality

Benefits:
Complements Low Coupling

these two principles are closely related, often low cohesion leads to high coupling and vice-versa

Understandability

classes/components are simpler, having fewer operations.

Maintainability

logical changes in the domain affect fewer modules, and changes in one module require fewer changes in other modules.

Reusability

application developers will find the component they need more easily among the cohesive set of operations provided by the module.

Indirection

Problem

where to assign a responsibility to avoid direct coupling between two or more things?

Solution

assign the responsibility to an intermediate object to mediate between other components or services so that they are not directly coupled.


  • ⇒ The intermediate object creates an indirection between the other components

Indirection: Example

sd get taxes
  • The Adapter acts as a level of indirection to external systems

Indirection: Considerations

  • Main benefit: lower coupling between components

  • Related Design Patterns: Adapter, Bridge, Façade, Observer, Mediator.

Polymorphism

Problem

How to handle alternatives based on element type? How to create pluggable software components?

Solution

When alternatives or behaviors vary by type (class), assign responsibility for the behavior—​using polymorphic operations—​to the types for which the behavior varies.

Corollary: Do not test for the type of an object and use conditional logic to perform varying alternatives based on type

Polymorphism: Example

cd tax calculator adapter
  • How to handle multiple external third-party tax calculators?

Polymorphism: Considerations

Benefit:
Extensibility

a design based on assigning responsibilities by polymorphism is easily extend (by the addition of new classes).


  • Related Design Patterns: Several Design Patterns rely on polymorphism: Adapter, Command, Composite, Proxy, State, and Strategy

Pure Fabrication

Problem

What object should have the responsibility, when you do not want to violate High Cohesion and Low Coupling but solutions offered by other principles are not appropriate?

Solution

Assign a highly cohesive set of responsibilities to an artificial or convenience class that does not represent a problem domain concept.

Pure Fabrication: Example

Who should be responsible for persisting the class Sale in a relational database?
  • According to Information Expert, it should be Sale.

  • However:

    • this task requires database-oriented operations (reduces cohesion)

    • the class becomes highly coupled to the database interface (increases coupling)

    • Persisting objects is a common task that should be more generic

cd persistent storage

Pure Fabrication: Considerations

  • Pure Fabrication usually takes on responsibilities from the domain class that would be assigned those responsibilities based on Information Expert

  • Several Design Patterns are Pure Fabrications: Adapter, Command, Strategy, etc.

Protected Variations

Problem

How to design objects, subsystems and systems so that the variations or instability in these elements does not have an undesirable impact on other elements?

Solution

Identify points of predicted variation or instability, assign responsibilities to create a stable interface around them.

Mechanisms Motivated by Protected Variations: Example

  • Core Protected Variations Mechanisms: data encapsulation, interfaces, polymorphisms, standards, etc.

  • Service Lookup: JNDI, UDDI, etc.

  • Interpreter-Driven Designs

  • Reflective or Meta-Level Designs

  • Uniform Access: Ada, Eiffel, C# support properties

  • Standard Languages: SQL

  • The Liskov Substitution Principle

  • Structure-Hiding Designs: Law of Demeter

Protected Variations: Considerations

  • Caution: Speculative PV and Picking Your Battles

    • Two points of change are worth defining: variation point and evolution point

  • See also: Information hiding and the Open-Closed Principle