2017-01-31 3 views
0

Ich habe die folgende Klasse "Grid" von Array, mit dem Zweck, ein paar Methoden für bidimentale Arrays zu implementieren, hat es derzeit keine "Konstruktor" -Funktion. Aus Gründen der Kürze zeige ich nur die problematische Funktion: Grid.getPlane, die ein mit Parametern konstruiertes Subgitter zurückgibt.Generieren von Objekten der gleichen Klasse wie die aufrufende Instanz in ES6

class Grid extends Array { 
//... 
    getPlane(width, height, x, y) { 
    let myPlane = new Grid; 
    // calculations... 
    return myPlane; 
    } 
//... 
} 

Ich habe dann eine andere Klasse 'Terrain', die von diesem erstreckt. Dieser sollte eine spezifischere Funktionalität für Topographiedaten haben. Die beabsichtigte Funktionalität wäre, dass, wenn ich die "getPlane" -Funktion einer Instanz der Klasse "Terrain" aufrufe, das zurückgegebene Objekt ebenfalls der "Terrain" -Klasse angehört (so kann ich für diese Klasse spezifische Funktionen verwenden). Aber wie Sie kann ich entweder vorhersagen verwenden, um die geerbte Funktion Deklaration von ‚Grid‘ und eine Grid (anstelle eines Terrain), oder die Funktion überschreiben, mich mit hässlichen duplizierten Code zu verlassen:

class Terrain extends Grid { 
//... 
    getPlane(width, height, x, y) { 
    let myPlane = new Terrain; 
    // calculations... 
    return myPlane; 
    } 
//... 
} 

ich versucht, zu verwenden Object.create in aber:

let myPlane = Object.create(this.prototype) 

Rückgabe undefined. Und

let myPlane = Object.create(this.constructor.prototype) 

Gibt mir ein Objekt namens ‚Terrain‘, die nicht wie ein Array nicht verhält. Gibt es eine Möglichkeit für Object.create, um mir ein Objekt der gleichen Klasse wie das Objekt 'this' zu bekommen? Oder irgendeine andere Möglichkeit, Objekte mit der gleichen Klasse zu generieren?

Antwort

1

Dies ist ein Fall von:

  1. Methoden, die zu viel zu tun Weg
  2. Ein Mangel an super
  3. tief verschachtelte Hierarchien eine schmerzhafte Art und Weise des Schreibens wartbaren Systeme sind auf Verfahren zur rufend; multipliziert mit 4000x in JS

Erstens: Jetzt

// assume 
class A extends Array { 
    doX (x) { 
    console.log(`A.doX(${x})`); 
    return new A(); 
    } 
} 

class B extends A { 
    doX (x, y) { 
    super.doX(x); 
    console.log(`B.doX(${x}, ${y})`); 
    return new B(); 
    } 
} 

class C extends B { 
    doX (x, y, z) { 
    super.doX(x, y); 
    console.log(`C.doX(${x}, ${y}, ${z})`); 
    return new C(); 
    } 
} 


const c = new C(); 
c.doX(1, 2, 3); 
// "A.doX(1)" 
// "B.doX(1, 2)" 
// "C.doX(1, 2, 3)" 

, sollte ich erwarten keine Probleme zu haben, was auch immer diesen Code ausgeführt wird.
Ich werde eine Hölle von Zeit wirklich nutzen, obwohl es.

Warum? Weil ich die Entscheidung getroffen habe, die Methode zu überladen, um gleichzeitig um Konstruktion und Logik zu sorgen.

Die A-Klasse gibt immer ein A zurück, die B-Klasse gibt immer ein B zurück, obwohl der Superaufruf tatsächlich ein völlig anderes Objekt (ein neues A) zurückgibt. Das C gibt ein neues C zurück, obwohl es bereits ein A und ein B erzeugt hat (hat aber keinen Bezug auf A).

Also was machst du?

Ein paar Gedanken:

  • den Bau des Objekts bewegen Weise das Heck aus dem Objekt, und es passieren, statt
  • Trennen Sie Ihre Datenobjekt aus Ihrer Bibliothek

