GRASP Principles - Part 2: Controller, Low Coupling and High Cohesion

Written by serhiirubets | Published 2022/06/17
Tech Story Tags: grasp | controller | low-coupling | high-cohesion | single-responsibility | design-patterns | oop | oop-design-patterns

TLDROOP principle says, that in our system, we should create as fewer connections between our classes, modules, files, etc… as possible. The fewer connections, the more stable the system we will have, it will be much more easily replace components, make refactoring, delete components and add (change) tests. This approach could solve some problems, not all, but anyway it could reduce some problems that multithreaded can create. For example, imagine, if we have a really big system and a large number of objects, that should be connected somehow.via the TL;DR App

Controller

Hello guys. In the previous article, I wrote about what is GRASP, why we should use it, and the first 2 principles: Information expert and Creator. So, if you haven’t read the previous article, pls, read it now here.

The controller is responsible for handling multiple requests from the client. The controller knows how to handle actions, that may come from the client, what to do with them and how to answer the client.

Another case problem is multithreaded. It’s a really hard topic and could take a lot of time for discussing what is it and the problems that could be (race conditions for an example), so, if you are interested in, pls, find a time and learn this topic. And the more multithreaded places we have in our system, the more problem we can face.

To avoid them, we could have only one multithreaded place, and it could be the controller. And when there are multiple requests from the client will be, the controller could convert from multithreaded to single-threaded flow and this approach could solve some problems, not all, but anyway it could reduce some problems that multithreaded can create.

Low Coupling

This pattern is a result of the OOP principle, encapsulation. This principle says, that in our system, we should create as fewer connections between our classes, modules, files, etc… as possible.

The fewer connections, the more stable the system. Let’s check an example below, we have 2 situations.

Option 1: Class A imports Class B. Class B imports Class C. Class C imports Class D

Option 2: Class A imports Class B, Class C, and Class D.

Imagine, that we need to replace class A with class X. In what case will be easier? 1 or 2? In the first option, we should add only 1 import, so we have only 1 dependency, while in option 2 we should add 3 imports and use all of them in new class X.

Of course, it’s a really simple example, but imagine, if we have a really big system and a large number of objects, that should be connected somehow.

Check another, almost the same example below:

Our class A imports class B and class D.

Class B imports class C.

Class C imports class D.

So, in this example, in class A we have a dependency from class D 2 times, directly from Class A to Class D, and from A → B → C → D.

So, following the Low Coupling principle, we should use as minimum connections as it is possible. In our example, we can avoid direct dependency from class D in class A, and keep the next connections:

The fewer connections, the more stable the system we will have. It will be much more easily replace components, make refactoring, reuse or delete components, and add (change) tests. Our code will be cleaner, and more understandable.

Of course, we should have and we will have connections. This principle says, that if the connection between modules is required, it’s ok. The principle says that we should avoid not requiring connection if they don’t need it.

Low coupling works really well with the Creator principle, that we discussed in the first part of this article series. Creator says, that each object (class) should be created only at that place, where this created object will be used. And if we follow the creator principle, we will have connections only in that place, where they are required.

High Cohesion

High cohesion it’s something similar to SRP (single-responsibility principle, from SOLID). The high cohesion principle says, that if you have a class (object, file, etc…) all fields and methods should do only one well-defined job.

Let’s check an example:

We have a class, that can send messages by email, SMS, Slack, etc…

class Sender {
  constructor(txt) {}
  
  sendEmail(options) {}
  
  sendSMS(options) {}
  
  sendToSlack(options) {}
}

In this example, we don’t follow the High Cohesion principle. Yes, our class is responsible for sending messages. But, we can see that we have 3 different channels (by email, SMS, and slack), and working with these channels will be different.

What is the problem, you can ask if we create 1 class for all 3 methods?

The first problem could be: for each of these channels, we could have different options structure, different parsing or preparation before sending methods, and different initial things.

The second one, that could be, for example, we have another project and we want also to send SMS. Of course, we can create it from scratch, but, if we have already a method for this purpose, we can reuse it, right?

And if we take our class Sender, for our new project, what will happen? Our class has 3 methods (but could have 30 or 50). But for our new project, we want only 1 method for SMS. Why we should have all other methods?

How to be in this situation? For example, we can copy only specific logic for sending SMS from our current class. But, if the class will be really big, it would be hard to understand what exactly logic, variables, and methods are needed for SMS and what don’t. So, in general, it’s not the best solution.

The better solution is following the High Cohesion principle: our class (object, file) should have only one responsibility. In our case we should create 3 different classes:

class EmailSender {}, class SMSSender {}, class SlackSender {}

And now, if for the new project we need the same functionality as we already have for SMS, we can just use SMSSenderclass, that will contain only needed SMS logic.

Using this principle, we have a good decomposition, we have understandable, maintainable, and reusable code.

And you can have a good question:

The low Coupling principle says: that we should create as fewer connections between our classes as possible when High Cohesion says that our class should have the logic, that does only one well-defined job, and if it’s not, we should separate our classes thereby creating new connections between classes.

The answer is that: Low Coupling principle says: that if you don’t need connections, you should avoid them. But if you do need it, it’s fine. Low Coupling is about creating as fewer connections as possible, but not about using 1 file for all modules and deleting all connections at all. If connections between modules are required, it’s totally fine. But if not, try to avoid them.

That’s all for today. I hope you enjoy this topic and in the next part, we will learn the remaining GRASP principles.

In the next article, we will cover all left principles from GRASP. See you there.


Written by serhiirubets | I'm a Fullstack JS engineer with 7 years of experience. Also, I'm a mentor, teacher, and author of front-end courses.
Published by HackerNoon on 2022/06/17