Skip to content

Commit 15b41ba

Browse files
committed
feat(patterns): added abstract factory and factory method pattern
0 parents commit 15b41ba

File tree

11 files changed

+3247
-0
lines changed

11 files changed

+3247
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
/node_modules

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Design Patterns with typescript
2+
3+
## Extracted from books:
4+
* Typescript Design Patterns - Vilic Vane
5+
* Mergulho nos padrões de projeto - Alexander Shvets
6+
* Refactoring Guru - refactoring.guru
7+
8+
### Patterns
9+
10+
- Factory Method
11+
- Abstract Factory
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ConcreteFactory1, ConcreteFactory2, clientCode} from './index'
2+
3+
describe('abstract-factory', (): void => {
4+
it('should execute abstract factory', (): void => {
5+
/**
6+
* The client code can work with any concrete factory class.
7+
*/
8+
console.log('Client: Testing client code with the first factory type...');
9+
clientCode(new ConcreteFactory1());
10+
11+
console.log('');
12+
13+
console.log('Client: Testing the same client code with the second factory type...');
14+
clientCode(new ConcreteFactory2());
15+
})
16+
})

abstract-factory/index.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// https://refactoring.guru/pt-br/design-patterns/abstract-factory/typescript/example
2+
/**
3+
* The Abstract Factory interface declares a set of methods that return
4+
* different abstract products. These products are called a family and are
5+
* related by a high-level theme or concept. Products of one family are usually
6+
* able to collaborate among themselves. A family of products may have several
7+
* variants, but the products of one variant are incompatible with products of
8+
* another.
9+
*/
10+
interface AbstractFactory {
11+
createProductA(): AbstractProductA;
12+
13+
createProductB(): AbstractProductB;
14+
}
15+
16+
/**
17+
* Concrete Factories produce a family of products that belong to a single
18+
* variant. The factory guarantees that resulting products are compatible. Note
19+
* that signatures of the Concrete Factory's methods return an abstract product,
20+
* while inside the method a concrete product is instantiated.
21+
*/
22+
export class ConcreteFactory1 implements AbstractFactory {
23+
public createProductA(): AbstractProductA {
24+
return new ConcreteProductA1();
25+
}
26+
27+
public createProductB(): AbstractProductB {
28+
return new ConcreteProductB1();
29+
}
30+
}
31+
32+
/**
33+
* Each Concrete Factory has a corresponding product variant.
34+
*/
35+
export class ConcreteFactory2 implements AbstractFactory {
36+
public createProductA(): AbstractProductA {
37+
return new ConcreteProductA2();
38+
}
39+
40+
public createProductB(): AbstractProductB {
41+
return new ConcreteProductB2();
42+
}
43+
}
44+
45+
/**
46+
* Each distinct product of a product family should have a base interface. All
47+
* variants of the product must implement this interface.
48+
*/
49+
interface AbstractProductA {
50+
usefulFunctionA(): string;
51+
}
52+
53+
/**
54+
* These Concrete Products are created by corresponding Concrete Factories.
55+
*/
56+
class ConcreteProductA1 implements AbstractProductA {
57+
public usefulFunctionA(): string {
58+
return 'The result of the product A1.';
59+
}
60+
}
61+
62+
class ConcreteProductA2 implements AbstractProductA {
63+
public usefulFunctionA(): string {
64+
return 'The result of the product A2.';
65+
}
66+
}
67+
68+
/**
69+
* Here's the the base interface of another product. All products can interact
70+
* with each other, but proper interaction is possible only between products of
71+
* the same concrete variant.
72+
*/
73+
interface AbstractProductB {
74+
/**
75+
* Product B is able to do its own thing...
76+
*/
77+
usefulFunctionB(): string;
78+
79+
/**
80+
* ...but it also can collaborate with the ProductA.
81+
*
82+
* The Abstract Factory makes sure that all products it creates are of the
83+
* same variant and thus, compatible.
84+
*/
85+
anotherUsefulFunctionB(collaborator: AbstractProductA): string;
86+
}
87+
88+
/**
89+
* These Concrete Products are created by corresponding Concrete Factories.
90+
*/
91+
class ConcreteProductB1 implements AbstractProductB {
92+
93+
public usefulFunctionB(): string {
94+
return 'The result of the product B1.';
95+
}
96+
97+
/**
98+
* The variant, Product B1, is only able to work correctly with the variant,
99+
* Product A1. Nevertheless, it accepts any instance of AbstractProductA as
100+
* an argument.
101+
*/
102+
public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
103+
const result = collaborator.usefulFunctionA();
104+
return `The result of the B1 collaborating with the (${result})`;
105+
}
106+
}
107+
108+
class ConcreteProductB2 implements AbstractProductB {
109+
110+
public usefulFunctionB(): string {
111+
return 'The result of the product B2.';
112+
}
113+
114+
/**
115+
* The variant, Product B2, is only able to work correctly with the variant,
116+
* Product A2. Nevertheless, it accepts any instance of AbstractProductA as
117+
* an argument.
118+
*/
119+
public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
120+
const result = collaborator.usefulFunctionA();
121+
return `The result of the B2 collaborating with the (${result})`;
122+
}
123+
}
124+
125+
/**
126+
* The client code works with factories and products only through abstract
127+
* types: AbstractFactory and AbstractProduct. This lets you pass any factory or
128+
* product subclass to the client code without breaking it.
129+
*/
130+
export function clientCode(factory: AbstractFactory) {
131+
const productA = factory.createProductA();
132+
const productB = factory.createProductB();
133+
134+
console.log(productB.usefulFunctionB());
135+
console.log(productB.anotherUsefulFunctionB(productA));
136+
}
137+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { clientCode, ConcreteCreator1, ConcreteCreator2 } from "./index";
2+
import { clientDelivery, BikeCreator, TruckCreator } from './my-factory-method';
3+
4+
describe('factory-method', (): void => {
5+
it('should create a valid instance', (): void => {
6+
/**
7+
* The Application picks a creator's type depending on the configuration or
8+
* environment.
9+
*/
10+
console.log('App: Launched with the ConcreteCreator1.');
11+
clientCode(new ConcreteCreator1());
12+
console.log('');
13+
14+
console.log('App: Launched with the ConcreteCreator2.');
15+
clientCode(new ConcreteCreator2());
16+
})
17+
18+
it('should create car delivery', () => {
19+
clientDelivery(new BikeCreator()); // Delivery by bike
20+
clientDelivery(new TruckCreator()); // Delivery by truck
21+
})
22+
})

