Take responsibility into your control with GRASP principles

5 min read
236

General Responsibility Assignment Software Patterns (or Principles), abbreviated GRASP, consist of guidelines for assigning responsibility to classes and objects in object-oriented design.

Wikipedia

I want to start with the fact that, strangely, GRASP is in the shadow of SOLID patterns, although it seems to me that it has more specific examples and is easier to understand. The importance of these patterns can’t be overstated in modern development.

Generally, nine rules will suggest the right way to choose the correct responsibility for your classes. Patterns very helpful for the development, but in my opinion, they have a terrible structure. I’ve separated these rules into two groups: roles and principles.

Principles

Let’s start with principles. Roles will realize these principles, and generally, they as four pillars on which role responsibilities stand.

Low coupling

Problem: How to reduce the impact of change? How to support low dependency and increased reuse?

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

Craig Larman

Coupling is the method and degree of interdependence between software modules, the strength of the relationships between modules, a measure of how interdependent different routines or modules are.

High coupling is considered a serious drawback, as it makes it difficult to understand the logic of modules, their modification, offline testing, and reuse individually. By contrast, Low coupling is the hallmark of a well-structured and well-designed system, and when combined with high cohesion, it meets the overall readability and maintainability scores.

In simple terms, the fewer connections between components, the better.

High cohesion

Problem: How to keep objects focused, understandable, manageable, and as a side effect support Low Coupling?

Solution: Assign a responsibility so that cohesion remains high. Use this to evaluate alternatives.

Craig Larman

Cohesion is a measure of the strength of the interconnectedness of elements within the module; the manner and extent to which the tasks performed by some software modules are related.

A low cohesion class performs many heterogeneous functions or unrelated responsibilities. It is undesirable to create such classes because they lead to the following problems:

An object (subsystem) is considered high cohesion if its responsibilities are well coordinated with each other, and it does not perform huge amounts of work.

Actually, methods inside the object must do similar operations. If your methods do different operations, then you need to decomposite it into a few separate objects.

Polymorphism

Problem: How to handle alternatives based on type?

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.

Craig Larman

It would be best if you have created your system to swap the current implementation on another easily. For example, you can use the dependency injection design pattern and then extend these classes or better change the type to interface and implement the realization.

Protected variations

Problem: How to design objects, subsystems, and systems so that the variations or instability in these elements don’t have an undesirable impact on other elements?

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

Craig Larman

This is the most important principle that is indirectly related to the rest of the GRASP principles. Currently, one of the crucial software metrics is the ease of change. As architects and developers, we must be ready for ever-changing requirements. This is not optional and “nice to have” quality attribute – it is a “must-have” and our duty.

Roles

The more straightforward part of these patterns is roles. The best way it avoids classes with a few positions. I know what creating a new class and moving some methods into it is mentally so complicated, but it needs to do.

Information expert

Problem: What is a basic principle by which to assign responsibilities to objects?

Solution: Assign responsibility to the class that has the information needed to fulfill it.

Craig Larman

An expert can be absolutely any class that has the necessary information. For example, we’ve got an EBook entity if we need information about the amount of all EBooks to need to create the next class EBooks.

<?php
class EBooks {
	public function get_total(): int {}
}

Creator

ProblemWho creates object A?

Solution: Assign class B the responsibility to create object A if one of these is true (more is better)
– B contains or compositely aggregates A
– B records A
– B closely uses A
– B has the initializing data for A

Craig Larman

Sounds straightforward, and I think understandable for each development. For example, you can use factories or any form of generative design patterns.

Let’s add the simple EBookFactory factory that will create all entities:

<?php
class EBookFactory {
	public function create_ebook( $author, $category, $pages ): EBook {
		return new EBook( $author, $category, $pages );
	}
}

Controller

Problem: What the first object beyond the UI layer receives and coordinates “controls” a system operation?

Solution: Assign the responsibility to an object representing one of these choices:
– Represents the overall “system”, “root object”, device that the software is running within, or a major subsystem (these are all variations of a facade controller)
– Represents a use case scenario within which the system operation occurs (a use case or session controller)

Craig Larman

The first object that deals with the user interface and delegates which object will be next in the process.

Who are using any frameworks could have known it as Controller from MVC pattern or Presenter in MVP.

The main idea is to make a separate entity and view template.

<?php
class EBooksController {
	public function index( EBook $ebook ) {
		require 'ebook.php';
	}
}

Pure fabrication

Problem: What object should have the responsibility, when you don’t 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 doesn’t represent a problem domain concept.

Craig Larman

Fabricated class/artificial class – assign a set of related responsibilities that don’t represent any domain object. In simple words, it’s something that doesn’t exist in the business logic.

For example, we want to get an entity from a database, and for this, we could create a EBookRepository class:

<?php

class EBookRepository {
	private $db;
	public function __construct( PDO $db ) {
		$this->db;
	}
	public function get_by_id( $id ): array {
		$ebook = $this->db->query(
			sprintf(
				'SELECT * FROM ebooks WHERE ID = %d',
				(int) $id
			)
		);

		return $ebook->fetch( PDO::FETCH_NUM );
	}
}

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 to not directly coupled.

Craig Larman

Indirection introduces an intermediate unit to communicate between the other units so that the other units are not directly coupled. Generally, all roles using this principle.

More objects could help you create a more flexible and scalable solution. As you can notice, Controller, Creator, Information Expert were used to decrease the coupling between modes, view, external API.

Let’s add a mediator between EBookRepository and database:

<?php

class EBookQuery {
	private $db;
	public function __construct( PDO $db ) {
		$this->db;
	}
	public function get_by_id( $id ): array {
		$ebook = $this->db->query(
			sprintf(
				'SELECT * FROM ebooks WHERE ID = %d',
				(int) $id
			)
		);

		return $ebook->fetch( PDO::FETCH_NUM );
	}
}

class EBookRepository {
	private $query;
	public function __construct( EBookQuery $query ) {
		$this->query = $query;
	}
	public function get_by_id( $id ): EBook {
		$ebook = $this->query->get_by_id( $id );

		return new EBook( $ebook->author, $ebook->category, $ebook->pages );
	}
}

Summary

A flexible and scalable solution is an art of software architecture, and GRASP patterns could help with your classes’ responsibilities. Generally, the GRASP principles look more practical and easier to implement than SOLID. Also, a lot of them are using popular frameworks as in PHP language as in others.

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe to news. I promise not to spam :)
Follow me, don't be shy