Design Patterns: GRASP Patterns of designing an application or module

 

This write-up will explain different GRASP Patterns i.e., General Responsibility Assignment Software Patterns (or Principles), explained in book: Applying UML and Patterns, which are more important in day to day programming life than the whole lot of GoF design patterns. GRASP is a set of 9 fundamental principles in OOPs design and responsibility assignment. These patterns solve some software problem common to many software development projects.

Information Expert (Information Hiding)

  • Problem Addressed: How will you assign a responsibility to a module/class?
  • Solution:
    • Assign responsibility to the class that has the information or data needed to fulfill it.
    • If you have an operation to do, and this operations needs some input data, then you should consider assigning the responsibility of carrying out this operation in the class that contains the input data for it.
  • This pattern is used to determine where to delegate responsibilities such as methods, computed fields, and so on.
  • It helps keeping the data localized, i.e. Data Encapsulation.
  • It reduces relationships between the classes. (Classes are self-sufficient in terms of data to carryout their tasks.)
  • Related Pattern or Principle:
    • Low Coupling
    • High Cohesion

Creator

  • The creation of objects is one of the most common activities in an object-oriented system.
  • Which class is responsible for creating the objects is a fundamental property of the relationship between objects of those particular classes.
  • Problem Addressed: Which class should creates object of another class (A)?
  • Solution:
    • In general, Assign the responsibility to create object of class(A) to class (B) if one, or preferably more, of the following apply:
      • Instances of (B) contain or compositely aggregate instances of (A)
      • Instances of (B) records or keeps track of instances of (A)
      • Instances of (B) closely use instances of (A)
      • Instances of (B) have the initializing information for instances of (A) and pass it on creation.
  • If you put together two part of the code that are semantically close ( the construction of (A), and the code that works a lot with (A)), then they become easier to reason about than if they were far apart.
  • Related Pattern or Principle:
    • Low Coupling
    • Factory pattern

Low Coupling

  • Coupling is a measure of how strongly one element is connected to, or has knowledge of, or relies on other elements.
  • Coupling introduces complexity, if only because the code can then no longer be understood in isolation.
  • The design principle of low coupling encourages to keep coupling/interactions/dependence between classes low.
  • Low coupling is an evaluative pattern that dictates how to assign responsibilities for the following benefits:
    • lower dependency between the classes
    • change in one class having a lower impact on other classes
    • higher reuse potential

Protected variations

  • The protected variations pattern protects elements from the variations in other elements (objects, systems, subsystems) by wrapping the cause of instability within an interface and using polymorphism to create various implementations of this interface.
  • Problem Addressed: 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.
    • The principle of Protected variations helps reducing the impacts of the changes of the code of one part (A) on another part (B). The code of part (B) is protected against the variations of the code of part (A), by organizing the responsibilities around stable interfaces.

Indirection

  • The Indirection pattern is another way to reduce coupling by creating an intermediary class (or any kind of component) between two classes (A) and (B). This way, the changes in each one of (A) and (B) don’t affect the other one. The intermediary class absorbs the impact by adapting its code rather than (A) or (B) (or more other classes).
  • The indirection pattern supports low coupling and reuses potential between two elements by assigning the responsibility of mediation between them to an intermediate object.
  • An example of this is the introduction of a controller component for mediation between data (model) and its representation (view) in the model-view-controller pattern. This ensures that coupling between them remains low.
  • Problem Addressed:
    • Where to assign responsibility, to avoid direct coupling between two (or more) things?
    • How to de-couple objects so that low coupling is supported and reuse potential remains higher?
  • Solution:
    • Assign the responsibility to an intermediate object to mediate between other components or services so that they are not directly coupled.
    • The intermediary creates an indirection between the other components.
  • This relates a lot to the Adapter design pattern, even though the Adapter design pattern is rather made to connect two existing incompatible interfaces. But it also has the effect of protecting each one against the changes of the other.
  • Difference between Protected variations and Indirection:
    • Protected variations is about designing interfaces in the existing components.
    • whereas Indirection is about introducing a new component in the middle.

Polymorphism

  • According to the polymorphism principle, responsibility for defining the variation of behaviors based on type of object is assigned to the type for which this variation happens. This is achieved using polymorphic operations.
  • The user of the type should use polymorphic operations instead of explicit branching based on type.
  • Problem:
    • How to handle alternatives based on type?
    • How to create pluggable software components?
  • Solution:
    • When related alternatives or behaviors vary by type (class), assign responsibility for the behavior — using polymorphic operations — to the types for which the behavior varies. (Polymorphism has several related meanings. In this context, it means “giving the same name to services in different objects”.)
  • The usage for polymorsphism is when there are several ways to accomplish a task, and you want to decouple the clients of this task from the various pieces of code that implement the various ways to perform it.
  • The Polymorphism principle is very close to the GoF Strategy pattern, if not identical.
  • Polymorphism contributes to the Low Coupling principle by:
    • Reducing the behavioural branching implementation in one class
    • Instead creating different classes according to behaviour
    • And thus reducing impact on other classes of a change in one class.

High Cohesion

  • High cohesion is an evaluative pattern that attempts to keep objects appropriately focused, manageable and understandable.
  • High cohesion is generally used in support of low coupling.
  • High cohesion means that the responsibilities of a given element / object / class are strongly related and highly focused.
  • This is the principle of do one thing and do it well.
  • The principle of high cohesion also applies to other elements of the code, such as:
    • functions
    • modules
    • systems
  • Breaking programs into classes and subsystems is an example of activities that increase the cohesive properties of a system.
  • Alternatively, low cohesion is a situation in which a given element/object/class has too many unrelated responsibilities. Elements with low cohesion often suffer from being hard to comprehend, reuse, maintain and change.

Pure fabrication

  • Usually, our code’s objects/classes map to the real concepts/objects in the project’s domain.
    • e.g., in IoT project, we tend to have CloudManager, SystemUpdater classes in the code.
  • Problem Addressed:
    • Sometimes you have a responsibility to assign, and it seems to not fit well in any domain class.
    • And according to the principle of High cohesion above, you shouldn’t force a responsibility into a class that is already doing something else.
    • That’s when the principle of Pure fabrication comes into play.
  • Solution:
    • Create an independent class that does not map to a domain object, and let it achieve this new responsibility in a cohesive way.
  • Related concept: Services
  • Related Patterns and Principles:
    • Low Coupling
    • High Cohesion

Controller

  • The controller pattern assigns the responsibility of dealing with events may be system events to a non-UI class that represents the overall system or a use case scenario.
  • A controller object is a non-user interface object responsible for receiving or handling a system event.
  • Problem: Who should be responsible for handling an input system event?
  • Solution:
    • A use case controller should be used to deal with all system events of a use case.
      • For example, A Log Monitor or System Health Monitor class/object.
    • And use case controller may be used for more than one use case.
      • For instance, for the use cases Create User and Delete User, one can have a single class called UserController, instead of two separate use case controllers.
  • The controller is defined as the first object beyond the UI layer that receives and coordinates (“controls”) a system operation.
  • The controller should delegate the work that needs to be done to other objects; it coordinates or controls the activity. It should not do much work itself.
    • For example, the Brokers as in Message Queuing applications like MQTT Brokers.
  • The GRASP Controller can be thought of as being a part of the application or service layer (assuming that the application has made an explicit distinction between the application or service layer and the domain layer) in an object-oriented system with common layers in an information system logical architecture.
  • Related Pattern or Principle:
    • Command
    • Facade
    • Layers
    • Pure Fabrication

References