Skip to content

Commit 3a8049f

Browse files
committed
Chore: Refactor subject implementation
1 parent f6bbec5 commit 3a8049f

File tree

4 files changed

+49
-61
lines changed

4 files changed

+49
-61
lines changed

src/models/edge.ts

+9-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { INodeBase, INode } from './node';
22
import { GraphObjectState } from './state';
33
import { Color, IPosition, ICircle, getDistanceToLine } from '../common';
44
import { isArrayOfNumbers } from '../utils/type.utils';
5-
import { IObserver, ISubject } from '../utils/observer.utils';
5+
import { IObserver, ISubject, Subject } from '../utils/observer.utils';
66
import { copyProperties } from '../utils/object.utils';
77

88
const CURVED_CONTROL_POINT_OFFSET_MIN_SIZE = 4;
@@ -157,9 +157,11 @@ export class EdgeFactory {
157157
});
158158
newEdge.setState(edge.getState());
159159
newEdge.setStyle(edge.getStyle());
160-
edge.getListeners().forEach((listener) => {
161-
newEdge.addListener(listener);
162-
});
160+
const listeners = edge.getListeners();
161+
162+
for (let i = 0; i < listeners.length; i++) {
163+
newEdge.addListener(listeners[i]);
164+
}
163165

164166
return newEdge;
165167
}
@@ -169,7 +171,7 @@ export const isEdge = <N extends INodeBase, E extends IEdgeBase>(obj: any): obj
169171
return obj instanceof EdgeStraight || obj instanceof EdgeCurved || obj instanceof EdgeLoopback;
170172
};
171173

172-
abstract class Edge<N extends INodeBase, E extends IEdgeBase> implements IEdge<N, E> {
174+
abstract class Edge<N extends INodeBase, E extends IEdgeBase> extends Subject implements IEdge<N, E> {
173175
protected _data: E;
174176

175177
public readonly id: number;
@@ -181,10 +183,10 @@ abstract class Edge<N extends INodeBase, E extends IEdgeBase> implements IEdge<N
181183
protected _state = GraphObjectState.NONE;
182184
protected _position: IEdgePosition;
183185

184-
private readonly _listeners: IObserver[] = [];
185186
private _type: EdgeType = EdgeType.STRAIGHT;
186187

187188
constructor(data: IEdgeData<N, E>, settings?: IEdgeSettings) {
189+
super();
188190
this.id = data.data.id;
189191
this._data = data.data;
190192
this.offset = data.offset ?? 0;
@@ -197,7 +199,7 @@ abstract class Edge<N extends INodeBase, E extends IEdgeBase> implements IEdge<N
197199
this.endNode.addEdge(this);
198200

199201
if (settings && settings.listeners) {
200-
this._listeners = settings.listeners;
202+
this.listeners = settings.listeners;
201203
}
202204
}
203205

@@ -233,10 +235,6 @@ abstract class Edge<N extends INodeBase, E extends IEdgeBase> implements IEdge<N
233235
return this._data.end;
234236
}
235237

236-
getListeners(): IObserver[] {
237-
return [...this._listeners];
238-
}
239-
240238
hasStyle(): boolean {
241239
return this._style && Object.keys(this._style).length > 0;
242240
}
@@ -411,20 +409,6 @@ abstract class Edge<N extends INodeBase, E extends IEdgeBase> implements IEdge<N
411409
}
412410
this.notifyListeners();
413411
}
414-
415-
addListener(observer: IObserver): void {
416-
this._listeners.push(observer);
417-
}
418-
419-
removeListener(observer: IObserver): void {
420-
this._listeners.splice(this._listeners.indexOf(observer), 1);
421-
}
422-
423-
notifyListeners(): void {
424-
this._listeners.forEach((listener) => {
425-
listener.update();
426-
});
427-
}
428412
}
429413

430414
const getEdgeType = <N extends INodeBase, E extends IEdgeBase>(data: IEdgeData<N, E>): EdgeType => {

src/models/graph.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { IGraphStyle } from './style';
55
import { ImageHandler } from '../services/images';
66
import { getEdgeOffsets } from './topology';
77
import { IEntityState, EntityState } from '../utils/entity.utils';
8-
import { IObserver, ISubject } from '../utils/observer.utils';
8+
import { IObserver, ISubject, Subject } from '../utils/observer.utils';
99
import { copyProperties } from '../utils/object.utils';
1010

1111
export interface IGraphData<N extends INodeBase, E extends IEdgeBase> {
@@ -53,7 +53,7 @@ export interface IGraphSettings<N extends INodeBase, E extends IEdgeBase> {
5353
listeners?: IObserver[];
5454
}
5555

56-
export class Graph<N extends INodeBase, E extends IEdgeBase> implements IGraph<N, E> {
56+
export class Graph<N extends INodeBase, E extends IEdgeBase> extends Subject implements IGraph<N, E> {
5757
private _nodes: IEntityState<any, INode<N, E>> = new EntityState<any, INode<N, E>>({
5858
getId: (node) => node.getId(),
5959
sortBy: (node1, node2) => (node1.getStyle().zIndex ?? 0) - (node2.getStyle().zIndex ?? 0),
@@ -64,15 +64,15 @@ export class Graph<N extends INodeBase, E extends IEdgeBase> implements IGraph<N
6464
});
6565
private _defaultStyle?: Partial<IGraphStyle<N, E>>;
6666
private _settings: IGraphSettings<N, E>;
67-
private _listeners: IObserver[] = [];
6867

6968
constructor(data?: Partial<IGraphData<N, E>>, settings?: Partial<IGraphSettings<N, E>>) {
7069
// TODO(dlozic): How to use object assign here? If I add add and export a default const here, it needs N, E.
70+
super();
7171
this._settings = settings || {};
7272
const nodes = data?.nodes ?? [];
7373
const edges = data?.edges ?? [];
7474
if (settings && settings.listeners) {
75-
this._listeners = settings.listeners;
75+
this.listeners = settings.listeners;
7676
}
7777
this.setup({ nodes, edges });
7878
}
@@ -377,20 +377,6 @@ export class Graph<N extends INodeBase, E extends IEdgeBase> implements IGraph<N
377377
return nearestEdge;
378378
}
379379

380-
addListener(observer: IObserver): void {
381-
this._listeners.push(observer);
382-
}
383-
384-
removeListener(observer: IObserver): void {
385-
this._listeners.splice(this._listeners.indexOf(observer), 1);
386-
}
387-
388-
notifyListeners(): void {
389-
this._listeners.forEach((listener) => {
390-
listener.update();
391-
});
392-
}
393-
394380
update(): void {
395381
this.notifyListeners();
396382
}

src/models/node.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IEdge, IEdgeBase } from './edge';
22
import { Color, IPosition, IRectangle, isPointInRectangle } from '../common';
33
import { ImageHandler } from '../services/images';
44
import { GraphObjectState } from './state';
5-
import { IObserver, ISubject } from '../utils/observer.utils';
5+
import { IObserver, ISubject, Subject } from '../utils/observer.utils';
66
import { copyProperties } from '../utils/object.utils';
77

88
/**
@@ -141,25 +141,25 @@ export const isNode = <N extends INodeBase, E extends IEdgeBase>(obj: any): obj
141141
return obj instanceof Node;
142142
};
143143

144-
export class Node<N extends INodeBase, E extends IEdgeBase> implements INode<N, E> {
144+
export class Node<N extends INodeBase, E extends IEdgeBase> extends Subject implements INode<N, E> {
145145
protected readonly _id: number;
146146
protected _data: N;
147147
protected _position: INodePosition;
148148
protected _style: INodeStyle = {};
149149
protected _state = GraphObjectState.NONE;
150150

151-
private readonly _listeners: IObserver[] = [];
152151
private readonly _inEdgesById: { [id: number]: IEdge<N, E> } = {};
153152
private readonly _outEdgesById: { [id: number]: IEdge<N, E> } = {};
154153
private readonly _onLoadedImage?: () => void;
155154

156155
constructor(data: INodeData<N>, settings?: Partial<INodeSettings>) {
156+
super();
157157
this._id = data.data.id;
158158
this._data = data.data;
159159
this._position = { id: this._id };
160160
this._onLoadedImage = settings?.onLoadedImage;
161161
if (settings && settings.listeners) {
162-
this._listeners = settings.listeners;
162+
this.listeners = settings.listeners;
163163
}
164164
}
165165

@@ -425,20 +425,6 @@ export class Node<N extends INodeBase, E extends IEdgeBase> implements INode<N,
425425
});
426426
}
427427

428-
addListener(observer: IObserver): void {
429-
this._listeners.push(observer);
430-
}
431-
432-
removeListener(observer: IObserver): void {
433-
this._listeners.splice(this._listeners.indexOf(observer), 1);
434-
}
435-
436-
notifyListeners(): void {
437-
this._listeners.forEach((listener) => {
438-
listener.update();
439-
});
440-
}
441-
442428
setData(data: N): void;
443429
setData(callback: (node: INode<N, E>) => N): void;
444430
setData(arg: N | ((node: INode<N, E>) => N)) {

src/utils/observer.utils.ts

+32
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,41 @@ export interface IObserver {
33
}
44

55
export interface ISubject {
6+
listeners: IObserver[];
7+
68
addListener(observer: IObserver): void;
79

10+
getListeners(): IObserver[];
11+
812
removeListener(observer: IObserver): void;
913

1014
notifyListeners(): void;
1115
}
16+
17+
export class Subject implements ISubject {
18+
listeners: IObserver[];
19+
20+
constructor() {
21+
this.listeners = [];
22+
}
23+
24+
addListener(observer: IObserver): void {
25+
this.listeners.push(observer);
26+
}
27+
28+
getListeners(): IObserver[] {
29+
return [...this.listeners];
30+
}
31+
32+
removeListener(observer: IObserver): void {
33+
if (!this.listeners.includes(observer)) {
34+
this.listeners.push(observer);
35+
}
36+
}
37+
38+
notifyListeners(): void {
39+
for (let i = 0; i < this.listeners.length; i++) {
40+
this.listeners[i].update();
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)