Ronaldo Vitto Lewerissa

Software engineering learning documentation.

Concise Cohesion and Coupling

Cohesion and coupling is rather a common concept among programming languages. It derives several principles such as Single Responsibility Principle.

Cohesion

Cohesion simply means specific on what it's doing, or in other word, focus on one thing only!

It refers to all software entities: modules, classes, functions.

Cohesion is the degree to which elements of a whole belong together. Methods and fields in a single class and classes of a component should have high cohesion. High cohesion in classes and components results in simpler, more easily understandable code structure and design. - Robert C. Martin

Rule of thumb: high cohesion and low coupling.

But you will notice that low coupling and high cohesion are contradict to one and another. When you're trying to decouple an entity, you will find yourself having a low cohesive entity.

On classes or modules, it rather easier to identify either they are doing single or multiple things.

Low Cohesive:

public class Staff() {  
  public void checkEmail() {}
  public void sendEmail() {}
  public void emailValidate() {}
  public void printLetter() {}
}

Staff is responsible on multiple task, which violate cohesiveness.

High Cohesive:

public class Staff() {  
  private double salary;
  public void setSalary() {}
  public double getSalary() {}
  public void printLetter() {}

On a function, it's a bit tricky to see if a function is focused.

Large function that has been extracted to smaller pieces turns into function comprises set of steps.

It might seen as follows:

function foo() {  
  doA();
  doB();
  doC();
}

Clearly the function does three things.

According to Uncle Bob, we can say a function is focused if each steps contains the same level of abstraction.

What it means briefly is that the function has to be equal in terms of implementation detail! For instance, sum('2', '5') is a much lower abstraction level than renderPage(). Fastest way to notice abstraction level of a function is too see LOC (lines of code) it has. It does not matter if the next function also comprises of a set of steps as long as each steps has it's abstraction level equal to it's caller abstraction level.

So continuing the previous example, doA() might look like:

function doA() {  
  doD();
  doE();
  doF();
}

Another trick is, if you can't extract another function from it (nor extracting merely as a restatement*), then your function is focused.

*such as extracting an if statement to it's own function.

A function that receives a flag or ones which has a switch statement are likely to do a lot of things.

function receivesFlag(let isTrue) {  
  if (isTrue) {
    doSomething();
  } else {
    doSomethingElse();
  }
}
function handleCases(let case) {  
  switch(case) {
    case A: {
      doFoo();
    }
    case B: {
      doBar();
    }
  }
}

Coupling

Coupling is a concept for classes, modules, and component.

Two classes, components or modules are coupled when at least one of them uses the other. The less these items know about each other, the looser they are coupled. A component that is only loosely coupled to its environment can be more easily changed or replaced than a strongly coupled component. - Robert C. Martin

It refers to how related classes/modules and how dependent they are on each other, and the difficulty on replacing either entity.

High coupling would make your code being difficult to perform any changes (maintain) as well as being replaceable.

This is due to the fact that when you change your dependencies, it is likely that you need to update code that depends on it.

This is true for me, there are times when I import a community-driven module for my codebase, and when that module was being updated, it breaks my current code, therefore requiring my code to adapt to the latest version.

As for the replaceability issue, your code functionality depends on many of your dependencies. Changing your code functionality as a whole would be difficult without changing the dependencies itself, therefore hard to replace.

Low coupling is the preferred methodology.

Tightly coupled code experience much harder maintenance (because changing your dependencies means changing your code) and difficult to replace.

Some guy on the internet made an analogy on this using iPhone an another typical smartphone:

On an iPhone, if you ever find yourself having a broken battery, the cost to replace the battery is so expensive such that you're better to replace the entire iPhone itself, whereas on other smartphone you can easily replace it with a new battery.

Written by Ronaldo Vitto Lewerissa

Read more posts by this author.