Regardons d'abord un exemple de la façon dont nous obtiendrions l'héritage dans ES5
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 } }
On peut visualiser la hiérarchie comme ceci :
Animal name energy eat() sleep() play() Dog breed bark() Cat declawed meow()
Disons que plus tard, vous êtes chargé d'ajouter une autre entité au système : l'utilisateur.
User email username pets friends adopt() befriend() Animal name energy eat() sleep() play() Dog breed bark() Cat declawed meow()
Tout fonctionne bien jusqu'à présent. Cependant, votre chef de projet vous demande désormais d'ajouter également la possibilité de manger, dormir et jouer à l'utilisateur. Comment feriez-vous? Voici comment nous aborderions cela en POO :
Mammal name eat() sleep() play() User email username pets friends adopt() befriend() Animal energy Dog breed bark() Cat declawed meow()
Cela semble assez fragile car une autre entité a dû être introduite qui aura désormais sa propre signification à mesure que le programme se développera. Cet anti-modèle est communément appelé Dieu Objet. Par conséquent, le problème avec la POO est que les entités ont une signification lorsque vous les écrivez, qui peut être modifiée ultérieurement à mesure que les exigences changent. Ces changements peuvent effondrer la structure hiérarchique des classes.
Je pense que le manque de réutilisabilité vient des langages orientés objet, pas des langages fonctionnels. Parce que le problème avec les langages orientés objet est qu'ils ont tout cet environnement implicite qu'ils emportent avec eux. Vous vouliez une banane mais ce que vous avez obtenu, c'est un gorille tenant la banane et toute la jungle.
-- Joe Armstrong (Créateur d'Erlang)
Plutôt que de penser à ce que sont les choses, passons à ce que font les choses.
Au lieu d'avoir ces méthodes étroitement couplées à une classe, nous pouvons les avoir sous forme de fonctions et les composer ensemble quand nous en avons besoin. Super! Mais alors, comment opérer sur une instance spécifique ? Eh bien, nous transmettons l'instance directement à nos fonctions. La fermeture permet aux fonctions de se souvenir de l'état (instance) qui a été transmis.
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 } })
Exemple de chien dormant, mangeur, joueur et aboyeur :
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()
Désormais, les utilisateurs peuvent également manger, dormir et jouer :
function User(email, username) { let user = { email, username, pets: [], friends: [] } return Object.assign( user, eater(user), sleeper(user), player(user) ) }
Félicitations, vous vous êtes libéré des structures successorales étroitement liées.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!