Schauen wir uns zunächst ein Beispiel an, wie wir die Vererbung in ES5 erreichen würden
class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length } } class Dog extends Animal { constructor(name, energy, breed) { super(name, energy) this.breed = breed } bark() { console.log('Woof Woof!') this.energy -= .1 } } class Cat extends Animal { constructor(name, energy, declawed) { super(name, energy) this.declawed = declawed } meow() { console.log('Meow!') this.energy -= .1 } }
Wir können die Hierarchie folgendermaßen visualisieren:
Animal name energy eat() sleep() play() Dog breed bark() Cat declawed meow()
Angenommen, Sie haben später die Aufgabe, eine weitere Entität zum System hinzuzufügen: Benutzer.
User email username pets friends adopt() befriend() Animal name energy eat() sleep() play() Dog breed bark() Cat declawed meow()
Bis jetzt funktioniert alles einwandfrei. Ihr Projektmanager weist Sie jedoch jetzt an, dem Benutzer auch die Möglichkeit zum Essen, Schlafen und Spielen hinzuzufügen. Wie würden Sie es machen? So würden wir das in OOP angehen:
Mammal name eat() sleep() play() User email username pets friends adopt() befriend() Animal energy Dog breed bark() Cat declawed meow()
Das sieht ziemlich fragil aus, da eine andere Entität eingeführt werden musste, die nun mit dem Wachstum des Programms ihre eigene Bedeutung haben wird. Dieses Anti-Muster wird im Volksmund „Gottesobjekt“ genannt. Das Problem bei OOP besteht also darin, dass Entitäten beim Schreiben eine Bedeutung haben, die später geändert werden kann, wenn sich die Anforderungen ändern. Diese Änderungen können die Klassenhierarchiestruktur zerstören.
Ich denke, dass der Mangel an Wiederverwendbarkeit in objektorientierten Sprachen auftritt, nicht in funktionalen Sprachen. Denn das Problem bei objektorientierten Sprachen ist, dass sie eine ganze implizite Umgebung mit sich herumtragen. Du wolltest eine Banane, aber was du bekamst, war ein Gorilla, der die Banane und den gesamten Dschungel hielt.
– Joe Armstrong (Erfinder von Erlang)
Anstatt darüber nachzudenken, was die Dinge sind, lasst uns zu dem übergehen, was die Dinge tun.
Anstatt diese Methoden eng an eine Klasse zu koppeln, können wir sie als Funktionen haben und sie bei Bedarf zusammenstellen. Großartig! Aber wie gehen wir dann mit einer bestimmten Instanz um? Nun, wir übergeben die Instanz direkt an unsere Funktionen. Durch den Abschluss können sich die Funktionen den Zustand (die Instanz) merken, der übergeben wurde.
const eater = (state) => ({ eat(amount) { console.log(`${state.name} is eating.`) state.energy += amount } }) const sleeper = (state) => ({ sleep(length) { console.log(`${state.name} is sleeping.`) state.energy += length } }) const player = (state) => ({ play(length) { console.log(`${state.name} is eating.`) state.energy -= length } }) const barker = (state) => ({ bark(length) { console.log(`Woof Woof!`) state.energy -= .1 } }) const meower = (state) => ({ meow(length) { console.log(`Meow!`) state.energy -= .1 } })
Beispiel für einen Hund, der schläft, frisst, spielt und bellt:
function Dog(name, energy, breed) { let dog = { name, energy, breed } return Object.assign( dog, eater(dog), sleeper(dog), player(dog), barker(dog) ) } const dog = Dog('Dog', 10, 'Bulldog') dog.eat(10) dog.bark()
Jetzt können Benutzer auch essen, schlafen und spielen:
function User(email, username) { let user = { email, username, pets: [], friends: [] } return Object.assign( user, eater(user), sleeper(user), player(user) ) }
Herzlichen Glückwunsch, Sie haben sich von eng gekoppelten Vererbungsstrukturen befreit.
Das obige ist der detaillierte Inhalt vonVererbung vs. Zusammensetzung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!