Single Responsibility Principle

This is a brand new concept I just learnt. Having this concept in mind will generally make us able to design a more robust software architecture.

Single Responsibility Principle is deeply related to Coupling and Cohesion, which also relates to the idea of separating concerns (The separation of Concerns).

Uncle Bob states that SRP is the idea in which:

Each software modules should have one and only one reason to change.

Well, I did not grasp it instantly the first time I hear about it. Reason to change? What the heck it's suppose to mean?

Turns out it's not that complicated though. Theoretically.

Single Responsibility Principle is all about people.

Who do you think will likely to care about the method you wrote on class Foo?

Who do you think will likely to request changes on the method you wrote on class Bar?

If more than a single person or -- as stated by Uncle Bob -- a single tightly coupled group of people representing a single narrowly defined business function, it is likely that you violate Single Responsibility Principle!

For instance (example from here):

public class Employee {  
  public Money calculatePay();
  public void save();
  public String reportHours();
}

The financial department will likely to care about calculatePay() method.

The IT department will likely to care about save() method.

The operational department will likely to care about reportHours() method.

If, for instance, financial department request changes on calculatePay() and accidentally breaks reportHours(), whose fault is this!?

If each methods otherwise be separated to it's own class, this sort of accident would not have happened!

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.

Brief Note on JavaScript Promises and Async/Await

Promise has the following syntax:

new Promise((resolve, reject) => {  
  // code goes here...
});

Invoking resolve() will returns a resolved promise.

let resolvedPromise = new Promise((resolve, reject) => resolve());  

Invoking reject() will returns an rejected promise.

let resolvedPromise = new Promise((resolve, reject) => reject());  

Throwing an error inside a .then will returns a rejected promise:

  Promise.then(() => throw new Error());

.then() will be executed if prior promise resolves, returns a rejected promise if prior promise is rejected.

.catch() will be executed if prior promise rejected, returns a resolved .

An unhandled rejected promise will not stop execution context from running, and is not handled the way normal throws (in try catch block).

Async function must yield (await) a promise!

Invoking an async function will returns a promise that resolves when the async function returns (pop), resolved with the returned value from the async function itself.

async().then((resolvedVal) => console.log(resolvedVal))  

If the returned is a promise, then it will resolved when the returned promise as in:

.then(() => new Promise((res, rej) => 2))

If none is explicitly returned, then it returns undefined as the resolved value.

Async functions are generators, which means it yields, where yield is equivalent to await. And it must yield a promise.

If the yielded promise resolves, it will continue executing the async function and pass along the resolved value back to it.

If it yields a promise that is rejected, the async function will returns with a rejected promise.

I currently have no idea how a async as a generator knows when it is done internally. What I mean is:

async function a() {  
  await new Promise((resolve, reject) => resolve());
  console.log('foo');
}
a().then((res) => console.log('bar'));  

This will outputs foo and bar respectively.

I was thinking that when it yields an object where .done() is true, then the async function will resolved, but apparently that is not the case.

Contributing to Open Source

I've learn from a couple of articles & tips here and there might help you to contribute to open source as well:

A tip from Dan Abramov:

Open source tip: you’ll learn A LOT from taking a single project you actively use, “watching” it on GitHub and reading every issue and PR.

It won’t make a lot of sense at first but stick with it.

JavaScript Design Pattern

Pattern is something I heard a lot in the developer community. Might be awesome to get some deeper insight what it actually is.

Without further ado, lets dive together on learning Design Patterns! I'll be using Addy Osmani's book, Learning JavaScript Design Patterns*, as a reference.

What is a Pattern, anyway?

Patterns are proven approach on solving over a recurring problems we might encounter during code production.

It matters because it has been tested over and over again on lots of scenarios.

Rather than having to write our own approach only to realize that it's not working as you might expected. Not saying that you shouldn't, but it would be wiser in terms of efficiency and effectivity to use an existing pattern.

Addy Osmani state the following advantages of pattern on his book, JavaScript Design Patterns:

  • Preventing minor issues (edge cases).

  • Applicable to lots of scenarios (not tied to a specific problem).

  • May decrease file size by reducing repetition also known as DRY (don't repeat yourself).

  • Add to a developer's vocabulary.

  • Improved over time through collective experience.

Know It's Brother

Design Pattern has it's opposite, named anti-pattern.

Well, as you might guess, it is a pattern that sucks!

Apparently it has been known for quite some time that using an anti-pattern leads to destruction.

So it might be better to just avoid it.

An example of anti-pattern is using document.write due it's capability to overwrite your whole document. Disaster indeed.

Patterns Into Sections

Patterns generally divided into three major classifications:

  • Creational

  • Structural

  • Behavioral

Take a closer look of the following image (taken from Slideshare):
JavaScript Design Pattern

I'll be covering each and every one of these into several blog posts.

Pattern Table of Content