GRASP Principles That Every Developer should know

Anand Maalige
Huawei Developers
Published in
8 min readJun 23, 2023

--

GRASP Principles

Introduction

Dear friends, In this article we will learn about need for code design and principles that are essential for every developer to know to write the code that is understandable, maintainable, testable, flexible and easily upgradable.

What does code design mean? In this context, doing code design means deciding which class is in charge of which responsibility or functionality. Code design is crucial because, the code that is well designed is consistent, well organized. Imagine a code that is poorly designed, is generally a mess.

Luckily for our generation developers, lot of readily available, tried, tested and successfully deployed design principles are available. One fundamental principles among such are GRASP principles. In this article we will learn about the GRASP principles.

GRASP Stands for “General Responsibility Assignment Software Principles”

Grasp principles can help you understand the fundamentals behind the GOF design patterns or in fact any other object oriented analysis and designs or patterns.

GRASP helps us deciding which responsibility should be assigned to which class/object.

There are 9 GRASP principles, also called as patterns, which every developer should understand well.

  1. Information Expert
  2. Creator
  3. Controller
  4. Low Coupling
  5. High Cohesion
  6. Indirection
  7. Polymorphism
  8. Pure Fabrication
  9. Protected Variations

Let us understanding each principle one by one

1. Information Expert

As you all know every functionality or method involves with variables and fields, what these variables/fields contain? is it data? also image if you could perform any operation without data. Practically there exists nothing without data. How do handle this data with Variables and fields? And think about the class that needs to perform the responsibility that has no information with it? The answer lies with Information Expert principle.

“Assign responsibility to the class that has the information to fulfill the responsibility” or in simple terms “The method that work with data, should be in the same place where data exist”.

This principle ensures that all the information required to perform any operation/responsibility is within itself and assures the fulfillment of the responsibility.

eg: Assume we need to get Movies from a MovieStore. Since MovieStore knows about all the movies that are available, we can assign this responsibility of returning all movies to MovieStore class. So MovieStore is information expert.

Information Expert
public class Movie: Entity, IAggregateRoot  
{
private readonly List<Movie> -movies;

public GetAllMovies()
{
return this._movies(ListofAllMovies)
}
}

2. Creator

Object creation is an important process and is required to know who and where should we create an instance of the class. Creator is a GRASP pattern which helps to decide which class should be responsible for creating a new object of a class.

Creator pattern says “each object should be created only in the place where it will be used”, i.e. container object is required to creates contained object. Who can create the object should be based on the objects association and their interaction.

Consider MovieStore and Movie in that store. MovieStore had an aggregation association with Movie, it means MovieStore is the container and the movie is the contained object. So we can instantiate Movie object in MovieStore class.

Creator

Suppose there are two classes Class A and Class B, assign class B to create the responsibility to create object A if one of these is true.

a. B contains or compositely aggregates A

b. B records A

c. B closely uses A

d. B has the initializing data for A

3. Controller

In every program or application there should be one class that represents the overall System that handles main functionality or we can call it as root object. root Object re-presents a use case scenarios within which operation occurs. This principle generally depends on overall functionality of the system.

This can deal with how to delegate the request from the UI layer objects to domain layer objects. When a requests comes from UI object, controller pattern helps in determining what is that first object that handle the message from UI . This is called controller because this object controls/co-ordinates/delegates with other units of the system to fulfill the request.

Benefits of the controller pattern as follows

  1. Mainly controls the sequence of activities to achieve the functionality.
  2. Controller is required to maintain the state of the system.

If the controller class is overloaded with many responsibilities, then it is called as bloated controller, which is not good practice. To overcome this, one can include sub controllers or delegate the responsibilities to other classes. In the below example you can see controller class is performing multiple actions like play movie(), getMovie() upon the action request from user action. In practical application, there can be many such requests coming in to controller, so controller is expected to handle all the requests efficiently for smooth operation and user experience.

Controller

4. Low Coupling