factory-method/index.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// https://refactoring.guru/pt-br/design-patterns/factory-method/typescript/example
2+
/**
3+
* The Creator class declares the factory method that is supposed to return an
4+
* object of a Product class. The Creator's subclasses usually provide the
5+
* implementation of this method.
6+
*/
7+
abstract class Creator {
8+
/**
9+
* Note that the Creator may also provide some default implementation of the
10+
* factory method.
11+
*/
12+
public abstract factoryMethod(): Product;
13+
14+
/**
15+
* Also note that, despite its name, the Creator's primary responsibility is
16+
* not creating products. Usually, it contains some core business logic that
17+
* relies on Product objects, returned by the factory method. Subclasses can
18+
* indirectly change that business logic by overriding the factory method
19+
* and returning a different type of product from it.
20+
*/
21+
public someOperation(): string {
22+
// Call the factory method to create a Product object.
23+
const product = this.factoryMethod();
24+
// Now, use the product.
25+
return `Creator: The same creator's code has just worked with ${product.operation()}`;
26+
}
27+
}
28+
29+
/**
30+
* Concrete Creators override the factory method in order to change the
31+
* resulting product's type.
32+
*/
33+
export class ConcreteCreator1 extends Creator {
34+
/**
35+
* Note that the signature of the method still uses the abstract product
36+
* type, even though the concrete product is actually returned from the
37+
* method. This way the Creator can stay independent of concrete product
38+
* classes.
39+
*/
40+
public factoryMethod(): Product {
41+
return new ConcreteProduct1();
42+
}
43+
}
44+
45+
export class ConcreteCreator2 extends Creator {
46+
public factoryMethod(): Product {
47+
return new ConcreteProduct2();
48+
}
49+
}
50+
51+
/**
52+
* The Product interface declares the operations that all concrete products must
53+
* implement.
54+
*/
55+
interface Product {
56+
operation(): string;
57+
}
58+
59+
/**
60+
* Concrete Products provide various implementations of the Product interface.
61+
*/
62+
class ConcreteProduct1 implements Product {
63+
public operation(): string {
64+
return '{Result of the ConcreteProduct1}';
65+
}
66+
}
67+
68+
class ConcreteProduct2 implements Product {
69+
public operation(): string {
70+
return '{Result of the ConcreteProduct2}';
71+
}
72+
}
73+
74+
/**
75+
* The client code works with an instance of a concrete creator, albeit through
76+
* its base interface. As long as the client keeps working with the creator via
77+
* the base interface, you can pass it any creator's subclass.
78+
*/
79+
export function clientCode(creator: Creator): void {
80+
// ...
81+
console.log('Client: I\'m not aware of the creator\'s class, but it still works.');
82+
console.log(creator.someOperation());
83+
// ...
84+
}
85+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
interface Delivery {
2+
operation(): void
3+
}
4+
5+
abstract class Factory {
6+
public abstract create(): Delivery;
7+
8+
public DoDelivery(): void {
9+
const vehicle = this.create();
10+
vehicle.operation();
11+
}
12+
}
13+
14+
export class TruckCreator extends Factory {
15+
16+
public create(): Delivery {
17+
return new Truck()
18+
}
19+
}
20+
21+
export class BikeCreator extends Factory {
22+
public create(): Delivery {
23+
return new Bike()
24+
}
25+
}
26+
27+
export class Bike implements Delivery {
28+
operation(): void {
29+
console.log('A item was delivered by bike')
30+
}
31+
}
32+
33+
export class Truck implements Delivery {
34+
operation(): void {
35+
console.log('A item was delivered by truck');
36+
}
37+
}
38+
39+
export const clientDelivery = (factory: Factory): void => {
40+
factory.DoDelivery();
41+
}

0 commit comments

Comments
 (0)