Categories
JavaScript Design Patterns

Commonly Used Design Patterns in JavaScript

To organize code in a standard way in a program, we have design patterns to enable us to do this.

The book Design Patterns: Elements of Reusable Object-Oriented Software was published in 1994 which came up with 23 design patterns that are used by object-oriented programs.

In this article, we’ll look at some of the more commonly used design patterns in JavaScript programs, including the singleton, iterator and factory patterns.

Singleton

Singleton is a pattern that is common in JavaScript. It’s a class that only creates one instance of an object only.

In JavaScript, we have the object literal to define an object that isn’t an instance of a class. For example, we have:

const obj = {
  foo: 1
}

We also have the class syntax, which does the same thing as constructor functions, where we can define a getInstance method to get an instance of a class.

To make a singleton class, we can write something like the following:

class Foo {
  static getInstance() {
    this.foo = 1;
    this.instance = this;
    return this.instance;
  }
}

const foo1 = Foo.getInstance();
const foo2 = Foo.getInstance();
console.log(foo1 === foo2);

Since we assigned this to this.instance and returns it in getInstance , we should always get the same reference.

We should get that foo1 === foo2 being true since they reference the same instance of the Foo class.

Singleton classes are useful for facade objects to hide the complexities of a program. State objects which are shared by different parts of a program also make singleton classes a good choice.

They also let us share data without creating global variables. Global scope isn’t polluted, so it’s a good choice for sharing data.

Iterator

The iterator pattern is a pattern where we create an iterator to sequentially access data in a container.

We need this pattern to traverse items in collections without exposing the underlying data structure. And we should also be able to traverse objects in an aggregate object without changing its interface.

In JavaScript, we can define iterable objects and generators to do this.

To define an iterable object, we can write:

const iterableObj = {
  *[Symbol.iterator]() {
    let index = 0;
    const arr = [1, 2, 3];
    while (index < arr.length) {
      yield arr[index];
      index++
    }
  }
}

for (const obj of iterableObj) {
  console.log(obj);
}

As we can see iterableObj hides the array that’s inside it from the outside. Also, we can change it to anything else we want and not have to worry about changing the code outside.

[Symbol.iterator] and generator functions have been available since ES6. Ever since then, we can define iterators easily.

We can also define generator functions which return generators. To do this, we can write the following:

const generatorFn = function*() {
  let index = 0;
  const arr = [1, 2, 3];
  while (index < arr.length) {
    yield arr[index];
    index++
  }
}

for (const obj of generatorFn()) {
  console.log(obj);
}

It’s similar to iterableObj since they both use generators. The difference is that generatorFn is a generator function, which returns generators. Generators are what we iterate through.

Factory

The factory pattern centers around the factory function. It’s a function that returns objects without using the new keyword to construct an instance of a class or a constructor function.

We want to use the factory pattern to make code more readable since it lets us create functions that returns new objects from more code.

It also lets us return objects without knowing the code that creates the object. We don’t have to worry about what class is instantiated to create the object or how it’s created otherwise.

In JavaScript, when a function returns an object without the new keyword, then it’s a factory function.

For example, we can create a simple factory function as follows:

const createFoo = () => ({
  foo: 1
});

The createFoo function always returns the { foo: 1 } object when it’s called. It always returns an object without the new keyword, so it’s a factory function and uses the factory pattern.

Factory functions are common in JavaScript. For example, browsers have the document.querySelector() method to get a DOM object given the CSS selector.

There’re similar methods that return objects everywhere in JavaScript.

The singleton pattern creates a single instance of an object. In JavaScript, we can do this with object literals or a getInstance method of a class that always returns the same instance of a class.

It’s useful for sharing data and hiding the complexity of implementation.

The iterator pattern lets us traverse through collections of objects without knowing the implementation of it. Also, it lets us change the underlying data structure and logic without changing the interface.

JavaScript has iterators to do this, in addition to generator functions.

Finally, the factory pattern is common in JavaScript. Anything function that returns a new object without using the new keyword to instantiate it is a factory function.

It’s useful for hiding the complexities of creating objects from other developers. For example, we don’t have to worry about how document.querySelector() gets an element from the DOM. It just returns the first DOM element that matches the selector.

Categories
JavaScript Design Patterns

JavaScript Design Patterns — Chain of Responsibility, Singleton, and Flyweight Patterns

Design patterns are the basis of any good software. JavaScript programs are no exception.

In this article, we’ll look at the chain of responsibility, singleton, and flyweight patterns.

Chain of Responsibility Pattern

The chain of responsibility is similar to the observer pattern, except that it sends the notification to one object, and then that object sends the notification to another object, and so on.

In the observer pattern, the notification is sent to all the observers at the same time.

For instance, if we send something to one object and then that object picks that up and does something with and send that to another and so on, then that implements the chain of responsibility patterns.

We can implement that as follows:

const backend = {
  receive(data) {
    // do something with data
  }
}

const middleLayer = {
  notify(data) {
    backend.receive(data);
  }
}

const frontEnd = {
  notify(data) {
    middleLayer.notify(data);
  }
}

In the code above, we have the frontEnd , which calls the notify method of the middleLayer object, which in turn called the backend ‘s receive method.

This way, we can pass data between them with one method seamlessly.

As long as we don’t expose any other methods that do communication between both objects, we have a clean way of sending data between frontEnd and middleLayer and backEnd .

Singleton

The singleton pattern is where we only instantiate one instance of an object.

In JavaScript, we can create objects with one instance by creating an object literal.

We can create one by writing:

const obj = {
  foo: 1,
  bar: 'baz'
}

An object literal is just sets of key-value pairs, where the value can also be other objects.

If we use classes, we can also write:

class Foo {
  constructor() {
    if (!Foo.instance) {
      Foo.instance = {
        foo: 1,
        bar: 2
      }
    }
    return Foo.instance;
  }
}

We assigned the created instance to the instance property of Foo .

Then when we create the constructor, we check the instance property to see if anything’s created for it.

If there’s nothing assigned to it then we assign an object to it.

Then we return Foo.instance so that we get the instance.

Now if we create 2 instances of Foo :

const foo = new Foo();
const bar = new Foo();

Then we can see if they’re the same instance by writing:

console.log(foo === bar);

We should get true since they both reference the same instance.

Singletons are handy for creating objects that are used by multiple pieces of code to store data in a central place.

If we don’t want conflicts in how data is accessed, then we can create a singleton instance of a class or an object literal to make sure that there are no issues with conflicts.

Flyweight Pattern

The flyweight pattern is where we restrict object creation,

We create a set of small objects that each consume a smaller amount of resources.

These objects sit behind a flyweight object, which is used to interact with these objects,

Those objects also interact with our code.

This way, we get the benefits of a big object, while we interact with smaller objects that are more manageable.

For instance, we can implement it as follows:

class Student {
 //..
}

const studentIdentity = {
 getIdentity(){
   const student = new Student();
    //..
    return {
    //...
    }
  }
}

const studentScore = {
 getScore(){
   const student = new Student();
    //..
    return {
    //...
    }
  }
}

We have a Student class that represents a student’s data.

Then in the studentIdentity and studentScore objects, we have methods to get his or her identity or score respectively.

This way, we don’t have to put all the methods into the Student class.

Instead, we have methods outside of it that are smaller that we can use to deal with specific kinds of student data.

Conclusion

We can use the singleton pattern to create one-off objects.

We can either create class instances with only one instance or with object literals.

The flyweight pattern is where we create smaller objects to deal with big objects, thereby reducing complexity.

The chain of responsibility pattern lets us send data from one object to another in a series fashion.

Categories
JavaScript Design Patterns

JavaScript Design Patterns — Factory Pattern

Design patterns are the basis of any good software. JavaScript programs are no exception.

In this article, we’ll look at the factory and observer patterns.

Factory Pattern

The factory pattern is a design pattern that lets us create new objects and return them in a clean way.

For instance, we may want to return different kinds of things that are similar.

To make this easy for us, we can create a factory function that lets us return different kinds of objects with one factory function.

We can write:

class Fruit {
  //...
}

class Apple extends Fruit {
  //...
}

class Orange extends Fruit {
  //...
}

const fruitFactory = (type) => {
  if (type === 'apple') {
    return new Apple()
  } else if (type === 'orange') {
    return new Apple()
  }
}

We have the fruitFactory factory function that gets the type of an object as the argument and then return an object according to the type.

If we have 'apple' as the value of type , we return an Apple instance.

And if we type set to 'orange' , we return an Orange instance.

Now we can return different subclass instances of Fruit without writing them out explicitly.

If we make any changes to the class or add or remove them, then we can still use the same factory function and don’t break anything.

Some factory functions in JavaScript are in the standard library.

They include String for creating strings, Number for creating numbers from other entities, Boolean for convert variables to boolean, Array for creating arrays, and more.

Now we don’t have to worry about changes to the class structure messing up our code since we used a factory function to create our Fruit objects.

Observer

The observer pattern is where we have multiple objects that listen to the one observable object.

This way, the observable object, which is also called the subject, can notify the objects that subscribed to the observable object with data that are emitted.

For instance, we can write:

