Categories
JavaScript Best Practices

JavaScript Clean Code — Classes

Spread the love

Classes in JavaScript is syntactic sugar on top of the prototypical inheritance features of the language. However, in terms of writing clean code, the principles still apply since they have the same structure as classes in class-based languages.

In this article, we’ll look at how to write JavaScript classes in a clean and maintainable way.

Class Organization

Classes should begin with the constructor with a list of variable members inside.

The methods of the class can follow after the constructor and the list of variables.

Encapsulation

We should keep private variables in blocks inside the class and not as a property of this.

This way, code that’s outside the class can’t access them and change the value of them accidentally.

We should define getters and setters to get and set variables that aren’t part of this, or to use them for computed properties.

This also helps with hiding implementation so that classes won’t use them accidentally, which creates a tight coupling of our code.

Classes Should be Small

Classes should be small. They shouldn’t have more than one responsibility. What we don’t want is to have classes that do multiple things. A God class is what we don’t want.

The name of the class should tell us what responsibilities it fulfills. If a method doesn’t something that’s not covered by the name of the class, then it shouldn’t be there.

We should be able to describe what our class does without using the words ‘if’, ‘and’, ‘or’ or ‘but’.

For example, the following example of a class that has one responsibility:

class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }

  get area() {
    return this.length * this.width;
  }
}

Our Rectangle class only has one responsibility and that’s to represent a rectangle.

On the other hand, if we have a createCircle method as follows, then we can’t describe our class without those conjunctions since our Rectangle class has more than one responsibility:

class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }

  get area() {
    return this.length * this.width;
  }

  createCircle(radius) {

  }
}

Single Responsibility Principle

Classes should have one responsibility and one reason to change. This principle gives us a good guideline on a class’s size. If it has more than one responsibility than it’s too big.

Identifying responsibilities let us create better abstractions in our code. We should move the createCircle method to its own class Circle since circles have nothing to do with rectangles.

So we can write it as follows:

class Circle {
  constructor(radius) {
    this.radius = radius;
  }
  get area() {
    return Math.PI * (this.radius ** 2);
  }
}

The single responsibility principle is one of the more important principles of object-oriented design. It’s also very easy to understand.

Unfortunately, it’s also one of the more principles that’s ignored. People just get their programs to work and forget to clean them up.

They move on to the next problem once they get their code working and just didn’t bother to clean it up.

Some people are also worried that making smaller, single-responsibility classes make it more difficult to understand the big picture. They think that navigating from class to class makes it harder to get the full picture of the system.

However, this isn’t true since they have the number of moving parts. If we put everything in one class, then we still have to dig for them inside the class rather than in different files. It’s no different.

Smaller classes have less code in it so it’s easier to read them.

Cohesion

Classes should have a small number of instance variables. Each method should manipulate one or more instance variables. A class where each variable is used by each method is maximally cohesive.

We like cohesion to be high so that the methods and instance variables are co-dependent and stay together as a whole.

High cohesion makes reading the code easy since it only revolves around a single concept. They’re also less frequently changed since each class doesn’t do much.

For example, our Circle class:

class Circle {
  constructor(radius) {
    this.radius = radius;
  }
  get area() {
    return Math.PI * (this.radius ** 2);
  }
}

is cohesive because we used our radius instance variable in the area getter method, so we used every instance variable in our method.

Maintain Cohesion Means Many Small Classes

As we can see, it’s easy to create cohesive classes that are small. They have fewer instance variables so it’s easy to use them all in methods.

Bigger classes have problems maintaining cohesion because we keep adding new instance variables that only a few methods use.

This means that when classes lose cohesion then we should split them up. So instead of writing:

class Shape {
  constructor(radius, length, width) {
    this.radius = radius;
    this.length = length;
    this.width = width;
  }

  get circleArea() {
    return Math.PI * (this.radius ** 2);
  }

  get rectangleArea() {
    return this.length * this.width;
  }
}

We should instead split them into the Rectangle and Circle class since the instance variables and methods make no sense being together.

So it’s better to write:

class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }

  get area() {
    return this.length * this.width;
  }
}

class Circle {
  constructor(radius) {
    this.radius = radius;
  }
  get area() {
    return Math.PI * (this.radius ** 2);
  }
}

Conclusion

JavaScript classes should follow the clean code principles, even though it’s only syntactic sugar on top of its prototypical inheritance model.

We should have cohesive classes where methods use all the instance variables defined.

Also, each class should have a single responsibility. This and cohesion makes reading the code easy since they only revolve around a single concept. They’re also less frequently changed since each class doesn’t do much.

Small classes are better than large classes since they don’t have more than a single responsibility and they are more cohesive.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *