From 3a8049f3d89b89b950e69feda942340f19a35dc9 Mon Sep 17 00:00:00 2001 From: AlexIchenskiy Date: Wed, 28 Feb 2024 15:39:45 +0100 Subject: [PATCH] Chore: Refactor subject implementation --- src/models/edge.ts | 34 +++++++++------------------------- src/models/graph.ts | 22 ++++------------------ src/models/node.ts | 22 ++++------------------ src/utils/observer.utils.ts | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/models/edge.ts b/src/models/edge.ts index 0fc6890..4f3b813 100644 --- a/src/models/edge.ts +++ b/src/models/edge.ts @@ -2,7 +2,7 @@ import { INodeBase, INode } from './node'; import { GraphObjectState } from './state'; import { Color, IPosition, ICircle, getDistanceToLine } from '../common'; import { isArrayOfNumbers } from '../utils/type.utils'; -import { IObserver, ISubject } from '../utils/observer.utils'; +import { IObserver, ISubject, Subject } from '../utils/observer.utils'; import { copyProperties } from '../utils/object.utils'; const CURVED_CONTROL_POINT_OFFSET_MIN_SIZE = 4; @@ -157,9 +157,11 @@ export class EdgeFactory { }); newEdge.setState(edge.getState()); newEdge.setStyle(edge.getStyle()); - edge.getListeners().forEach((listener) => { - newEdge.addListener(listener); - }); + const listeners = edge.getListeners(); + + for (let i = 0; i < listeners.length; i++) { + newEdge.addListener(listeners[i]); + } return newEdge; } @@ -169,7 +171,7 @@ export const isEdge = (obj: any): obj return obj instanceof EdgeStraight || obj instanceof EdgeCurved || obj instanceof EdgeLoopback; }; -abstract class Edge implements IEdge { +abstract class Edge extends Subject implements IEdge { protected _data: E; public readonly id: number; @@ -181,10 +183,10 @@ abstract class Edge implements IEdge, settings?: IEdgeSettings) { + super(); this.id = data.data.id; this._data = data.data; this.offset = data.offset ?? 0; @@ -197,7 +199,7 @@ abstract class Edge implements IEdge implements IEdge 0; } @@ -411,20 +409,6 @@ abstract class Edge implements IEdge { - listener.update(); - }); - } } const getEdgeType = (data: IEdgeData): EdgeType => { diff --git a/src/models/graph.ts b/src/models/graph.ts index 0c0e4c0..553538a 100644 --- a/src/models/graph.ts +++ b/src/models/graph.ts @@ -5,7 +5,7 @@ import { IGraphStyle } from './style'; import { ImageHandler } from '../services/images'; import { getEdgeOffsets } from './topology'; import { IEntityState, EntityState } from '../utils/entity.utils'; -import { IObserver, ISubject } from '../utils/observer.utils'; +import { IObserver, ISubject, Subject } from '../utils/observer.utils'; import { copyProperties } from '../utils/object.utils'; export interface IGraphData { @@ -53,7 +53,7 @@ export interface IGraphSettings { listeners?: IObserver[]; } -export class Graph implements IGraph { +export class Graph extends Subject implements IGraph { private _nodes: IEntityState> = new EntityState>({ getId: (node) => node.getId(), sortBy: (node1, node2) => (node1.getStyle().zIndex ?? 0) - (node2.getStyle().zIndex ?? 0), @@ -64,15 +64,15 @@ export class Graph implements IGraph>; private _settings: IGraphSettings; - private _listeners: IObserver[] = []; constructor(data?: Partial>, settings?: Partial>) { // TODO(dlozic): How to use object assign here? If I add add and export a default const here, it needs N, E. + super(); this._settings = settings || {}; const nodes = data?.nodes ?? []; const edges = data?.edges ?? []; if (settings && settings.listeners) { - this._listeners = settings.listeners; + this.listeners = settings.listeners; } this.setup({ nodes, edges }); } @@ -377,20 +377,6 @@ export class Graph implements IGraph { - listener.update(); - }); - } - update(): void { this.notifyListeners(); } diff --git a/src/models/node.ts b/src/models/node.ts index 16e947c..3d283c8 100644 --- a/src/models/node.ts +++ b/src/models/node.ts @@ -2,7 +2,7 @@ import { IEdge, IEdgeBase } from './edge'; import { Color, IPosition, IRectangle, isPointInRectangle } from '../common'; import { ImageHandler } from '../services/images'; import { GraphObjectState } from './state'; -import { IObserver, ISubject } from '../utils/observer.utils'; +import { IObserver, ISubject, Subject } from '../utils/observer.utils'; import { copyProperties } from '../utils/object.utils'; /** @@ -141,25 +141,25 @@ export const isNode = (obj: any): obj return obj instanceof Node; }; -export class Node implements INode { +export class Node extends Subject implements INode { protected readonly _id: number; protected _data: N; protected _position: INodePosition; protected _style: INodeStyle = {}; protected _state = GraphObjectState.NONE; - private readonly _listeners: IObserver[] = []; private readonly _inEdgesById: { [id: number]: IEdge } = {}; private readonly _outEdgesById: { [id: number]: IEdge } = {}; private readonly _onLoadedImage?: () => void; constructor(data: INodeData, settings?: Partial) { + super(); this._id = data.data.id; this._data = data.data; this._position = { id: this._id }; this._onLoadedImage = settings?.onLoadedImage; if (settings && settings.listeners) { - this._listeners = settings.listeners; + this.listeners = settings.listeners; } } @@ -425,20 +425,6 @@ export class Node implements INode { - listener.update(); - }); - } - setData(data: N): void; setData(callback: (node: INode) => N): void; setData(arg: N | ((node: INode) => N)) { diff --git a/src/utils/observer.utils.ts b/src/utils/observer.utils.ts index 8b9c57b..ba7ada0 100644 --- a/src/utils/observer.utils.ts +++ b/src/utils/observer.utils.ts @@ -3,9 +3,41 @@ export interface IObserver { } export interface ISubject { + listeners: IObserver[]; + addListener(observer: IObserver): void; + getListeners(): IObserver[]; + removeListener(observer: IObserver): void; notifyListeners(): void; } + +export class Subject implements ISubject { + listeners: IObserver[]; + + constructor() { + this.listeners = []; + } + + addListener(observer: IObserver): void { + this.listeners.push(observer); + } + + getListeners(): IObserver[] { + return [...this.listeners]; + } + + removeListener(observer: IObserver): void { + if (!this.listeners.includes(observer)) { + this.listeners.push(observer); + } + } + + notifyListeners(): void { + for (let i = 0; i < this.listeners.length; i++) { + this.listeners[i].update(); + } + } +}