1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
| // ++++++++++ Observer ++++++++++
const createObserver = <EventType>(): {
subscribe: (listener: listenerType<EventType>) => () => void; // take listener and return an unsubscribe function
publish: (event: EventType) => void;
} => {
let listeners: listenerType<EventType>[] = [];
return {
subscribe: (listener: listenerType<EventType>): (() => void) => {
listeners.push(listener);
return () => {
listeners = listeners.filter((l) => l !== listener);
};
},
publish: (event: EventType) => {
listeners.forEach((l) => l(event));
},
};
};
// +++++++++++++++++++++++++++++
interface BeforeAddValueEvent<T> {
newValue: T;
value: T;
}
interface AfterAddValueEvent<T> {
value: T;
}
class InMemoryDBWithObserver<T extends BaseRecord> extends InMemoryDataBase<T> {
public set(newValue: T): void {
this.BeforeAddValueObserver.publish({
newValue,
value: this.db[newValue.id],
});
this.db[newValue.id] = newValue;
this.AfterAddValueObserver.publish({
value: newValue,
});
}
public get(id: string): T | undefined {
return this.db[id];
}
// observer
private BeforeAddValueObserver = createObserver<BeforeAddValueEvent<T>>();
private AfterAddValueObserver = createObserver<AfterAddValueEvent<T>>();
onBeforeAddValue(
listener: listenerType<BeforeAddValueEvent<T>>
): () => void {
return this.BeforeAddValueObserver.subscribe(listener);
}
onAfterAddValue(listener: listenerType<AfterAddValueEvent<T>>): () => void {
return this.AfterAddValueObserver.subscribe(listener);
}
// visiter pattern
visit(visitor: (item: T) => void): void {
Object.values(this.db).forEach(visitor);
}
// strategy pattern
getBest(strategy: (item: T) => number): T {
let findRes: { max: number; res: T | null } = {
max: -Infinity,
res: null,
};
return Object.values(this.db).reduce((prev, cur) => {
let score = strategy(cur);
if (prev.max < score) return { max: score, res: cur };
return prev;
}, findRes).res;
}
}
const pokemonDB = new InMemoryDBWithObserver<Pokemon>();
pokemonDB.onBeforeAddValue((event) => {
console.log('Before add value');
console.log(event);
});
const unsubscribe = pokemonDB.onAfterAddValue((event) => {
console.log('After ADD A Value');
console.log(event);
});
pokemonDB.onAfterAddValue((event) => {
console.log('-----------');
});
pokemonDB.set({
id: 'Bulbasaur',
attack: 59,
defense: 10,
});
pokemonDB.set({
id: 'Bulbasaur',
attack: 20,
defense: 30,
});
unsubscribe();
pokemonDB.set({
id: 'Spinpsaur',
attack: 159,
defense: 110,
});
console.log('Visit pattern:');
pokemonDB.visit((item) => console.log(item));
console.log('strategy pattern:');
console.log(pokemonDB.getBest((item) => item.defense));
|