const observable = {
  observers: {},
  subscribe(obj) {
    const id = Object.keys(this.observers).length + 1;
    this.observers[id] = obj;
    return id;
  },

  notify(data) {
    for (const o of Object.keys(this.observers)) {
      this.observers[o].listen(data);
    }
  }
}

const observer = {
  listen(data) {
    console.log(data);
  }
}
observable.subscribe(observer);
observable.notify({
  a: 1
});

We have the observable object that lets observer objects, which have a listen method to listen to data, to subscribe to notifications from the observable object.

When we can observable.notify with something as we did above, the listen method of the observer objects is run.

This way, the communication is all done by communicating via the notify method and nowhere else.

No implementations are exposed and therefore no tight coupling.

As long as the methods do the same thing, we shouldn’t have to worry about breaking the observer objects that subscribe to the observable .

We can also add an unsubscribe method to observable which uses the returned id from subscribe to remove an observer object from the observers object.

For instance, we can write:

const observable = {
  observers: {},
  subscribe(obj) {
    const id = Object.keys(this.observers).length + 1;
    this.observers[id] = obj;
    return id;
  },

  notify(data) {
    for (const o of Object.keys(this.observers)) {
      this.observers[o].listen(data);
    }
  },

  unsubscribe(id) {
    delete this.observers[id];
  }
}

const observer = {
  listen(data) {
    console.log(data);
  }
}
const subscriberId = observable.subscribe(observer);
observable.notify({
  a: 1
});

observable.unsubscribe(subscriberId);

We added an unsubscribe method so that we can remove the observer object from the this.observers list.

Once we unsubscribed, we won’t get notifications any more with the unsubscribed observer if we call notify again.

Examples of the observer pattern are used in many places.

Another good thing about the observer pattern is that we don’t communicate anything through the classes.

Loose coupling should always be preferred to tight coupling.

We only communicate through one channel in the example above so the observer pattern is as loose as coupling can get.

They include message queues and event handlers for GUI events like mouse clicks and key presses.

Conclusion

We can use the observer pattern to decouple objects as much as possible.

All we do is receive events from one observable object that we want changes from.

The observable sends notifications to observer objects.

The factory pattern lets us create objects of similar types in one place by creating a factory function that lets us do that.

Categories
JavaScript Design Patterns

JavaScript Design Patterns — Strategy and Decorator Pattern

Design patterns are the basis of any good software. JavaScript programs are no exception.

In this article, we’ll look at the strategy and decorator design patterns.

Strategy Pattern

The strategy design pattern is a design pattern that lets us select from a family of algorithms during run time.

The algorithms should be substitutable with each other.

This pattern’s main idea is to let us create multiple algorithms for solving the same problem.

We have one object that does things one way and another that does things another way.

There may be more than one way to do the same thing.

For instance, we may have one function that calls a function depending on what we want to do.

We may write:

const smartStrategy = () => {
  //..
}

const smarterStrategy = () => {
  //..
}

const dumbStrategy = () => {
  //..
}

const doTask = () => {
  if (shouldBeSmart) {
    smartStrategy()
  } else if (shouldBeSmarter) {
    smarterStrategy()
  } else if (shouldBeDumb) {
    dumbStrategy()
  }
}

We have the doTask function that runs the functions that have the strategies that we want to use to accomplish a task.

This way, we can pick the algorithm that suits the task the most.

This can be from a user setting or it can be chosen automatically.

The whole idea is to define a family of algorithms, encapsulate each one, and make them interchangeable.

Making them interchangeable is important since we want them to accomplish the same results at the end.

The strategy pattern is good for separating volatile code from a part of the program so that the part that changes are separate from the stable code.

Also, using the strategy pattern, we don’t have to split the implementation code over several inherited classes as often.

Using this pattern reduces the chance of having many child classes for one parent class.

This is a simple pattern that let us choose different ways to do the same thing and reduce the risk of volatile code that break things by separating them out.

Algorithms are encapsulated by one interface that doesn’t change so that outside code can just use that instead of changing the inner workings of the code all the time.

Closed for Modification, Open for Extension

In the same vein, once we wrote some code, we shouldn’t be modifying them too often.

Instead, they should be open to extensions so that we can add capabilities to them later.

Changing code always introduces risks. The more changes we make, the higher the chance that we break things.

Therefore, we should just extend things as much as possible so that the existing code stays untouched.

Decorator Pattern

The decorator pattern is a pattern where we write wrapper code to let us extend the core code.

We just keep wrapping objects around objects that we want to use so that we keep extending the capabilities of the existing objects by defining new objects that have the capabilities of the existing object.

For instance, we can write:

const computer = {
  //...
  description() {
    //...
  }
};

const disk = {
  ///...
  computer,
  description() {
    //...
  }
}

const laptop = {
  ///...
  disk
}

The computer object has the core capabilities of a computer.

Then we extend the capabilities of disk with by putting the computer object inside the disk object.

Then we create a laptop object, that has the disk object, which has the computer object.

Now we created extensions computer , which are disk and laptop , without modifying the computer object itself.

That’s a safe way to add capabilities to computer without the risks of modifying computer directly.

Modifying things directly may break the thing itself or the things that use it.

Likewise, we can use the same pattern as an object but replacing them with classes.

For instance, we can write:

class Computer {
  //...
  description() {
    //...
  }
}

class Disk {
  constructor() {
    this.computer = new Computer();
  }
  //...

  description() {
    //...
  }
}

const laptop = {
  constructor() {
    this.disk = new Disk();
  }
  //...
}

We nest the Disk and Computer instances within Laptop so we can use them.

Conclusion

The strategy pattern lets us use different ways of solving the same problem by encapsulating them within one entity that can be invoked directly.

With this pattern, the strategies themselves are hidden from the outside.

With the decorator pattern, we can extend the capabilities of things by creating new things rather than changing things directly.

This reduces the chance of breaking things.

Categories
JavaScript Design Patterns

JavaScript Design Patterns — Composition, Inheritance, and Configuration

Design patterns are the basis of any good software. JavaScript programs are no exception.

In this article, we’ll look at the difference between composition and inheritance, and look at why we should make our program have some flexibility.

Composition Versus Inheritance

Composition is when our object contains other objects.

Inheritance is when we receive the parent object’s members in the current object.

They both have their place.

Composition is a has-a relationship while inheritance is a has-a relationship.

It’s very important to make distinctions between the 2 since they do different things.

In JavaScript, there’re various ways that 2 things are composed.

For instance, we can have functions that are nested in other functions.

We can write:

const foo = fn => {
  //...
  fn();
  //...
}

We have a foo function that calls fn .

Also, we can have nested objects. For example, we can write:

const obj = {
  foo: 1,
  bar: {
    baz: 2,
  }
}

We have an object as the value of bar .

We can also have functions as values of object properties.

Composition is used for holding functionality that’s needed by something.

Inheritance, on the other hand, is an is-a relationship.

This means a child object is also a parent object. It’s just that a child object may have additional things.

For instance, if we have:

class Animal {
  //...
}

class Cat extends Animal {
  //...
}

Then Cat is a subclass of Animal . Cat is an animal.

Cat shares all the members of Animal .

We can any methods ofAnimal and access any instance variables of Animal from a Cat instance.

For example, we can write:

class Animal {
  speak() {
    //...
  }
}
class Cat extends Animal {
  //...
}

The extends keyword indicates that Cat inherits the members of Animal .

Then if we create a Cat instance, we can write:

const cat = new Cat();

Then we can call speak by calling:

cat.speak();

Likewise, we can create an is-a relationship between 2 objects with the Object.create method:

const animal = {
  speak() {
    //...
  }
}

const cat = Object.create(animal);

Object.create takes a prototype object which will be the prototype of the child object.

So animal is the parent of cat in the example above.

Like with class instances, we can call speak by writing cat.speak() .

In both the class and object examples, the prototype of the object resides in the __proto__ property of the object.

Inheritance is good for creating multiple classes or objects that share things from the parent class or object respectively.

The general principle is that if we have volatile code, then we should use more has-a relationships rather than an is-a relationship since we assume that shared code between a parent and different children may change.

If we change shared code between parents and children, then we break all the children as well.

Creating Algorithms

Once we decided on the patterns to use, then we’ve to devise our algorithm for our program.

This should be easy once we created some basic designs. Implementation is all that’s needed in most cases.

We can use existing libraries to make our lives easier, which is what we should do in most cases.

If we have an is-a relationship between classes or objects, then we need to think about which pieces of code are shared and which ones are unique to classes or objects.

If we use a has-a model instead, then we can create algorithms in different places and use them as we wish to.

We should think about making our program configurable so that we don’t have to change code to have slightly different functionality.

This way, we make our lives easier since code change always has some risks.

To reduce that, we should make things that change frequently be configurable so that we don’t have to deal with them.

For instance, we can read settings from a file or database so an algorithm can be selected according to settings that are set in those places.

Conclusion

Our programs should have some flexibility so that we don’t have to change code for it to do different things.

Composition is when we have something in an object or class so we can use them as we wish.

Inheritance is when we have some shared code that’s used by other objects or classes.