Categories
JavaScript Best Practices

JavaScript Clean Code — SOLID

Like any other programming language, JavaScript is also subject to the principles outlined in SOLID.

SOLID consists of 5 concepts that we can use to make our programs better. They are:

  • Single responsibility principle
  • Open / closed principles
  • Liskov Substitution principle
  • Interface segregation principle
  • Dependency inversion principle

In this article, we’ll look at each and see how we can apply them to JavaScript programs.

Single Responsibility Principle

The single responsibility principle says that each of our classes has to be only used for one purpose.

We need this so that we don’t have to change code as often when something changes. It’s also hard to understand what the class is doing if it’s doing many things.

Unrelated concepts in the same class also make comprehending the purpose of the code harder.

For example, we can write something like the following to follow the single responsibility principle:

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

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

The Rectangle class above only has the length and width of a rectangle as members and lets us get the area from it.

It does nothing else, so it follows the single responsibility principle.

A bad example would be:

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

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

  createCircle() {

  }
}

We should have a createCircle method in a Rectangle class since they’re unrelated concepts.

Open/Closed Principle

The open/closed principle states that a piece of software is open for extension but closed for modification.

This means that we should be able to add more functionality without changing existing code.

For example, if we have the following Rectangle class:

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

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

Then if we want to add a function to for calculating its perimeter, we can do it by adding a method to do it as follows:

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

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

  get perimteter() {
    return 2 * (this.length + this.width);
  }
}

As we can see, we didn’t have to change existing code to add it, which satisfies the open/closed principle.

Liskov Substitution Principle

This principle states that if we have a parent class and a child class, then we can interchange the parent and child class without getting incorrect results.

This means that the child class must implement everything that’s in the parent class. The parent class serves the class has the base members that child classes extend from.

For example, if we want to implement classes for a bunch of shapes, we can have a parent Shape class, which are extended by all classes by implementing everything in the Shape class.

We can write the following to implement some shape classes and get the area of each instance:

class Shape {
  get area() {
    return 0;
  }
}

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

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

class Square extends Shape {
  constructor(length) {
    super();
    this.length = length;
  }

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

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

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

const shapes = [
  new Rectangle(1, 2),
  new Square(1, 2),
  new Circle(2),
]

for (let s of shapes) {
  console.log(s.area);
}

Since we override the area getter in each class that extends Shape , we get the right area for each shape since the correct code is run for each shape to get the area.

Interface Segregation Principle

The interface segregation principle states that “clients shouldn’t be forced to depend on interfaces that they don’t use.”

This means that we shouldn’t impose the implementation of something if it’s not needed.

JavaScript doesn’t have interfaces, so this principle doesn’t apply directly since it doesn’t enforce the implementation of anything via interfaces.

Dependency Inversion Principle

This principle states that high-level modules shouldn’t depend on low-level modules and they both should depend on abstractions, and abstractions shouldn’t depend upon details. Details should depend upon abstractions.

This means that we shouldn’t have to know any implementation details of our dependencies. If we do, then we violated this principle.

We need this principle because if we do have to reference the code for the implementation details of a dependency to use it, then when the dependency changes, there’s going to be lots of breaking changes to our own code.

As software gets more complex, if we don’t follow this principle, then our code will break a lot.

One example of hiding implementation details from the code that we implement is the facade pattern. The pattern puts a facade class in front of the complex implementation underneath so we only have to depend on the facade to use the features underneath.

If the underlying classes change, then only the facade has to change and we don’t have to worry about making changes to our own code unless the facade has breaking changes.

For example, the following is a simple implementation of the facade pattern:

class ClassA {

}

class ClassB {

}

class ClassC {

}

class Facade {
  constructor() {
    this.a = new ClassA();
    this.b = new ClassB();
    this.c = new ClassC();
  }
}

class Foo {
  constructor() {
    this.facade = new Facade();
  }

}

We don’t have to worry about ClassA , ClassB and ClassC to implement the Foo class. As long as the Facade class doesn’t change, we don’t have to change our own code.

Conclusion

We should follow the SOLID principle to write code that’s easy to maintain.

To following SOLID, we have to write classes that only do one thing.

Our code has to open for extension but closed for modification. This reduces the chance of messing up the existing code.

Parent and child classes have to be interchangeable when we switch them. When we switch them, the result still has to be correct.

Finally, we should never have to depend on the implementation details of any piece of code that we reference so that we won’t end up with lots of breaking changes when something changes.

This lets us reduce the coupling between modules.

Categories
JavaScript Best Practices

JavaScript Clean Code: Emergent Design

Emergent design is a framework for systematic change. It focuses on delivering small pieces of working code with business value. It’s part of agile software development, which is more flexible than traditional software development.

In this article, we’ll look at the four principles of software development with emergent design.


What Are the Four Rules of Emergent Design?

The four rules of emergent design are simple. They’re:

