DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Originally published at thewebdev.info

Basic JavaScript Design Patterns — Decorators, Facades, and Proxies

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/

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

The organize our code, we should use some basic design patterns. In the article, we’ll look at decorators, facades, and proxies.

Decorator

Decorators let us add functionality to an object on the fly.

JavaScript has decorators now so that we can use them to create our own decorator and mutate objects on the fly.

For instance, we can create one as follows:

const foo = () => {
  return (target, name, descriptor) => {
    descriptor.value = "foo";
    return descriptor;
  };
};

class Foo {
  @foo()
  bar() {
    return "baz";
  }
}

const f = new Foo();
console.log(f.bar);
Enter fullscreen mode Exit fullscreen mode

A decorator in JavaScript, it’s just a function that returns a function with the parameters target , name and descriptor .

descriptor is the property descriptor, so we can set the value , writable , and enumerable properties for it.

Our foo decorator transformed the value property of it to the string 'foo' .

So we get that Foo instance’s bar property becomes the string 'foo' instead of a method.

JavaScript decorators are still experimental syntax, so we need Babel to use it.

The @babel/plugin-proposal-decorators will let us use decorators in our JavaScript projects.

The function returned by the decorator function has 2 other parameters.

The target is the constructor or class that the decorator is in.

The name parameter is the string of the entity that it’s modifying.

We can use it to modify other entities in a class.

Facade

The facade is a simple pattern that lets us provide an alternative interface to an object.

We can use it to hide the complex implementation behind a facade.

This is good because we don’t want users of our code to know everything.

The facade is very simple and we don’t have to do much.

We can also use it to combine multiple operations that are called together into one.

For instance, if we have multiple methods that are called together, then they can be put together into one method.

We can implement the facade pattern as follows:

const adder = {
  add() {
    //...
  },
}

const subtractor = {
  subtract() {
    //...
  },
}
const facade = {
  calc() {
    adder.add();
    subtractor.subtract();
  }
}
Enter fullscreen mode Exit fullscreen mode

We created a facade object that combines the add and subtract methods.

The facade pattern is used to create an interface in front of multiple objects.

Also, we can use it to handle the intricacies of browsers.

Some browsers may not have the preventDefault or stopPropgation methods, so we may want to check that they exist before calling them:

const facade = {
  stop(e) {
    if (typeof e.preventDefault === "function") {
      e.preventDefault();
    }
    if (typeof e.stopPropagation === "function") {
      e.stopPropagation();
    }
    //...
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we call always call stop and don’t have to worry about their existence since we check that they exist before calling them.

Proxy

The proxy pattern is where one object acts as an interface to another object.

The difference between proxy and facade patterns are that we can proxies create interfaces for one object.

But facades can be interfaces for anything.

We can create a proxy as follows:

const obj = {
  foo() {
    //...
  },

  bar() {
    //...
  },
}

const proxy = {
  call() {
    obj.foo();
    obj.bar();
  }
}
Enter fullscreen mode Exit fullscreen mode

We use the proxy to call both foo and bar in obj .

This pattern is good for lazy initialization.

This is where we only initialize something when it’s used.

Instead of always running the initialization code, we run the initialization code only when needed.

Proxy As a Cache

Proxies can act as a cache. We can check the cache for the results before returning the result.

If we have it, then we return the result from the cache.

Otherwise, we generate the result, put it in the cache, and then return it.

For instance, we can write:

const obj = {
  add() {
    //...
  }
}

const proxy = {
  cache: {},
  add(result) {
    if (!cache[result]) {
      cache[result] = obj.add();
      //...
    }
    return cache[result];
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we can do expensive operations only when needed instead of always doing it.

Conclusion

The decorator pattern can be used to change entities on the fly.

JavaScript has experimental decorators that let us do that.

Facades and proxies can both be used to hide the implementations of objects.

Proxies if for hiding one object, and facades can be used to hide more complex implementations.

Top comments (0)