Pattern consigliato per influenzare altre Classi su chiamate di metodi / di istanze

1

Qual è il modo consigliato di fare riferimento ad altre classi all'interno di una classe contenitore mantenendo tutto disaccoppiato?

Nell'esempio seguente, vorrei aggiungere automaticamente al SceneGraph il Path costruito.

class SceneGraph {
  constructor() {
    this.children = []
  }

  addChild(child) {
    this.children.push(child)
  }
}

class Path {
  constructor(opts) {
    this.color = opts.color

    // @NOTE: Coupling here
    sceneGraph.addChild(this)
  }
}

// User space

const sceneGraph = new SceneGraph()
const path = new Path({ color: 'red' })

"Automaticamente" qui significa che mi piacerebbe evitare di fare qualcosa del genere:

const path = new Path({ color: 'red' })
sceneGraph.addChild(path)

... che sembra abbastanza intuitivo, ma presto comincia a diventare goffo man mano che vengono aggiunti sempre più metodi alla Path che dovrebbe anche influire sulla SceneGraph - i.e path.remove() .

L'esempio sopra mi sembra problematico, dal momento che SceneGraph è intrinsecamente accoppiato con Path .

C'è un modello disponibile che risolve questo problema?

    
posta Nik Kyriakides 07.02.2018 - 12:50
fonte

3 risposte

2

Potresti creare un PathFactory che non fa altro che creare Paths . PathFactory verrebbe creato con un'istanza di SceneGraph , in modo che PathFactory possa "automaticamente" collegare i due senza SceneGraph avendo direttamente un riferimento a Path .

class PathFactory {
  constructor(sceneGraph) {
    this.sceneGraph = sceneGraph;
  }

  createPath(options) {
    let path = new Path(options);
    this.sceneGraph.addChild(path);
    return path;
  }
}

const sceneGraph = new SceneGraph();
const pathFactory = new PathFactory(sceneGraph);

const path = pathFactory.createPath({color: 'red' });

Certo, questo sta spostando l'accoppiamento tra SceneGraph e Path con un intermediario PathFactory , tuttavia ottiene quello che desideri ottenere. Aggiunta automatica senza una relazione diretta tra SceneGraph e Path .

    
risposta data 07.02.2018 - 13:01
fonte
2

Path e SceneGraph sono già accoppiati mentre eccetto ogni Path da aggiungere al SceneGraph dopo l'istanziazione.

Ma qui c'è una chiara gerarchia. SceneGraph contiene un elenco di percorsi Path. In questi casi, trovo più pulito quando il contenitore genitore è responsabile della creazione dei bambini. Poi:

class SceneGraph {
  constructor() {
    this.children = []
  }

  newChild(opts) {
    var child = new Path(opts)
    this.children.push(child)
    return child;
  }
}

class Path {
  constructor(opts) {
    this.color = opts.color
  }
}

Esistono restrizioni sostanziali con questa soluzione. Ora c'è una dura dipendenza da SceneGraph a Path. Non è possibile iniettare implementazioni Path alternative. Se è necessaria più flessibilità, allora un factory separato come suggerito dalla risposta di Neil potrebbe essere un approccio migliore.

    
risposta data 10.02.2018 - 21:23
fonte
1

Se vuoi supportare la bella sintassi di path.remove() ecc., penso che dovrai avere il percorso con un link al suo contenitore, ScenePath. Questo introduce una fastidiosa dipendenza ciclica. Se questo vale la convenienza dell'API è una buona domanda. (Sono molto più tollerante delle dipendenze circolari rispetto alla maggior parte)  Codice di esempio:

class Path {
  constructor(sceneGraph, options) {
    this.variousStuff = options.variousStuff;
    this.sceneGraph = sceneGraph;  // introduces circular dependency
    sceneGraph.addChild(this);
  }

  remove() {  // note - I'd name this "delete", not "remove"
    this.sceneGraph.remove(this);
    this.cleanupAnythingThatNeedsCleanup();
  }

  clone() {    // and might name this "deepClone" if that fits better
     let cloned = this.clone();  // or deepClone()
     this.sceneGraph.addChild(cloned);
  }
}
    
risposta data 11.02.2018 - 00:56
fonte

Leggi altre domande sui tag