Decorators everywhere!!

Summary: This is probably going to be a long read. I recently came across a problem on CodeWars.com which involved upgrading a marine game unit and it kind of gave me the thought for writing this. we can see decorators everywhere. Although this pattern appears in many large code bases and popular books, they give the example of mostly using them with Object oriented UI toolkits(gof) or File Streaming(Java I/O-Head first design pattern) etc. I have tried to compile a few examples for Javascript.

Feel free to jump to any of these sections

  1. Decorators in JS
  2. Decorators in ES6
  3. Decorators in ES7, Typescript and Angular 2.
  4. Decorators in Angular 1.

Note: If you are not familiar with the design pattern I would encourage you to read this. Design Pattern

https://en.wikipedia.org/wiki/Decorator_pattern

Brief Introduction

The Gof book calls this a structural pattern aka Wrapper. The Intent being

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. — Gof

Structure

How to use them in Javascript?

Decorating a Tea
Let’s illustrate the decorator pattern with an example: decorating a Tea. You start with the prepare() method.

 var tea = {};
/*note that we are intentionally keeping it lightweight and simple, so as to cater to a larger customer base like some don’t want sugar, others don’t want milk or want a black tea*/
 tea.prepare = function() {
console.log(“heating water, adding tea powder”);
};

Now let’s implement a getDecorator() method which will be used to add extra decorators. The decorators will be implemented as constructor functions, and they’ll all inherit from the base tree object.

tea.getDecorator = function(deco){
tea[deco].prototype = this;
return new tea[deco];
};

Now let’s create the first decorator, Milk(). The Milk objects also provide a decorate() method, but they make sure they call their parent’s decorate() first.


tea.Milk = function() {
this.prepare = function() {
this.Milk.prototype.prepare();
console.log(‘Pouring some milk’);
}
}

//Similarly, adding a Mint() and Masala() decorators:
tea.Mint = function() {
this.prepare = function() {
this.Mint.prototype.prepare();
console.log(‘Adding mint leaves’);
}
}
tea.Masala = function() {
this.prepare = function() {
this.Masala.prototype.prepare();
console.log(‘Adding masala’);
}
};
 tea = tea.getDecorator(‘Milk’);
tea = tea.getDecorator(‘Masala’);
tea = tea.getDecorator(‘Mint’);

Finally, running the prepare() method: tea.prepare();
logs the following as expected
“heating water, adding tea powder”
‘Pouring some milk’
‘Adding masala’
‘Adding mint leaves’

How to use in ES6?

It is much easier to implement the design pattern in ES6 since we are not using any other patterns to do stuff like inheritance.
Lets look at another example, which is upgrading your weapons or basic units in game development.

Following Code taken from a CodeWars problem. Marine gets a WeaponUpgrade, it increases the damage by 1, and if it is an ArmorUpgrade then increase the armor by 1.

You have 3 classes:

Marine: has a damage and an armor properties
MarineWeaponUpgrade and MarineArmorUpgrade: upgrades to apply on marine. Accepts a Marine in the constructor and has the same properties as the Marine

class Marine {
constructor(_damage, _armor) {
this.damageCount = _damage || 0;
this.armorCount = _armor || 0;
}

get damage() { return this.damageCount; }
set damage(value) { this.damageCount = value; }
  get armor() { return this.armorCount; }
set armor(value) { this.armorCount = value;}
}
class MarineWeaponUpgrade {
constructor(decoratedMarine) {
this.decoratedMarine = decoratedMarine;
}
  get damage() { return this.decoratedMarine.damage + 1; }
set damage(value) { this.decoratedMarine.damage = value; }
  get armor() { return this.decoratedMarine.armor; }
set armor(value) { this.decoratedMarine.armor = value; }
}
class MarineArmorUpgrade {
constructor(decoratedMarine) {
this.decoratedMarine = decoratedMarine;
}
  get damage() { return this.decoratedMarine.damage; }
set damage(value) { this.decoratedMarine.damage = value; }
  get armor() { return this.decoratedMarine.armor + 1; }
set armor(value) { this.decoratedMarine.armor = value; }
}
//tests
describe(‘Decorator’, () => {
it(‘Single upgrade’, () => {
let marine = new Marine(10, 1);
Test.assertEquals(new MarineWeaponUpgrade(marine).damage, 11);
Test.assertEquals(new MarineWeaponUpgrade(marine).damage, 11);
});
it(‘Double upgrade’, () => {
let marine = new Marine(15, 1);
marine = new MarineWeaponUpgrade(marine);
marine = new MarineWeaponUpgrade(marine);
Test.assertEquals(marine.damage, 17);
});
it(‘Triple upgrade’, () => {
let marine = new Marine(20, 1);
marine = new MarineWeaponUpgrade(marine);
marine = new MarineWeaponUpgrade(marine);
marine = new MarineWeaponUpgrade(marine);
Test.assertEquals(marine.damage, 23);
});
});

Decorators in the future of JS…

I like the fact that most languages are evolving to an extent where the common design patterns are not really patterns anymore, but language features.

Note: According to spec editor Brian Terlson, ES2016 or ES7 will only add Array.prototype.includes and the exponentiation operator (**). The decorator is currently in stage-Zero proposal

This is more like in far future, maybe in ES 2017 -18 we can see this. We can already see it in Typescript although, which means it is already being used by many people for Angular 2.

Lets look at how we can use it now

An ES2016 decorator is an expression which returns function and can take a target, name and property descriptor as arguments. You apply it by prefixing the decorator with an `@` character and placing this at the very top of what you are trying to decorate. Decorators can be defined for either a class or property.

Decorating a property
Let’s take a look at a basic Person class:

class  Person {
greet() { return '${this.name} says hi!';}
}

this gets attached to the Person.prototype roughly like this..

Object.defineProperty(Person.prototype, ‘greet’, {
 value: specifiedFunction,
 enumerable: false,
 configurable: true,
 writable: true
});

Now imagine we want to make our greet method not writable.

function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
//modifying above class by adding the decorator for method
class Person {
@readonly
greet() { return '${this.name} says hi!';}
}
//Now were able to make it readonly we can verify this as follows
var rahul = new Person();
rahul.greet = function () {
return 'hello!';
}
//Exception: Attempted to assign to readonly property

Decorating a class
we can simply decorate the above Person class with something like, knows ‘Kalaripayatu’

function martialArtist(target) {
target.isMartialArtist = true;
target.style = ‘Kalaripayatu’;
}
@martialArtist
class Person {
greet() { return ‘${this.name} says hi!’;}
}
console.log(Person.style); // Kalaripayatu

Decorators in Angular 1

In Angular we have a decorator method provided by Angular team. These are functions that allow a service, directive or filter to be modified prior to its usage.

we can an example below where we are extending the $log service where we have extended the original warn with the decorated warn.

I am going keep this simple and not give every flavor of how it can be used in Angular.

angular.module('myApp', [])

.config([ '$provide', function($provide) {

$provide.decorator('$log', [
'$delegate',
function $logDecorator($delegate) {

var originalWarn = $delegate.warn;
$delegate.warn = function decoratedWarn(msg) {
msg = 'Decorated Warn: ' + msg;
originalWarn.apply($delegate, arguments);
};

return $delegate;
}
]);
}]);

Few Links

https://docs.angularjs.org/guide/decorators

https://www.typescriptlang.org/docs/handbook/decorators.html