Skip to main content

Decorator Design Pattern

Structural: Decorator Pattern


Could you please explain the decorator design pattern?

View Answer:
Interview Response: Decorators are a structural JS design pattern that aims to promote code reuse. Like Mixins, we can consider them as another viable alternative to object sub-classing. This pattern enables behavior that gets dynamically added to an individual object without affecting the behavior of other objects in the same class. Decorators can increase functionality in a more flexible way than sub-classing.

Code Example #1: Decorating Objects with New Functionality

// ES2015+ Keywords/syntax used: class, constructor, const

// A vehicle constructor
class Vehicle {
constructor(vehicleType) {
// some sane defaults
this.vehicleType = vehicleType || 'car';
this.model = 'default';
this.license = '00000-000';
}
}

// Test instance for a basic vehicle
const testInstance = new Vehicle('car');
console.log(testInstance);

// Outputs:
// vehicle: car, model:default, license: 00000-000

// Lets create a new instance of vehicle, to be decorated
const truck = new Vehicle('truck');

// New functionality we're decorating vehicle with
truck.setModel = function (modelName) {
this.model = modelName;
};

truck.setColor = function (color) {
this.color = color;
};

// Test the value setters and value assignment works correctly
truck.setModel('CAT');
truck.setColor('blue');

console.log(truck);

// Outputs:
// vehicle:truck, model:CAT, color: blue

// Demonstrate "vehicle" is still unaltered
const secondInstance = new Vehicle('car');
console.log(secondInstance);

// Outputs:
// vehicle: car, model:default, license: 00000-000

Code Example #2: Decorating Objects with Multiple Decorators

// ES2015+ Keywords/syntax used: class, constructor, const, let, extends, super

// The constructor to decorate
class MacBook {
constructor() {
this.cost = 997;
this.screenSize = 11.6;
}
getCost() {
return this.cost;
}
getScreenSize() {
return this.screenSize;
}
}

// Decorator 1
class Memory extends MacBook {
constructor(macBook) {
super();
this.macBook = macBook;
}

getCost() {
return this.macBook.getCost() + 75;
}
}

// Decorator 2
class Engraving extends MacBook {
constructor(macBook) {
super();
this.macBook = macBook;
}

getCost() {
return this.macBook.getCost() + 200;
}
}

// Decorator 3
class Insurance extends MacBook {
constructor(macBook) {
super();
this.macBook = macBook;
}

getCost() {
return this.macBook.getCost() + 250;
}
}

// init main object
let mb = new MacBook();

// init decorators
mb = new Memory(mb);
mb = new Engraving(mb);
mb = new Insurance(mb);

// Outputs: 1522
console.log(mb.getCost());

// Outputs: 11.6
console.log(mb.getScreenSize());

Code Example #3:

The objects participating in this pattern are:

Client -- Example code: the run() function

  • maintains a reference to the decorated Component

Component -- In example code: User

  • object to which additional functionality gets added

Decorator -- In example code: DecoratedUser

  • wraps around -- Component by maintaining a reference to it
  • defines an interface that is compatible with the interface of the Component
  • implements the additional functionality (addedMembers in the diagram)
let User = function (name) {
this.name = name;

this.say = function () {
console.log('User: ' + this.name);
};
};

let DecoratedUser = function (user, street, city) {
this.user = user;
this.name = user.name; // ensures interface stays the same
this.street = street;
this.city = city;

this.say = function () {
console.log(
'Decorated User: ' + this.name + ', ' + this.street + ', ' + this.city
);
};
};

function run() {
let user = new User('Kelly');
user.say();

let decorated = new DecoratedUser(user, 'Broadway', 'New York');
decorated.say();
}

run();

/*

OUTPUT:

User: Kelly
Decorated User: Kelly, Broadway, New York

*/

The Decorator pattern belongs to which pattern family?

View Answer:
Interview Response: The Decorator pattern is part of the Structural design pattern family.

What are the Decorator Pattern's object participants?

View Answer:
Interview Response: The Client, Component, and Decorator are the object participants in the Decorator Pattern.

  • Client – The Client object participant references the decorated Component.
  • Component – The object to which additional functionality gets added is a Component.
  • Decorator – By keeping a reference to the Component, defining an interface that conforms to the Component's interface, and implementing the additional functionality, the Decorator acts as a wrapper around it. In an application, there can be more than one Decorator.


What are some of the advantages of employing the Decorator pattern?

View Answer:
Interview Response: Benefits of the Decorator Pattern include:

  • You can change the behavior of an object without creating a new subclass.
  • At runtime, you can add or remove responsibilities from an object.
  • Wrapping an object in multiple decorators allows you to combine several behaviors.
  • Single Responsibility Principle - The principle of single responsibility. A monolithic class that implements many possible behavior variants can get divided into several smaller classes.


What are some of the disadvantages of employing the Decorator pattern?

View Answer:
Interview Response: Drawbacks of the Decorator Pattern include:

  • It’s hard to remove a specific wrapper from the wrapper's stack.
  • It’s hard to implement a decorator in such a way that its behavior doesn’t depend on the order in the Decorator's stack.
  • The initial configuration code of layers might look pretty ugly.