Structural Patterns in ES6+ using Game of Thrones
Structural design patterns ease the design of large structure of relationships between objects while keeping these structures flexible and efficient. Let's find them out, using some references from Game Of Thrones.

In software engineering, a Design Pattern describes an established solution to the most commonly encountered problems in software design. It represents the best practices evolved over a long period through trial and error by experienced software developers.

In this article we will talk about the Structural Design Patterns. Structural Patterns are design patterns that ease the design by identifying a simple way to realize relationships among entities.

The most common Structural Patterns are:

  • Adapter
  • Decorator
  • Facade
  • Flyweight
  • Proxy

#Adapter Pattern

The Adapter Pattern is a structural pattern where the interface of one class is translated into another. This pattern lets classes work together that could not otherwise because of incompatible interfaces.

Let's pretend that the Targaryen house wants to go to war with all its strength, both the Unsullied and the Dragons, but Daenerys wants to find a unique way to ask both of them to kill someone.

According to the "Adapter" pattern, we will need:

  • An Unsullied classis
  • A Dragon class
  • An Adapter class to transform the burn method of thedragon to the common kill method
class Unsullied {
    constructor(name) {
        this.name = name;
        this.kill = this.kill.bind(this);
    }

    kill() {
        console.log(`Unsellied ${this.name} kill`)
    }
}

class Dragon {
    constructor(name) {
        this.name = name;
        this.burn = this.burn.bind(this);
    }

    burn() {
        console.log(`Dragon ${this.name} burn`)
    }
}

class DragonAdapter extends Dragon {
    kill() {
        this.burn()
    }
}

(() => {
    const Army = [
        new DragonAdapter('Rhaegal'),
        new Unsullied('Grey worm'),
        new DragonAdapter('Drogon')
    ];
    Army.forEach(soldier => soldier.kill());
})();

#When to use the Adapter Pattern?

  • When new components and modules need to be integrated to work with existing ones in an application or when part of code is refactored with improved interface but still needs to work with old parts.
  • When you find an external library that manages corner cases and you want to integrate it with your code

#Decorator Pattern

The decorator pattern is a structural design pattern that focuses on the ability to add behaviour or functionalities to existing classes dynamically. It is another viable alternative to sub-classing.

Let's pretend we want to implement a way to check that everything drunk by King Joffrey isn't poisoned

According to the "Decorator" pattern, we will need:

  • The King class
  • The King Joffrey instance
  • The Drink class
  • The Poisoned Drink class
  • The isNotPoisoned function to save the life of our king
function isNotPoisoned(t, n, descriptor) {
    const original = descriptor.value;
    if (typeof original === 'function') {
        descriptor.value = function (...args) {
            const [drink] = args;
            if (drink.constructor.name === 'PoisonedDrink') throw new Error('Someone want to kill the king');
            return original.apply(this, args);
        }
    }
    return descriptor;
}

class PoisonedDrink {
    constructor(name) {
        this.name = name
    }
}

class Drink {
    constructor(name) {
        this.name = name
    }
}

class King {
    constructor(name) {
        this.name = name
    }

    @isNotPoisoned
    drink(drink) {
        console.log(`the king drank ${drink}`)
    }
}


(() => {
    const joffrey = new King('Joffrey Baratheon');
    const normalDrink = new Drink('water');
    const poisonedDrink = new Drink('poisoned water');
    joffrey.drink(normalDrink);
    joffrey.drink(poisonedDrink);
})();

#When to use the Decorator Pattern?

  • When we want to enrich, with the same function, a large number of classes or methods that have different contexts
  • When we want to enrich previously created classes and we don't have time to do a complete refactoring

Ps. Be careful with the implementation of this decorator, we already know how it will end in case of bug

#Façade Pattern

The Facade pattern is a structural design pattern that is widely used in the JavaScript libraries. It is used to provide a unified and simpler interface for ease of use that shields away from the complexities of its consisting subsystems or subclasses.

Let's pretend we want to manage our army optimally to win the "battle of the bastards". Our armies instances provides us all the methods to move the infantry, the soldiers on foot and our magnificent giant. But we are forced to call these methods separately which makes us very verbose and therefore waste a lot of time. How can we simplify the management of our armies?

According to the "Facade" pattern, we will need:

  • The Armies instances
  • The Façade Army Class
class Horse {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Infatry ${this.name} attack`);
    }
}

class Soldier {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Soldier ${this.name} attack`);
    }
}

class Giant {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Giant ${this.name} attack`);
    }
}

class ArmyFacade {
    constructor() {
        this.army = [];
        (new Array(10)).fill().forEach((_, i) => this.army.push(new Horse(i + 1)));
        (new Array(10)).fill().forEach((_, i) => this.army.push(new Soldier(i + 1)));
        (new Array(1)).fill().forEach((_, i) => this.army.push(new Giant(i + 1)));
        this.getByType = this.getByType.bind(this);
    }
    getByType(type, occurrency) {
        return this.army.filter(el => {
            return el.constructor.name === type && occurrency-- > 0
        });
    }
    attack(armyInfo = {}) {
        const keys = Object.keys(armyInfo);
        let subAramy = []
        keys.forEach(soldier => {
            switch (soldier) {
                case "horse": {
                    subAramy = [...subAramy, ...this.getByType('Horse', armyInfo.horse)]
                    break;
                }
                case "soldier": {
                    subAramy = [...subAramy, ...this.getByType('Soldier', armyInfo.soldier)]
                    break;
                }
                case "giant": {
                    subAramy = [...subAramy, ...this.getByType('Giant', armyInfo.giant)]
                    break;
                }
            }
        });
        subAramy.forEach(soldier => soldier.attack());
    }
}

(() => {
    const army = new ArmyFacade();
    army.attack({
        horse: 3,
        soldier: 5,
        giant: 1
    })
})();

#When to use the Façade Pattern?

Whenever we realize that we can transform a series of lines of code, perhaps even repeated several times in the code, an abstract function that simplifies its use gives us the possibility to fix errors faster

#Flyweight Pattern

The Flyweight pattern is a structural design pattern focused on efficient data sharing through fine-grained objects. It is used for efficiency and memory conservation purposes.

Let's pretend we want to manage our army of white walkers, to simplify let's say that our white walkers can only have 3 states:

  • alive
  • dead
  • resurrected (again)

According to the "Flyweight" pattern, we will need:

  • The White Walker class
  • The White Walker Flyweight class
  • A Client that want spawn a white walker
class WhiteWalker {
    constructor({
        sprite,
        someOtherBigInformation
    }) {
        this.sprite = sprite;
        this.someOtherBigInformation = someOtherBigInformation;
        this.state = 'alive';
        this.resurrect = this.resurrect.bind(this);
        this.kill = this.kill.bind(this);
        this.getState = this.getState.bind(this);
    }
    kill() {
        this.state = 'dead'
    }
    resurrect() {
        this.state = 'resurrected'
    }
    getState() {
        return this.state;
    }
}

const whiteWalker = new WhiteWalker({
    sprite: Date.now()
});

class WhiteWalkerFlyweight {
    constructor(position, name) {
        this.position = position;
        this.name = name;
        this.whiteWalker = whiteWalker;
    }
    getInformation() {
        console.log(`The White Walker ${this.name} whit sprite ${this.whiteWalker.sprite} is ${this.whiteWalker.state}`);
    }
    getFatherInstance() {
        return this.whiteWalker;
    }
}


(() => {
    const myArmy = [];
    for (let i = 0; i < 10; i++) {
        myArmy.push(new WhiteWalkerFlyweight({
            x: Math.floor(Math.random() * 200),
            y: Math.floor(Math.random() * 200)
        }, i + 1));
    }
    myArmy.forEach(soldier => soldier.getInformation());
    console.log("KILL ALL");
    const [onOfWhiteWalker] = myArmy;
    onOfWhiteWalker.getFatherInstance().kill();
    myArmy.forEach(soldier => soldier.getInformation());
    console.log("RESURRECT ALL");
    onOfWhiteWalker.getFatherInstance().resurrect();
    myArmy.forEach(soldier => soldier.getInformation());
})();

#When to use the Flyweight Pattern?

  • When we want to avoid generating a large number of objects
  • When we want to create objects that require a lot of memory
  • When we want to avoid creating objects that require a lot of computational effort
  • When we have limited resources available: calculation, memory, space etc.

Nobody wants their RAM to be under siege from the white walker's army

Structural Patterns in ES6+ using Game of Thrones

#Proxy Pattern

The proxy pattern is a structural design pattern that behaves exactly as its name suggests. It acts as a surrogate or placeholder for another object to control access to it.

Let's pretend that Queen Cersei has issued a new amendment that prohibits the recruitment of more than 100 new soldiers without her explicit consent. How can we keep the enrollment process under control?

According to the "Proxy" pattern, we will need:

  • The Soldier class
  • The ArmyProxy class to keep the enrollment process under control
  • The Cercei instances to receive the consent
class Soldier {
    constructor(name) {
        this.name = name
    }
    attack() {
        console.log(`Soldier ${this.name} attack`);
    }
}

class Queen {
    constructor(name) {
        this.name = name
    }
    getConsent(casualNumber) {
        return casualNumber % 2 === 0
    }
}

class ArmyProxy {
    constructor() {
        this.army = [];
        this.available = 0
        this.queen = new Queen('Cercei');
        this.getQueenConsent = this.getQueenConsent.bind(this);
    }

    getQueenConsent() {
        return this.queen.getConsent(Math.floor(Math.random() * 200));
    }

    enrollSoldier() {
        if (!this.available) {
            const consent = this.getQueenConsent();
            if (!consent) {
                console.error(`The queen ${this.queen.name} deny the consent`);
                return
            }
            this.available = 100;
        }

        this.army.push(new Soldier(this.army.length));
        this.available--;
    }
}


(() => {
    const myArmy = new ArmyProxy();
    for (let i = 0; i < 1000; i++) {
        myArmy.enrollSoldier();
    }
    console.log(`I have ${myArmy.army.length} soldier`);
})();

When to use the Proxy Pattern?

  • When the object you want to use is located remotely and therefore we want to keep the logic encapsulated in a proxy so as not to affect the client
  • When you want to provide an approximate answer while waiting for the real result of the execution, which may take a long time, to be generated
  • When you want to control the access or creation of an object without wanting or being able to touch the logic of the object itself

#Conclusion

If you like this artcile and want to comment or check the code you can find these and all the other design patterns on this github repo:

thecreazy/got-javascript-patterns