Skip to content

Commit 1c90c5f

Browse files
author
Badacadabra
committed
Add Bridge (ES5 + ES6 + CoffeeScript)
1 parent 5827beb commit 1c90c5f

File tree

6 files changed

+259
-0
lines changed

6 files changed

+259
-0
lines changed

doc/GoF/Structural/Bridge/Bridge.dia

2.15 KB
Binary file not shown.

doc/GoF/Structural/Bridge/Bridge.png

14.9 KB
Loading

doc/GoF/Structural/Bridge/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Synopsis
2+
3+
I do not know what to eat for dinner. In my kitchen, I think I have pasta and rice with some sauces, especially pesto and carbonara.
4+
5+
# Problem
6+
7+
Even in this very simple situation, they are already four recipes that could be great. I can eat pasta with pesto, pasta with carbonara, risotto with pesto, or risotto with carbonara.
8+
9+
To represent that with code, we may want to use inheritance, which is not really convenient. If we use inheritance, we will have a "class" for each possible recipe (PastaWithPesto, PastaWithCarbonara, RisottoWithPesto, RisottoWithCarbonara). But imagine that, a couple of minutes later, you also find potatoes and a tomato sauce in the kitcken. Do you really want to create new classes such as PotatoesWithCarbonara, PastaWithTomato and so on?
10+
11+
# Solution
12+
13+
Here we need to split the hierarchy of classes and prefer composition over inheritance. We will then use a Bridge between a simple recipe and a sauce. This means that we will be able to select the sauce independently. To do so, we may have:
14+
15+
* An abstract class (Recipe) with a reference to another abstraction, generally an interface (Sauce)
16+
* Concrete implementations of both abstractions
17+
18+
Of course, methods defined in the main abstract class delegate operations to the methods of the other abstraction.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ==============================
2+
# ABSTRACTIONS
3+
# ==============================
4+
5+
class Recipe
6+
constructor: (sauce) ->
7+
throw new Error "You cannot instantiate an abstract class!" if @constructor is Recipe
8+
@_sauce = sauce
9+
10+
cook: ->
11+
throw new Error "You cannot call an abstract class!"
12+
13+
class Sauce
14+
constructor: ->
15+
throw new Error "You cannot call an abstract method!" if @constructor is Sauce
16+
17+
ingredients: ->
18+
throw new Error "You cannot call an abstract method!"
19+
20+
# ==============================
21+
# CONCRETE RECIPES
22+
# ==============================
23+
24+
class Pasta extends Recipe
25+
cook: ->
26+
"Pasta with #{@_sauce.ingredients()}"
27+
28+
class Risotto extends Recipe
29+
cook: ->
30+
"Risotto with #{@_sauce.ingredients()}"
31+
32+
# ==============================
33+
# CONCRETE SAUCES
34+
# ==============================
35+
36+
class Pesto extends Sauce
37+
ingredients: ->
38+
"Pesto (basil, garlic, oil, grated cheese, pine nuts)"
39+
40+
class Carbonara extends Sauce
41+
ingredients: ->
42+
"Carbonara (eggs, bacon, black pepper, grated cheese)"
43+
44+
# ==============================
45+
# CLIENT CODE
46+
# ==============================
47+
48+
# Sauces
49+
pesto = new Pesto
50+
carbonara = new Carbonara
51+
52+
# Recipes
53+
pasta = new Pasta pesto
54+
risotto = new Risotto carbonara
55+
56+
console.log pasta.cook()
57+
console.log risotto.cook()
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use strict';
2+
3+
// ==============================
4+
// ABSTRACTIONS
5+
// ==============================
6+
7+
var Recipe = (function () {
8+
function Recipe(sauce) {
9+
if (this.constructor === Recipe) {
10+
throw new Error("You cannot instantiate an abstract class!");
11+
}
12+
this._sauce = sauce;
13+
}
14+
15+
Recipe.prototype.cook = function() {
16+
throw new Error("You cannot call an abstract method!");
17+
};
18+
19+
return Recipe;
20+
})();
21+
22+
var Sauce = (function () {
23+
function Sauce() {
24+
if (this.constructor === Sauce) {
25+
throw new Error("You cannot instantiate an abstract class!");
26+
}
27+
}
28+
29+
Sauce.prototype.ingredients = function() {
30+
throw new Error("You cannot call an abstract method!");
31+
};
32+
33+
return Sauce;
34+
})();
35+
36+
// ==============================
37+
// CONCRETE RECIPES
38+
// ==============================
39+
40+
var Pasta = (function () {
41+
function Pasta(sauce) {
42+
Recipe.call(this, sauce);
43+
}
44+
Pasta.prototype = Object.create(Recipe.prototype);
45+
Pasta.prototype.constructor = Pasta;
46+
47+
Pasta.prototype.cook = function () {
48+
return "Pasta with " + this._sauce.ingredients();
49+
};
50+
51+
return Pasta;
52+
})();
53+
54+
var Risotto = (function () {
55+
function Risotto(sauce) {
56+
Recipe.call(this, sauce);
57+
}
58+
Risotto.prototype = Object.create(Recipe.prototype);
59+
Risotto.prototype.constructor = Risotto;
60+
61+
Risotto.prototype.cook = function () {
62+
return "Risotto with " + this._sauce.ingredients();
63+
};
64+
65+
return Risotto;
66+
})();
67+
68+
// ==============================
69+
// CONCRETE SAUCES
70+
// ==============================
71+
72+
var Pesto = (function () {
73+
function Pesto() {}
74+
Pesto.prototype = Object.create(Sauce.prototype);
75+
Pesto.prototype.constructor = Pesto;
76+
77+
Pesto.prototype.ingredients = function () {
78+
return "Pesto (basil, garlic, oil, grated cheese, pine nuts)";
79+
};
80+
81+
return Pesto;
82+
})();
83+
84+
var Carbonara = (function () {
85+
function Carbonara() {}
86+
Carbonara.prototype = Object.create(Sauce.prototype);
87+
Carbonara.prototype.constructor = Carbonara;
88+
89+
Carbonara.prototype.ingredients = function () {
90+
return "Carbonara (eggs, bacon, black pepper, grated cheese)";
91+
};
92+
93+
return Carbonara;
94+
})();
95+
96+
// ==============================
97+
// CLIENT CODE
98+
// ==============================
99+
100+
// Sauces
101+
var pesto = new Pesto(),
102+
carbonara = new Carbonara();
103+
104+
// Recipes
105+
var pasta = new Pasta(pesto),
106+
risotto = new Risotto(carbonara);
107+
108+
console.log(pasta.cook());
109+
console.log(risotto.cook());
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// ==============================
2+
// ABSTRACTIONS
3+
// ==============================
4+
5+
class Recipe {
6+
constructor(sauce) {
7+
if (new.target !== undefined) {
8+
throw new Error("You cannot instantiate an abstract class!");
9+
}
10+
this._sauce = sauce;
11+
}
12+
13+
cook() {
14+
throw new Error("You cannot call an abstract class!");
15+
}
16+
}
17+
18+
class Sauce {
19+
constructor() {
20+
if (new.target !== undefined) {
21+
throw new Error("You cannot call an abstract method!");
22+
}
23+
}
24+
25+
ingredients() {
26+
throw new Error("You cannot call an abstract method!");
27+
}
28+
}
29+
30+
// ==============================
31+
// CONCRETE RECIPES
32+
// ==============================
33+
34+
class Pasta extends Recipe {
35+
cook() {
36+
return "Pasta with " + this._sauce.ingredients();
37+
}
38+
}
39+
40+
class Risotto extends Recipe {
41+
cook() {
42+
return "Risotto with " + this._sauce.ingredients();
43+
}
44+
}
45+
46+
// ==============================
47+
// CONCRETE SAUCES
48+
// ==============================
49+
50+
class Pesto extends Sauce {
51+
ingredients() {
52+
return "Pesto (basil, garlic, oil, grated cheese, pine nuts)";
53+
}
54+
}
55+
56+
class Carbonara extends Sauce {
57+
ingredients() {
58+
return "Carbonara (eggs, bacon, black pepper, grated cheese)";
59+
}
60+
}
61+
62+
// ==============================
63+
// CLIENT CODE
64+
// ==============================
65+
66+
// Sauces
67+
let pesto = new Pesto(),
68+
carbonara = new Carbonara();
69+
70+
// Recipes
71+
let pasta = new Pasta(pesto),
72+
risotto = new Risotto(carbonara);
73+
74+
console.log(pasta.cook());
75+
console.log(risotto.cook());

0 commit comments

Comments
 (0)