Get up to 80 % extra points for free! More info:

GRASP Design Patterns

Welcome to a comprehensive article that will explain design patterns from the GRASP group for you. These were composed by Craig Larman, a popular author and programmer interested in software design and the development process. GRASP is an acronym of General Responsibility Assignment Software Patterns. The question of assigning responsibility is a persistent problem in OOP applications and one of the most important pillars of a high-quality architecture. We've already talked about the importance of assigning responsibility in the Software Architecture and Depencency Injection course. Unlike, for example, the "GOF Design Patterns", GRASP doesn't provide specific implementation patterns, but rather best practices. For this reason, we'll be able to describe all the GRASP patterns in a single lesson today with no problems.

Controller

You, as programmers with an interest in software design, should know the term "controller" very well, at least in the context of the "MVC architecture". It works a bit like a remote TV controller or an Xbox controller. It's a component whose role is to communicate with the user. We can find variations of the controller pattern basically in all well-written applications. For example, C# .NET form apps use the Code Behind layer, which is a controller. When the communication with the user is mediated through a separate control class, the application gets divided into layers and its logic is fully encapsulated from the presentation. Such applications are readable and easy to maintain.

Let's show a simple example. Consider we're programming a calculator. A deterrent example of a monolithic application would look as follows (using the C# language):

public int Add()
{
    Console.WriteLine("Enter the 1st number");
    int a = int.Parse(Console.ReadLine());
    Console.WriteLine("Enter the 2nd number");
    int b = int.Parse(Console.ReadLine());
    return a + b;
}

In the method above, all the communication with the user (writing and reading from the console) is mixed with the application logic (the calculation itself). In practice, of course, the method would calculate something more complicated so it'd be worth it to write it. Imagine that there's a much more complicated operation instead of the a + b. Sometimes, we say that such a method has side effects, so it's not universal and calling it also triggers some console communication which might not be noticeable at first glance. This problem is pretty obvious here and perhaps it won't come to your mind to write the application this way.

However, the same problem can be less noticeable when we write application logic directly to the event handler methods of form control elements. You've already programmed a form application for sure. You may have seen code like this:

public void AddButton_Click(Object sender)
{
    int a = int.Parse(number1.Text);
    int b = int Parse(number2.Text);
    resultLabel.Text = (a + b).ToString();
}

We contaminate the controller, the control class, with application logic (the calculation). In all applications, there should always be one layer that only serves to communicate with the user, whether human or an API. This layer shouldn't be missing (the first wrong code) or shouldn't do anything else (the second wrong code).

The correct version of the console calculator code would be, for example, as follows:

public static function main()
{
    Calculator calculator = new Calculator();
    Console.WriteLine("Enter the 1st number");
    int a = int.Parse(Console.ReadLine());
    Console.WriteLine("Enter the 2nd number");
    int b = int.Parse(Console.ReadLine());
    Console.WriteLine (calculator.Add(a, b));
}

The main() method is, in this case, a part of a controller that only communicates with the user. All the application logic is encapsulated in the logic layer classes, here in the Calculator class. This class doesn't contain any interactions with the console.

A correct version of the second example would look the same:

class CalculatorController
{

    private Calculator calculator = new Calculator();

    public void AddButton_Click(sender: Object)
    {
        int a = int.Parse(number1.Text);
        int b = int.Parse(number2.Text);
        resultLabel.Text = (calculator.Add(a, b)).ToString();
    }

}

And the UML diagram:

The Controller GRASP pattern - Design Patterns

We can see that controller's responsibility includes parsing as well because it's an input processing. The same is with changing the resultLabel label value, which is the output. However, the calculation itself is in the Calculator class again, which doesn't even know about the form.

In order to provide universal examples, let's also show how rendering a page without a controller looks like in PHP:

<?php
$database = new PDO('mysql:host=localhost;dbname = testdb;charset=utf8mb4', 'name', 'password');
$cars = $database->query("SELECT * FROM cars")->fetchAll();
?>
<table>
<?php foreach ($cars as $car) : ?>
    <tr>
        <td><?= htmlspecialchars($car['license_plate'])?></td>
        <td><?= htmlspecialchars($car['color'])?></td>
    </tr>
<?php endforeach ?>
</table>

And with the controller:

class CarController
{

    private $carManager;

    public function __construct()
    {
        $this->carManager = new CarManager();
    }

    public function all()
    {
        $cars = $this->carManager->getCars(); // A template variable
        require('Templates/cars.phtml'); // Loads the template
    }

}

The "car.phtml" template would look like this:

<table border = "1">
    <?php foreach ($cars as $car) : ?>
        <tr>
            <td><?= htmlspecialchars($car['license_plate']) ?></td>
            <td><?= htmlspecialchars($car['color']) ?></td>
        </tr>
    <?php endforeach ?>
</table>

We have separated the logic and the presentation into 2 files and reduced the coupling.

Creator

The Creator pattern answers the question into which class we should place code creating an instance of another class. Craig says that the B class instantiates the A class if:

1. A is a part of it

An example could be Invoice and InvoiceItem classes. It makes sense to create instances of invoice items in the Invoice class as they're clearly part of it. The Invoice class has the responsibility for its items here.

The Creator GRASP pattern - Design Patterns

2. A is its dependency

The B class creates A if it depends on it. An example could be e.g. a signature database whose instance would be created by the Invoice class so that it can display a signature on invoices it generates. If this dependency is also used somewhere else, it's better not to create new instances of the dependency all over again, but to use the Dependency Injection pattern.

The Creator GRASP pattern and dependencies - Design Patterns

3. Has enough information to instantiate it

Typically, there are multiple places where we could create a class instance. However, we should create it in a place where all information are already available, meaning the variables or instances needed for the creation. It doesn't make much sense to retrieve additional data into some class when everything is already available elsewhere.

As an example, let's decide whether to instantiate the InvoiceList class with customer invoices in the InvoiceManager or CustomerManager class. Let's find out which of the classes has all the information that InvoiceList needs. If we need, for example, to get all the invoices and select those of a particular customer, we'll instantiate InvoiceList in InvoiceManager since the manager has all the invoices.

The Creator GRASP pattern and information - Design Patterns

4. B contains A

If A is a nested class in the B class, its instance should also be created by the B class. However, nested classes have not become very popular.

The Creator GRASP pattern and a nested class - Design Patterns

High cohesion

High cohesion means that our application consists of reasonably large pieces of code, each one focusing on one thing. This is also one of the basic principles of the OOP itself. High cohesion is closely related to low coupling (see below), because when we concentrate related code to one place, the need to reference other parts of the application is reduced. Another related pattern is the Law of Demeter, which basically says that objects shouldn't "talk" to foreigner objects.

An example of High Cohesion is, for example, concentrating the functionality related to users into the UserManager class. If we placed e.g. the code logging the user in into the InvoiceManager class, where it would be required in order to display the user's invoices, or we placed the code canceling the user's account into the Cleaner class, removing inactive accounts, we'd violate High Cohesion. The code that should be together in the UserManager class would be fragmented in different places of the application, where it was just needed. Therefore, we concentrate the related code to one place, even if these methods are used only once within the application.

Indirection

Indirection is a very interesting principle which we've already encountered with the Controller. It says that when we create a "man in the middle" class somewhere in our application, i.e. an "extra" class, it can paradoxically simplify the application. We can clearly see that controllers reduce the number of references between objects, and at the cost of just a few lines of code, they also support better code re-usability and readability. Indirection is one of the ways to achieve Low coupling. We've already introduced an example with the Controller pattern.

Information expert

An informational expert is another principle that helps us decide to which class a method, an attribute, or other class member belongs. The class with the most information always has the responsibility. We call this class the information expert, and we add more functionality and data to it. We've already talked about a similar principle with the Creator pattern.

Low coupling

Low coupling is basically the same thing as High cohesion but from another point of view. In our application, we should create the smallest number of references between objects as possible. We can achieve it by distributing the responsibility wisely.

As a deterrent example, let's mention a Manager class, which would include the logic for working with customers, invoices, logistics, simply for everything. Such objects are sometimes called "god objects", they have too much responsibility and therefore create too many coupling (such a Manager would typically use a large number of classes to work like this). The total number of references in the application is not that important, it's the number of references between objects what matters. We always try to make the class communicates with as few other classes as possible, so we should add other managers such as UsersManager, InvoiceManager, LogisticsManager and others. You probably already figured out that such a manager would not be very reusable in other application as well.

The Low Coupling GRASP design pattern - Design Patterns

A deterrent example of a god object not respecting Low coupling

And we don't just have to stick with classes. Low coupling is also related to, for example, other practices such as method naming ("We should name methods with as fewest words as possible and without the AND conjunction"). The do() or parseAndProcessRecord() methods suggest that they do too much.

Note: Since we've already mentioned god objects, let's mention the opposite problem, which is called the Yoyo problem. With too little program structures, the granularity too high, often even due to overuse of inheritance, there are so many small classes in the program. The programmer then must keep switching from class to class, to find out how it works and then go back. This action may resemble throwing yoyo down and up, over and over again. For this reason, object composition is often preferred over inheritance.

Talking about references between objects, we should also avoid cyclical references, which are generally considered to be a bad practice. This happens when the A class refers to the B class which refers back to the A class. If this happens, there's something wrong with the design. Cyclical references can be also created through multiple classes.

Polymorphism

Yes, even polymorphism is a design pattern. Although the principle of polymorphism should be well known to you, let's repeat it for the sake of completeness. Most often, it's the case when a descendant modifies the behavior of its ancestor but keeps its interface. From the programming point of view, we're talking about overriding parent methods. We can then work with the objects using a unified interface, but each object modifies the inherited functionality by itself. Polymorphism isn't limited only to inheritance, it's generally about working with objects of different types using a common interface they all implement. Let's show the popular example of animals each having a talk() method, but overriding it from the Animal ancestor to make their specific sounds:

Animal sounds as a polymorphism example in OOP - Design Patterns

If you want a more realistic example of polymorphism, there can be a common ancestor for all form controls. Every descendant would override the ancestor's methods such as draw(), getSize() and similar depending on their specific functionality.

The Polymorphism GRASP pattern - Design Patterns

Protected variations

The practice talks about creating a stable interface at the key places of the application, where the modification of the interface would cause the need to overwrite a large part of the application. Let's discuss a real example again. In ICT.social's information system, we use the Protected variations principle, specifically, the "Adapter design pattern" and that's how we prevent changes which Facebook makes in their API. Allowing registrations via Facebook and similar integration result in an increase in the number and activity of our users, but unfortunately at the cost of rewriting the application every few months. Using the FacebookManagerInterface interface, our system doesn't have to change anymore. When the new version comes out and Facebook redoes everything, we'll just implement this interface by a new manager (such as FacebookManagerXX, where XX is the Facebook API version), and we'll pass the new instance implementing this interface to the system. Of course, the interface can also be defined by polymorphism and an abstract classes.

The Protected variations GRASP pattern - Design Patterns

Pure fabrication

We have already talked about Pure fabrication today. It represents classes that only serve to simplify the design of the system. Just as Controller was actually a case of Indirection, Indirection is a case of Pure fabrication. Service classes out of the application functionality reduce the number of dependencies and increase coherence.

That would be all of GRASP and I look forward to seeing you with other online courses on ICT.social.


 

All articles in this section
Design Patterns
Article has been written for you by David Capka Hartinger
Avatar
User rating:
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities