DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Originally published at thewebdev.info

JavaScript Design Patterns — Factory Pattern

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

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()
  }
}
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)