Skip to content

Commit a4ce851

Browse files
author
Badacadabra
committed
Add all structural patterns in TypeScript
1 parent 1c90c5f commit a4ce851

File tree

7 files changed

+406
-0
lines changed

7 files changed

+406
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// ==============================
2+
// ABSTRACT CONNECTIONS
3+
// ==============================
4+
5+
interface AnalogInterface {
6+
handleAnalogSignal(): string;
7+
}
8+
9+
interface DigitalInterface {
10+
handleDigitalSignal(): string;
11+
}
12+
13+
// ==============================
14+
// CONCRETE CONNECTIONS
15+
// ==============================
16+
17+
// VGA has its own interface which handles images only through an analog signal
18+
class VGA implements AnalogInterface {
19+
public handleAnalogSignal(): string {
20+
return "Interface: VGA\nData: images\nSignal: analog\n";
21+
}
22+
}
23+
24+
// But your computer uses HDMI as output and your projector uses VGA as input...
25+
// Here you need an adapter if you want to display images. The two interfaces are incompatible.
26+
class HDMIToVGAAdapter implements DigitalInterface {
27+
private vga: VGA = new VGA();
28+
29+
public handleDigitalSignal(): string {
30+
return this.vga.handleAnalogSignal();
31+
}
32+
}
33+
34+
// ==============================
35+
// CLIENT CODE
36+
// ==============================
37+
38+
let adapter: HDMIToVGAAdapter = new HDMIToVGAAdapter();
39+
40+
console.log(adapter.handleDigitalSignal()); // Your computer uses HDMI and your projector uses VGA
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// ==============================
2+
// ABSTRACTIONS
3+
// ==============================
4+
5+
abstract class Recipe {
6+
protected sauce: Sauce;
7+
8+
constructor(sauce: Sauce) {
9+
this.sauce = sauce;
10+
}
11+
12+
public abstract cook(): string;
13+
}
14+
15+
interface Sauce {
16+
ingredients(): string;
17+
}
18+
19+
// ==============================
20+
// CONCRETE RECIPES
21+
// ==============================
22+
23+
class Pasta extends Recipe {
24+
public cook(): string {
25+
return "Pasta with " + this.sauce.ingredients();
26+
}
27+
}
28+
29+
class Risotto extends Recipe {
30+
public cook(): string {
31+
return "Risotto with " + this.sauce.ingredients();
32+
}
33+
}
34+
35+
// ==============================
36+
// CONCRETE SAUCES
37+
// ==============================
38+
39+
class Pesto implements Sauce {
40+
public ingredients(): string {
41+
return "Pesto (basil, garlic, oil, grated cheese, pine nuts)";
42+
}
43+
}
44+
45+
class Carbonara implements Sauce {
46+
public ingredients(): string {
47+
return "Carbonara (eggs, bacon, black pepper, grated cheese)";
48+
}
49+
}
50+
51+
// ==============================
52+
// CLIENT CODE
53+
// ==============================
54+
55+
// Sauces
56+
let pesto: Sauce = new Pesto(),
57+
carbonara: Sauce = new Carbonara();
58+
59+
// Recipes
60+
let pasta: Recipe = new Pasta(pesto),
61+
risotto: Recipe = new Risotto(carbonara);
62+
63+
console.log(pasta.cook());
64+
console.log(risotto.cook());
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// ==============================
2+
// ABSTRACT COMPONENT
3+
// ==============================
4+
5+
interface Toy {
6+
description(): string;
7+
}
8+
9+
// ==============================
10+
// SIMPLE (CONCRETE) COMPONENT
11+
// ==============================
12+
13+
// A ball does not contain anything
14+
class Ball implements Toy {
15+
public description(): string {
16+
return "There's a ball!\n";
17+
}
18+
}
19+
20+
// ==============================
21+
// COMPOSITE (CONCRETE) COMPONENT
22+
// ==============================
23+
24+
// A toy box is a toy entity which contains toys, including smaller toy boxes.
25+
class ToyBox implements Toy {
26+
private toys: Toy[] = [];
27+
28+
public description(): string {
29+
return "There's a toy box!\n";
30+
}
31+
32+
public add(toy: Toy): void {
33+
this.toys.push(toy);
34+
}
35+
36+
public inventory(): string {
37+
let inventory: string = "Let's open the toy box...\n";
38+
for (let toy of this.toys) {
39+
inventory += toy.description();
40+
if (toy instanceof ToyBox) {
41+
inventory += toy.inventory();
42+
}
43+
}
44+
return inventory;
45+
}
46+
}
47+
48+
// ==============================
49+
// CLIENT CODE
50+
// ==============================
51+
52+
// Here we organize our toys in an optimal way
53+
let ball1: Ball = new Ball(),
54+
ball2: Ball = new Ball(),
55+
ball3: Ball = new Ball(),
56+
bigToyBox: ToyBox = new ToyBox(),
57+
smallToyBox: ToyBox = new ToyBox();
58+
59+
smallToyBox.add(ball1);
60+
smallToyBox.add(ball2);
61+
bigToyBox.add(ball3);
62+
bigToyBox.add(smallToyBox);
63+
64+
// Now we open our big toy box...
65+
console.log(bigToyBox.inventory());
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// ==============================
2+
// ABSTRACT PIZZA
3+
// ==============================
4+
5+
interface Pizza {
6+
ingredients(): string;
7+
}
8+
9+
// ==============================
10+
// CONCRETE PIZZA
11+
// ==============================
12+
13+
class Margherita implements Pizza {
14+
public ingredients(): string {
15+
return "- Tomatoes\n- Mozzarella\n";
16+
}
17+
}
18+
19+
// ==============================
20+
// PIZZA DECORATOR
21+
// ==============================
22+
23+
abstract class CustomPizza implements Pizza {
24+
protected pizza: Pizza;
25+
26+
constructor(pizza: Pizza) {
27+
this.pizza = pizza;
28+
}
29+
30+
public ingredients(): string {
31+
return this.pizza.ingredients();
32+
}
33+
}
34+
35+
// ==============================
36+
// PIZZA INGREDIENTS
37+
// ==============================
38+
39+
class Bacon extends CustomPizza {
40+
public ingredients(): string {
41+
return this.pizza.ingredients() + "- Bacon\n";
42+
}
43+
}
44+
45+
class Peppers extends CustomPizza {
46+
public ingredients(): string {
47+
return this.pizza.ingredients() + "- Peppers\n";
48+
}
49+
}
50+
51+
// ==============================
52+
// CLIENT CODE
53+
// ==============================
54+
55+
console.log(new Peppers(new Bacon(new Margherita())).ingredients());
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// ==============================
2+
// ABSTRACT ANIMAL
3+
// ==============================
4+
5+
interface Animal {
6+
eat(): string;
7+
}
8+
9+
// ==============================
10+
// CONCRETE ANIMALS
11+
// ==============================
12+
13+
class Dog implements Animal {
14+
public eat(): string {
15+
return "Dog: 'Meat, please!'\n";
16+
}
17+
}
18+
19+
class Rabbit implements Animal {
20+
public eat(): string {
21+
return "Rabbit: 'A carrot would be great!'\n";
22+
}
23+
}
24+
25+
// ==============================
26+
// THE FACADE
27+
// ==============================
28+
29+
class HowToFeedAnimals {
30+
private dog: Dog = new Dog();
31+
private rabbit: Rabbit = new Rabbit();
32+
33+
public feedAnimals(): string {
34+
return `${this.dog.eat()}${this.rabbit.eat()}`;
35+
}
36+
}
37+
38+
// ==============================
39+
// CLIENT CODE
40+
// ==============================
41+
42+
let facade: HowToFeedAnimals = new HowToFeedAnimals();
43+
44+
console.log(facade.feedAnimals());
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// ==============================
2+
// ABSTRACT GNU/LINUX DISTRO
3+
// ==============================
4+
5+
abstract class LinuxDistro {
6+
private name: string;
7+
8+
constructor(name: string) {
9+
this.name = name;
10+
}
11+
12+
public boot(): string {
13+
return `${this.name} is booting...`;
14+
}
15+
}
16+
17+
// ==============================
18+
// CONCRETE GNU/LINUX DISTROS
19+
// ==============================
20+
21+
// Let's say that each instance of a Linux distro will have the name of its constructor...
22+
23+
class Debian extends LinuxDistro {
24+
constructor() {
25+
super("Debian");
26+
}
27+
}
28+
29+
class RedHat extends LinuxDistro {
30+
constructor() {
31+
super("RedHat");
32+
}
33+
}
34+
35+
class Slackware extends LinuxDistro {
36+
constructor() {
37+
super("Slackware");
38+
}
39+
}
40+
41+
// ==============================
42+
// FACTORY OF GNU/LINUX DISTROS
43+
// ==============================
44+
45+
class LinuxDistrosFactory {
46+
public static readonly DEBIAN: number = 0;
47+
public static readonly REDHAT: number = 1;
48+
public static readonly SLACKWARE: number = 2;
49+
50+
public static activeDistros: Map<number, LinuxDistro> = new Map(); // Flyweights!
51+
52+
public static getLinuxDistro(id: number): LinuxDistro {
53+
// If the distro has never been instantiated, we will have to create a new object
54+
if (!this.activeDistros.has(id)) {
55+
switch (id) {
56+
case this.DEBIAN:
57+
this.activeDistros.set(id, new Debian());
58+
break;
59+
case this.REDHAT:
60+
this.activeDistros.set(id, new RedHat());
61+
break;
62+
case this.SLACKWARE:
63+
this.activeDistros.set(id, new Slackware());
64+
break;
65+
default:
66+
throw new Error("The Linux distribution you are looking for has not been found");
67+
}
68+
}
69+
// If the distro has already been instantiated, we return the initial instance
70+
return this.activeDistros.get(id);
71+
}
72+
}
73+
74+
// ==============================
75+
// CLIENT CODE
76+
// ==============================
77+
78+
// Creation of our objects through the factory
79+
let debian: Debian = LinuxDistrosFactory.getLinuxDistro(LinuxDistrosFactory.DEBIAN),
80+
debianAgain: Debian = LinuxDistrosFactory.getLinuxDistro(LinuxDistrosFactory.DEBIAN);
81+
82+
console.log(debian.boot()); // Debian is booting...
83+
console.log(debianAgain.boot()); // Debian is booting...
84+
console.log(debian === debianAgain); // true (the same instance has been reused)

0 commit comments

Comments
 (0)