paint-brush
Understanding SOLID Principles in JavaScriptby@dangtrunganh
29,790 reads
29,790 reads

Understanding SOLID Principles in JavaScript

by Trung Anh DangMay 10th, 2020
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

Understanding SOLID Principles in JavaScript is an acronym that stands for five design principles. The SOLID principles are useful when constructing both individual modules or larger architectures. They include the Single Responsibility Principle, the Liskov Substitution principle, the Interface Segregation principle and the Dependency Inversion principle. These principles were developed by Robert C. Martin in the late 80s and are now known as SOLIDS. They are an acronym for five specific design principles that stand for five different design principles in JavaScript.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Understanding SOLID Principles in JavaScript
Trung Anh Dang HackerNoon profile picture

The invention of SOLID principles began in the late 80s. Robert C. Martin started to develop these principles while arguing the principle of software design on USENET (an early kind of Facebook). After addition and subtraction, Robert C. Martin formulated the principles in the early 2000s. It was until 2004 that the principles were arranged and called SOLID principles. It is an acronym that stands for five specific design principles.

  • S represents the Single Responsibility principle
  • O represents the Open Closed principle
  • L represents the Liskov Substitution principle
  • I represents the Interface Segregation principle
  • D represents the Dependency Inversion principle

The SOLID principles are useful when constructing both individual modules or larger architectures. So, we’re going to explore each principle alongside examples in JavaScript.

The Single Responsibility Principle

This principle was described in the work of Tom DeMarco¹ and Meilir Page-Jones². They called it cohesion. They defined cohesion as the functional relatedness of the elements of a module.

What goes wrong with the SRP?

In fact, this principle might be the least well understood because has a particularly inappropriate name. Many developers understood that every module should do just one thing. Make no mistake, there is a principle like that. But it is not one of the SOLID principles and it is actually not the SRP.

So, what is the SRP?

It has been described as follows: “Each software module has one, and only one, reason to change”. Because, software systems are changed to meet user demands, to satisfy stakeholders, so we can rephrase the principle to say this: “Each module should be responsible to one, and only one, user or stakeholderBut there will likely be more than one user or stakeholder who wants the system to change in the same way, we called them as an actor or a group, so the final version is: “Each module should be responsible to one, and only one, actor”.

We look at some examples to understand what is the SRS

Let assume that we have an Employee object, it has three functions: calculatePay(), reportHours() and save().

Unfortunately, it is violating the SRP because those three functions are responsible for three different actors.

  • The calculatePay() function is responsible for the accounting department.
  • The reportHours() function is used by the human resources department.
  • The save() function is specified by the database administrators.

So, the way to avoid this problem is to separate code that supports different actors.

The EmployData object to save a shared simple data structure, it used by all three actors.

The PayCalculator object has the 

calculatePay()
 method.

The HourReporter object has the 

reportHours()
 method.

The EmployeeServer object has the 

save()
 method.

It is how we use the SRS to refactor bad codes. Each function is responsible for a specific actor. The SRP is one of the simplest of the principles, and one of the hardest to get right.

The Open-Closed Principle

Bertrand Meyer made this principle famous in the 1980s, which appeared in his book Object-Oriented Software Construction³. Software systems be designed to allow the behavior of those systems to be changed by adding new code, rather than changing existing code.

What is the open–closed principle (OCP)?

The OCP states the following: “Software entities (classes, modules, functions, and so on) should be open for extension, but closed for modification” by Meyer, BertrandThis principle advises us to refactor the system so that further changes of that kind will not cause more modification. Most developers recognize the OCP as a principle that guides them in the design of classes and modules.

There are two primary attributes in the OCP, they are.

  • Open for extension — we are able to extend what the module does.
  • Closed for modification — extending the behavior of a module does not result in changes to the source or binary code of the module.

It would seem that these two attributes are at odds with each other because the normal way to extend the behavior of a module is to make changes to the source code of that module.

So, how to implement the OCP?

Let assume that every employee has a role and granted privileges. But how if we introduce a new role to the system and don’t modify existing things. So rather we can do like the below example to make it pass the OCP.

So as the above example, we don’t have to modify the existing code rather we can extend it to add a new role. The OCP is one of the driving forces behind the architecture of systems. The goal is to make the system easy to extend without incurring a high impact of change.

The Liskov Substitution Principle

Barbara Liskov’s famous definition of subtypes, from 1988 in a conference keynote address titled Data Abstraction and Hierarchy. In short, this principle says that to build software systems from interchangeable parts, those parts must adhere to a contract that allows those parts to be substituted one for another.

You can understand it in a way as follows

One of the classic examples of this principle is a rectangle having four sides. A rectangle’s height can be any value and width can be any value. A square is a rectangle with equal width and height. So we can say that we can extend the properties of the rectangle class into square class. In order to do that you need to swap the child (square) class with parent (rectangle) class to fit the definition of a square having four equal sides but a derived class does not affect the behavior of the parent class so if you will do that it will violate the Liskov Substitution Principle.

See what’s going wrong?

Consider that we have an application that uses a rectangle object defined as follows.

Based on the knowledge that a square is a rectangle whose sides are equal in length, we decide to create a square object to use in place of a rectangle.

Unfortunately, a problem is discovered when the application attempts to use our square in place of a rectangle. It turns out that one of the methods computes the rectangle’s area like so.

When the method is invoked with a square, the product is 16 rather than the expected value of 12. Our square object violates the Liskov Substitution Principle with respect to the area function. In this case, the presence of the length and width properties was a hint that our square might not end up being 100% compatible with the rectangle, but we won’t always have such obvious hints.

The Interface Segregation Principle

This principle advises software designers to avoid depending on things that they don’t use.

Taking a simple example to understand the ISP

Suppose if you enter a restaurant and you are pure vegetarian. The waiter in that restaurant gave you the menu card which includes vegetarian items, non-vegetarian items, drinks, and sweets. In this case, as a customer, you should have a menu card which includes only vegetarian items, not everything which you don’t eat in your food.

Here the menu should be different for different types of customers. The common or general menu card for everyone can be divided into multiple cards instead of just one. Using this principle helps in reducing the side effects and frequency of required changes.

How to implement the ISP in JavaScript?

Because we don’t have an interface by default in JavaScript. But we all would have faced situations where we want to do so many things on the constructor of a class. So, how to implement the ISP now?

Let’s say some settings we have to do in the constructor. The settings we do should be segregated from the other unwanted settings in the constructor.

Here, validateUser() function will get invoked in initiateUser()constructor call even though it’s not needed all the time. We can bring this into ISP with the following code.

As the above code, we are segregating the unwanted logic from the contractor function.

The Dependency Inversion Principle

Before we discuss this topic keep in mind that Dependency Inversion and Dependency Injection both are different concepts. Most of the people get confused about it and consider both are the same. Now key points are here to keep in mind about this principle.

Don’t confuse with Dependency Injection Principle

The Dependency Inversion Principle (DIP) tells us that the most flexible systems are those in which source code dependencies refer only to abstractions, not to concretions. Rather, details should depend on policies.

Looking at a real-life example

You can consider the real-life example of a TV remote battery. Your remote needs a battery but it’s not dependent on the battery brand. You can use any XYZ brand that you want and it will work. So we can say that the TV remote is loosely coupled with the brand name. Dependency Inversion makes your code more reusable.

How to implement the DIP in JavaScript?

In a statically typed language, like Java, this means that the use, import, and include statements should refer only to source modules containing interfaces, abstract classes, or some other kind of abstract declaration. Nothing concrete should be depended on. In case of JavaScript How we can implement the DIP?

Let take a simple example to understand how we can do it easily.

I want to contact the server for some data. Without applying DIP, this might look like as follows.

With DIP, I might instead write code like.

fillFromServer("/address/to/data", thingyView)

Where the abstraction 

fillFromServer
 function can for the particular case where we want to use jQuery's Ajax be implemented as follows.

The abstraction view can be implemented for the particular case of a view based on elements with IDs thingy1 and thingy2 as follows.

Easy, right? I hope this gives you a basic understanding of how to apply the SOLID principles in JavaScript.

Previously published at https://medium.com/javascript-in-plain-english/rethinking-solid-principles-in-javascript-7effdd4dc37d