Also lassen Sie uns Bau Trennung versuchen

class Person { 
    static make() { return new Person(); } 
    setDetails (name, age) { 
    this.name = name; 
    this.age = age; 
    } 
    clone (cfg = this) { 
    const person = Person.make(); 
    person.setDetails(cfg.name, cfg.age); 
    return person; 
    } 
} 

class Employee extends Person { 
    static make() { return new Employee(); } 
    setDetails (name, age, id) { 
    super.setDetails(name, age); 
    this.id = id; 
    } 
    clone (cfg = this) { 
    const employee = Employee.make(); 
    employee.setDetails(cfg.name, cfg.age, cfg.id); 
    return employee; 
    } 
} 

Sie werden feststellen, dass die Methoden clone nicht vererbt werden. Sie konzentrieren sich auf Instanziierung. Sie sind im Grunde Fabriken (das ist wirklich der Bereich eines separaten Objekts oder eine statische Methode, wie make|from|of|empty|unit).

Dann rufen sie setDetails an, was eine Instanzmethode ist, die im Grunde tut, was der Konstruktor hätte tun sollen oder was eine Fabrik tun sollte, und hat geerbtes Verhalten.

Apropos DRY, Erbschaft ist eine Art schrecklicher Weg, um so zu bleiben. Nur aus dem, was ich geschrieben habe, wie viele Zeilen wurden entweder zum Überschreiben von Konstruktoren (clone, make), oder Aufruf an Eltern (super) oder auch nur über die Erweiterung, nur weil gerade besorgt?

Das bringt mich zu einem anderen Muster: Bibliotheken, reine Funktionen/Methoden und Dekoration.

Wenn Sie sich nicht um den eigentlichen "Typ" kümmern (und in rohen JS, während in der Konsole hilfreich, sollten Sie nicht, weil es woanders nutzlos ist), dann können Sie glücklich datenzentrische Objekte machen.
Structs, genau wie Sie in C oder Go oder Python oder dergleichen sehen würden.

Dann könnten Sie alle wiederverwendbaren Berechnungen schreiben, die Sie möglicherweise für Bibliotheken/Dienste wünschen, die Sie für diese Strukturen (oder idealerweise Kopien davon) verwenden.

class Vector { 
    static make2D (x = 0, y = 0) { 
    return { x, y }; 
    } 
    static make3D (x = 0, y = 0, z = 0) { 
    return { ...Vector.make2D(x, y), z }; 
    } 
    static add2D (v1, v2) { 
    return Vector.make2D(v1.x + v2.x, v1.y + v2.y); 
    } 
} 


const vectors = [ 
    { x: 0, y: 1 }, 
    { x: 32, y: 8 }, 
    { x: 10, y: 12 }, 
    { x: 0, y: 0 }, 
]; 

const vectorSum = vectors.reduce(Vector.add2D, Vector.make2D()); 
vectorSum; // { x: 42, y: 21 } 

Wenn Sie benötigt sie wirklich eingegeben haben, werden dann so etwas wie Sie tun können:

class Vector { 
    add2D (
    {x: x1, y: y1}, 
    {x: x2, y: y2} 
) { 
    return Vector2D.of(x1 + x2, y1 + y2); 
    } 
} 

class Vector2D { 
    constructor (x, y) { 
    return Object.assign(this, { x, y }); 
    } 
    static of (x, y) { return new Vector2D(x, y); } 
    static from ({ x, y }) { return Vector2D.of(x, y); } 
    static empty() { return Vector2D.of(0, 0); } 
} 

const vector = vectors 
    .map(Vector2D.from) 
    .reduce(Vector.add2D, Vector2D.empty()); 

// Vector2D { x: 42, y: 21 } 

Sie würden eine harte Zeit, über Ihren Code zu diesem Zeitpunkt nicht trocken sein beschwert haben. Sie könnten sogar 5D-Vektoren in die Eingabedaten einfügen, und Sie würden korrekte 2D-Vektoren erhalten ...

Verwandte Themen