This principle helps us to develop the software to reduce the impact of changes, how to handle the high dependency and increased reuse?

Coupling is a measure of how one element is related to another elements in a system. It is how strongly objects are connected to each other. The higher the coupling, greater the dependency of one element to other element. The two elements are coupled if

  1. One element has aggregation/composition association with other element.
  2. One elements implements/extends other element.

This principle states that “Assign the responsibilities so that coupling remains low”.

Minimize the dependency on other classes/objects, hence making the system maintainable, efficient and reusable. In the below example we can see rent video class is not directly returning the video to user, instead it is decoupled and is accessed through video store via video object.

Example of low coupling

5. High Cohesion

Cohesion is a measure of how strongly all the responsibilities of the elements are tied or related. It means to what degree the parts inside a class/elements belong together.

Classes with low cohesion tend to have unrelated data or unrelated behaviors. this can devaluate the stability of the system.

High cohesion helps us build the system that has focused objects, understandable, manageable, at the same time are low coupled and clearly defines the purpose of the element.

Low coupling and high cohesion should generally go hand in hand. System is expected to be low coupled but with High Cohesion at all times. Think of train, which has many bogies but are connected/linked one with other. All bogies connected together to form a train at the same time they can be delinked to connect the different routes, this achived high cohesion but low coupling at the same time.

Cohesion

6. Indirection

This principle talks about how to avoid the direct coupling between two or more elements. Here one can think of having a intermediate unit to communicate between the different elements so that elements are not directly coupled. Indirection should helps us achieve low coupling. Many times you would have come across adapters/mediators which are the example of indirection. Indirection is about assigning the responsibility to avoid direct coupling between two or more units as direct coupling can result in complexity and avoid re-use.

You can think of office, which has office head and supporting staff, whenever a something to be done by supporting staff, head communicates to supporting staff through secretory.

indirection states that “Assign the responsibility to an intermediate object to mediate between other components or services so that they are not directly coupled.”

Indirection achieves low coupling but may reduce the readability of the system. There is tradeoff to be taken into consideration based on the system context.

Indirection

7. Polymorphism

Polymorphism is fundamental principle of object-oriented design. This principle helps us how to handle related but varying elements based on the element type. Polymorphism guides us in deciding which object is responsible for handling those varying types. One can think of varying types as one name but many interfaces based on the context, one function name but perform different operations based on the type of inputs. Below example shows calling the getArea() to shape class, which decides which area method to be called based on the shape type thus achieving the result based on type of the shape

Polymorphism

8. Pure fabrication

In generic approach we may run into a situation where you need to achieve low coupling and high cohesion but adhering to other GRASP principles that becomes bottleneck to Designers and developers. Sometimes it becomes hard to find out where responsibility should be placed.

This principle states that “Assign a cohesive responsibilities to a convenience class that doesn't represent a domain concept”

it means Use a fabricated class or artificial class , which is a set of related responsibilities that doesn't represent any domain.

One should have come across domain driven design or domain service. eg : We often come across situation to convert the time or currency based on country/region. It is difficult to say where this function should be placed, the best option is create a new class or interface to handle this situation.

Pure fabrication provides a high cohesive set of elements. Benefits while achieving low coupling and reusability.

9. Protected Variations

In modern day software development, the much needed requirement is to support the “Ease of change”. As programmers and designers, one must be ready to support forever changing requirements. As mentioned earlier now a days we are provided with many design guidelines , principles, patterns and practices to support changes on different level of abstraction. Few of them are SOLID — open-close principle, GOF patterns, encapsulation, event drive architectures where user actions/events handles the further set of executions to achieve the seamless results.

Protected variation principles helps us to avoid the impact of variations of one elements on other elements. It will provide a set of well defined interface so that there will be no effect on other units when variation happens and this leads to more structured and flexible design.

Example of protected variations are polymorphism, Encapsulation, interfaces. Being a programmer on day to day basis, we have used classes, interfaces, abstractions. These are the ways which are inherently achieving the protected variations.

Reference

--

--