  • Running all the tests.
  • Removing duplication.
  • Expressing the intent of the programmer.
  • Minimizing the number of classes and methods.

Running All the Tests

Running tests ensures that our system is still acting as expected after we made changes. The design might have been perfect on paper, but after doing the work, we may find unexpected flaws in the system. So we have to run tests to make sure that our changes are good.

We have to write code that’s easily testable. This means that the code should be loosely coupled so it can be easily tested by unit tests. Loose coupling also means that code can be more easily modified since it doesn’t affect other parts of the code.

We can use dependency inversion tools like dependency injection and abstractions to minimize coupling in JavaScript code.

Writing tests enforce low coupling and high cohesion since writing easily testable code requires code with these two characteristics.


Refactoring

Once we have tests, we can refactor without thinking too much about breaking things since we have tests to make sure that we don’t break anything.

We can apply the principles of good software design like SOLID when we refactor the code.

When we refactor, it’s time to choose better names, write smaller classes and methods, reduce the number of classes and methods, and write more expressive code.


Removing Duplicate Code

Duplicate code should be removed when we refactor code. Less duplicate code means less code to maintain and fewer places that we’ll forget to change.

Duplicate is not just code that is exactly the same. Their implementation can also be duplicated. For example, we can have a size and isEmpty methods for our own data structure.

They’re so similar that we don’t really need both. We can just make isEmpty return size() === 0;.

Code with duplicate implementation can be extracted into a common location. Anything that violates the single responsibility principle has to be extracted into its own class.

For example, let’s say we have a postal rate calculator to calculate international mailing rates as follows:

class PostalRateCalculator {
  calculateUSRates() {

  }

  calculateEURates() {

  }
}

We really don’t need two functions since the only difference between the two methods is the postal rates. Therefore, we can combine them into one method and use the different rates for different countries to calculate the rates as follows:

class PostalRateCalculator {
  calculateRates(country) {
    if (country === 'US') {

    } else if (country === 'UK') {

    }
    //...
  }
}

This way, we don’t have two methods that do similar things cluttering up the code.


Expressive Code

Expressive code means easy to understand code. Our code has to communicate our intentions clearly so that readers won’t misunderstand what we’re trying to do.

We can be expressive by choosing good names for everything.

Also, keeping a standard nomenclature for everything reduces the chance that we’ll be misled when other people read our code.

Well-written unit tests are also expressive. They should tell us a lot about what the program does since we have inputs and outputs all in one place. In addition, each test has its own description to tell us even more information about the part of the system that it’s testing.


Minimal Classes and Methods

If we don’t need the code, then we shouldn’t write it. Useless or duplicate code is just bad.

They clutter up our programs while providing little to no value.


Conclusion

There isn’t a clear cut way to use emergent to write our programs. However, it’s mostly down to refactoring using the clean code principles and then using unit tests to make sure everything still runs correctly.

Also, we have to write tests as if they’re documentation. Just like normal code, they have to be expressive.

Duplicate code or code with similar implementation can probably be merged into one.

Categories
JavaScript Best Practices

JavaScript Clean Code — Law of Demeter

The Law of Demeter states that we should hide the implementation details of our code as much as we can. This ensures that we have loose coupling between our code.

In this article, we’ll look at how to apply the Law of Demeter to our code.

Why do we Need Loose Coupling?

Loose coupling means that we don’t have to worry about changing a lot of code when one piece of code changes.

Each unit only has limited knowledge about other units so that only a few parts of the other units are referenced.

They only talk to their immediate friends, so that they don’t talk to other parts that aren’t related.

So this means that if we have 2 classes A and B , then A only references the methods of B than has to be referenced. Other members are kept private along with other implementation details.

How do we apply the Law of Demeter?

We can apply the Law of Demeter to our JavaScript code by writing it in a way that references a few classes and members of them as possible.

An example of classes that references each other too much is the following:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  setAddress(address) {
    this.address = new Address(address);
  }

  getAddress() {
    return this.address;
  }
}

We have the Address class that references PostalCode, and Person class that references Address and Occupation .

If any of Address and PostalCode change, then we have to change the Person and Address class.

Also, we have to set the postal code of a Person by writing:

person.getAddress().getPostalCode().setPostalCode('12345');

That’s a lot of chaining involving returning instances of different classes. If any of these methods change, then the whole chain has to be rewritten.

What we should do instead is to combine all the references into one method as follows:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  setAddress(address) {
    this.address = new Address(address);
  }

  getAddress() {
    return this.address;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

Then we only have to update the Person class if the PostalCode class changes instead of updating the whole chain of calls just to update the getting and setting the postal code when the PostalCode class changes.

The point is that we should have to know the whole system to get something done.

PostalCode doesn’t have to be connected to Address since they can be changed individually.

If we couple them together, then we have to know about Address before changing PostalCode .

The example above shows coupling that can be avoided and it should be.

Facade Pattern

We can also use the facade pattern to hide the complexity of a system so that we don’t have to know about them.

For example, we can hide multiple classes behind a facade class and then use the facade class to indirectly interact with multiple classes that hide behind the facade class as follows:

class ClassA {

}

class ClassB {

}

class ClassC {

}

class Facade {
  constructor() {
    this.a = new ClassA();
    this.b = new ClassB();
    this.c = new ClassC();
  }
}

class Foo {
  constructor() {
    this.facade = new Facade();
  }

}

In the example above, the Foo class knows nothing about what’s behind the Facade class. The Facade class holds instances of ClassA , ClassB , and ClassC .

It provides a simple interface to a complex system which consists of ClassA , ClassB , and ClassC .

When any of the classes behind Facace changes, we just have to change the Facade class.

It serves as an entry point to all we want to do with those classes. Instead of referencing them all individually and creating a mess of references, we have one unified interface for all of them.

This satisfies the Law of Demeter because we only access the Facade class to do anything with ClassA , ClassB , and ClassC . We don’t have to know about the underlying implementation of them.

It makes the software easy to use, understand and test since we only have to use and test Facade to interact with all the classes underneath.

It removed the need to reference multiple parts of a complex system since the Facade class provides all we need.

If the code underneath the facade class is poorly designed, we can also wrap around it with a well-designed API that helps people using the facade class to work with it in an easy way.

Most importantly, tight coupling is eliminated since nothing but the facade class references the complex code underneath it.

Conclusion

The Law of Demeter is all about hiding implementation as much as possible to outside code so that they won’t have to reference different parts of the code to get something done.

We should only create classes that talk to closely related classes, rather than talking to everything.

Talking to everything creates a mess of references that’s hard to figure out when we need to change the code.

One good way to implement the laws in the Law of Demeter is to use the Facade pattern. The pattern states that we have a facade class that serves as the entry point for other classes which are part of a complex system.

It’s used to provide an easy to use API which hides the implementation underneath. So if we do need to change the code underneath, we only have to update the facade class rather than everything that references the implementation code underneath.

Categories
JavaScript Best Practices

JavaScript Clean Code: Error Handling

Error handling is an important part of programs. There are many situations where our programs encounter values that are unexpected and we have to properly handle them.

In this article, we’ll look at how to handle them so that errors are easily found easily and gracefully handled.


Exceptions are Better than Return Codes

Throwing exceptions is better because they let us know that an error exists and that we have to handle it.

Most modern programming languages have exceptions built-in, so we should throw them instead of returning an error code.

Error codes aren’t as explicit and may be missed. Exceptions are also much cleaner since we don’t have to check all the codes that may be returned.

For example, if we return error codes in our functions then we may have code that looks something like this:

We have to return all the error codes in our setNumFruit method. Also, before we do something after the class definition, we have to check all the error codes.

We can throw exceptions instead:

We’ve eliminated the need to check all the error codes by wrapping the code we want to run in a try block. Now we can just catch the error instead of checking all the error codes that may be returned.

This is much better than checking all error codes before doing something — it’s especially important as code becomes more complex.


Write Try-Catch-Finally

We should wrap our try in the code that throws exceptions that we want to catch. It creates its own scope for block-scoped variables so anything declared with let or const can only be referenced in the try block.

Variables declared with var are hoisted so that they can be referenced outside the block. We won’t get an error even if they’re referenced outside the block. This will get us 1:

try {  
  var x = 1;  
} catch (ex) {  
  console.error(ex);  
}  
console.log(x);

But this will get us Uncaught ReferenceError: x is not defined:

try {  
  let x = 1;  
} catch (ex) {  
  console.error(ex);  
}  
console.log(x);

Don’t Ignore Caught Errors

We should report our errors when they’re caught. They shouldn’t be caught and then ignored. This is because we don’t want to sweep underlying issues under the rug.

Reporting exceptions let us know about the error and then handle it accordingly.

The examples above, like the console.error, call in the following:

try {  
  const error = fruitStand.setNumFruit(1);  
  console.log(fruitStand.numFruits);  
} catch (ex) {  
  console.error(ex);  
}

Thi s one of the ways to report the error. We can also use other libraries to report the error in a central place.


Don’t Ignore Rejected Promises

Like any other exception, rejected promises also need to be handled. They can be handled with the callback that we pass into the catch method or use the try...catch block for async functions — they’re the same.

For example, we can report the error by writing the following:

Promise.reject('fail')  
  .catch(err => {  
    console.error(err);  
  })

Or for async functions we can write this:

(async () => {  
  try {  
    await Promise.reject('fail')  
  } catch (err) {  
    console.error(err);  
  }  
})()

Providing Context with Exceptions

We should provide enough context in exceptions that other developers can pinpoint the error when an exception is thrown.

We can get a stack trace with exceptions in JavaScript, but it may not provide all the information we need.


Conclusion

When we handle errors, throwing exceptions is better than returning error codes since they let us use the try...catch block to handle errors. This is much cleaner than checking multiple error codes.

When we throw exceptions, we have to provide enough details to pinpoint the problem.

We shouldn’t just ignore errors after catching them. We should at least report them so that they can be looked at.

Finally, rejected promise errors should be handled the same way as other exceptions.

Categories
JavaScript Best Practices

JavaScript Clean Code — Objects and Classes

When writing clean code we have to be careful about the objects and classes or constructors that we define. We don’t want to expose data that we don’t want the outside world to view, and we want our data structures to be defined in a way that’s easy to reuse.

In this article, we’ll look at how to organize our data in a way that’s easy for us to use.

Use Classes Over Constructor Functions

We should use classes instead of constructor functions. It’s much clearer and inheritance can be done much more easily.

They’re the same underneath. Class syntax is syntactic sugar for the constructor function syntax.

For example, we can define a Person class as follows:

class Person{
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

We can see that our constructor takes in name and age as parameters and set it as values of instance variables.

Where it shines is when we need to extend the class. We can create an Employee class that has the instance variables of the Person class and extends it as follows:

class Employee extends Person {
  constructor(name, age, employeeCode) {
    super(name, age);
    this.employeeCode = employeeCode;
  }
}

All we have to do is to call the superclass’ constructor with the super call, and then we can set the instance variables of the Employee class in the constructor.

Using Getters and Setters

Getters and setters are good for hiding the actual data that’s being accessed. We can have computed properties, which means that we can abstract way the implementation of those computed properties so that we don’t have to worry about changing the code that uses them when we change the implementation.

Also, setters let us validate before setting the data rather than setting them directly.

Likewise, error handling and logging can also be added easily into them.

We can also lazy load object properties with getters.

For example, we can use getters as follows:

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

  get area() {
    return this._length * this._width;
  }
}

As we can see, we can use it to create a computed property to get the area of a rectangle.

Then we can use it as follows:

let rectangle = new Rectangle(1, 2);
console.log(rectangle.area);

We can add validation into setters as follows:

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

  get area() {
    return this._length * this._width;
  }

  get length() {
    return this._length;
  }

  set length(length) {
    if (length <= 0) {
      throw new Error('Length must be bigger than 0');
    }
    this._length = length;
  }

  get width() {
    return this._width;
  }

  set width(width) {
    if (width <= 0) {
      throw new Error('Width must be bigger than 0');
    }
    this._width = width;
  }
}

Note that it’s also a good idea to define getters so that we can access the properties’ values.

Keeping Members Private

There’re no private variables in JavaScript classes, so we should define private variables in block scope so that they won’t be exposed to the public with let and const .

Chaining Methods

Chaining methods make calling a series of functions less verbose. We can define a function that can be chained by returning this .

For example, we can write the following:

class Person {
  setName(name) {
    this.name = name;
    return this;
  }

  setAge(age) {
    this.age = age;
    return this;
  }
}

Then we can use it as follows:

const person = new Person().setName('Joe').setAge(10);

Then when we log person , we get:

{name: "Joe", age: 10}

This is a common pattern that’s used in many libraries like jQuery and Lodash.

Preferring Composition Over Inheritance

We should leave a class inheritance for ‘is-a’ relationships only. An is-a relationship is a subset of some bigger entity. For example, an employee is a person.

Otherwise, we should use composition instead. For example, if we want to keep the address for a Person, we can create a class called Address and instantiate it in the methods of the Person class and use it there instead.

A Person has a ‘has-a’ relationship with Address , so we shouldn’t use inheritance in this case.

For example, we can write something like the following code:

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }
}

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  setAddress() {
    const address = new Address('123 A St.');
  }
}

Conclusion

The class syntax is much easier to understand than the constructor function syntax, especially when we need to do inheritance. Therefore, it’s time to move away from the constructor function syntax as fast as we can.

We should only use class inheritance in ‘is-a’ relationships. Otherwise, we should use composition instead.

Also, we can use getters and setters to keep the implementation of the members underneath private. We can use getters for computed properties and setters for running code before setting values to a member. For example, we can run some validation code before setting the value with a setter.

Chaining methods is also a commonly accepted way to clean up code by making calls less verbose.