Skip to content

Commit 08a2e69

Browse files
author
Badacadabra
committed
Add Decorator (ES5 + ES6 + CoffeeScript)
1 parent 99f2a2b commit 08a2e69

File tree

6 files changed

+229
-0
lines changed

6 files changed

+229
-0
lines changed
2 KB
Binary file not shown.
15.4 KB
Loading
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Synopsis
2+
3+
I am hungry and I want to eat pizza. I take my car and go to the nearest pizzeria. After a couple of minutes looking at the menu, I do not find what I want. So I finally ask for a custom pizza that will be prepared on the fly. I am not quite sure of the ingredients I want, but let's try to make a tasty pizza!
4+
5+
# Problem
6+
7+
A custom pizza could be anything. Ingredients will be added one by one to produce something unique, depending on my choices.
8+
Here a pizza is an object that is built at runtime, which means that we cannot have an exhaustive constructor with all possible ingredients as parameters.
9+
10+
# Solution
11+
12+
The idea is to "augment" a basic product (such as a Margherita) with some extras (Bacon, Peppers, ...) using the Decorator design pattern.
13+
To make this working, we need:
14+
15+
* An abstract representation of a Pizza (abstract class or interface)
16+
* A concrete implementation of this abstraction (like a simple pizza which will be decorated with ingredients)
17+
* An abstract representation of a custom pizza (abstract class which is actually the abstract decorator)
18+
* Concrete decorators (ingredients) that will be used to add new flavors to a more basic pizza
19+
20+
N.B. JavaScript is a very flexible language. Whenever we need to build something step by step or add new functionalities dynamically to an object, we can do it more easily using the idiomatic mechanisms of JavaScript (which is not a class-based language). Class-style programming is a matter of taste here and may be useful when we need more encapsulation. However, the best solution is often the simplest one...
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"use strict"
2+
3+
# ==============================
4+
# ABSTRACT PIZZA
5+
# ==============================
6+
7+
class Pizza
8+
constructor: ->
9+
throw new Error "You cannot instantiate an abstract class!" if @constructor is Pizza
10+
11+
ingredients: ->
12+
throw new Error "You cannot call an abstract method!"
13+
14+
# ==============================
15+
# CONCRETE PIZZA
16+
# ==============================
17+
18+
class Margherita extends Pizza
19+
ingredients: ->
20+
"- Tomatoes\n- Mozzarella\n"
21+
22+
# ==============================
23+
# PIZZA DECORATOR
24+
# ==============================
25+
26+
class CustomPizza extends Pizza
27+
constructor: (pizza) ->
28+
throw new Error "You cannot instantiate an abstract class!" if @constructor is CustomPizza
29+
@_pizza = pizza
30+
31+
ingredients: ->
32+
@_pizza.ingredients()
33+
34+
# ==============================
35+
# PIZZA INGREDIENTS
36+
# ==============================
37+
38+
class Bacon extends CustomPizza
39+
ingredients: ->
40+
"#{@_pizza.ingredients()}- Bacon\n"
41+
42+
class Peppers extends CustomPizza
43+
ingredients: ->
44+
"#{@_pizza.ingredients()}- Peppers\n"
45+
46+
# ==============================
47+
# CLIENT CODE
48+
# ==============================
49+
50+
console.log new Peppers(new Bacon(new Margherita())).ingredients()
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use strict';
2+
3+
// ==============================
4+
// ABSTRACT PIZZA
5+
// ==============================
6+
7+
var Pizza = (function () {
8+
function Pizza() {
9+
if (this.constructor === Pizza) {
10+
throw new Error("You cannot instantiate an abstract class!");
11+
}
12+
}
13+
14+
Pizza.prototype.ingredients = function () {
15+
throw new Error("You cannot call an abstract method!");
16+
};
17+
18+
return Pizza;
19+
})();
20+
21+
// ==============================
22+
// CONCRETE PIZZA
23+
// ==============================
24+
25+
var Margherita = (function () {
26+
function Margherita() {}
27+
Margherita.prototype = Object.create(Pizza.prototype);
28+
Margherita.prototype.constructor = Margherita;
29+
30+
Margherita.prototype.ingredients = function () {
31+
return "- Tomatoes\n- Mozzarella\n";
32+
};
33+
34+
return Margherita;
35+
})();
36+
37+
// ==============================
38+
// ABSTRACT DECORATOR
39+
// ==============================
40+
41+
var CustomPizza = (function () {
42+
function CustomPizza(pizza) {
43+
if (this.constructor === CustomPizza) {
44+
throw new Error("You cannot instantiate an abstract class!");
45+
}
46+
this._pizza = pizza;
47+
}
48+
CustomPizza.prototype = Object.create(Pizza.prototype);
49+
CustomPizza.prototype.constructor = CustomPizza;
50+
51+
CustomPizza.prototype.ingredients = function () {
52+
return this._pizza.ingredients();
53+
};
54+
55+
return CustomPizza;
56+
})();
57+
58+
// ==============================
59+
// CONCRETE DECORATORS (INGREDIENTS)
60+
// ==============================
61+
62+
var Bacon = (function () {
63+
function Bacon(pizza) {
64+
CustomPizza.call(this, pizza);
65+
}
66+
Bacon.prototype = Object.create(CustomPizza.prototype);
67+
Bacon.prototype.constructor = Bacon;
68+
69+
Bacon.prototype.ingredients = function () {
70+
return this._pizza.ingredients() + "- Bacon\n";
71+
};
72+
73+
return Bacon;
74+
})();
75+
76+
var Peppers = (function () {
77+
function Peppers(pizza) {
78+
CustomPizza.call(this, pizza);
79+
}
80+
Peppers.prototype = Object.create(CustomPizza.prototype);
81+
Peppers.prototype.constructor = Peppers;
82+
83+
Peppers.prototype.ingredients = function () {
84+
return this._pizza.ingredients() + "- Peppers\n";
85+
};
86+
87+
return Peppers;
88+
})();
89+
90+
// ==============================
91+
// CLIENT CODE
92+
// ==============================
93+
94+
console.log(new Peppers(new Bacon(new Margherita())).ingredients());
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// ==============================
2+
// ABSTRACT PIZZA
3+
// ==============================
4+
5+
class Pizza {
6+
constructor() {
7+
if (new.target !== undefined) {
8+
throw new Error("You cannot instantiate an abstract class!");
9+
}
10+
}
11+
12+
ingredients() {
13+
throw new Error("You cannot call an abstract method!");
14+
}
15+
}
16+
17+
// ==============================
18+
// CONCRETE PIZZA
19+
// ==============================
20+
21+
class Margherita extends Pizza {
22+
ingredients() {
23+
return "- Tomatoes\n- Mozzarella\n";
24+
}
25+
}
26+
27+
// ==============================
28+
// PIZZA DECORATOR
29+
// ==============================
30+
31+
class CustomPizza extends Pizza {
32+
constructor(pizza) {
33+
super();
34+
if (new.target !== undefined) {
35+
throw new Error("You cannot instantiate an abstract class!");
36+
}
37+
this._pizza = pizza;
38+
}
39+
40+
ingredients() {
41+
return this._pizza.ingredients();
42+
}
43+
}
44+
45+
// ==============================
46+
// PIZZA INGREDIENTS
47+
// ==============================
48+
49+
class Bacon extends CustomPizza {
50+
ingredients() {
51+
return this._pizza.ingredients() + "- Bacon\n";
52+
}
53+
}
54+
55+
class Peppers extends CustomPizza {
56+
ingredients() {
57+
return this._pizza.ingredients() + "- Peppers\n";
58+
}
59+
}
60+
61+
// ==============================
62+
// CLIENT CODE
63+
// ==============================
64+
65+
console.log(new Peppers(new Bacon(new Margherita())).ingredients());

0 commit comments

Comments
 (0)