## Inheritance Problems * Duplication of code in children * Excessive complexity in the inheritance hierarchy * Changing the behavior of the parent can lead to errors in the children \ In this article, we will look at what these problems are about and how we can solve them using composition. \ > the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. **You wanted a banana but what you got was a gorilla holding the banana** and the entire jungle. - Joe Armstrong, creator of Erlang ## Role-Playing Game Inheritance Consider the process of creating a hierarchy of role-playing game characters. Initially, two types of characters are required - Warrior and Mage, each of which has a certain amount of health and a name. These properties are public and can be moved to the parent Character class. ```javascript class Character { constructor(name) { this.name = name; this.health = 100; } } ``` \ A warrior can strike, spending his stamina: ```javascript class Warrior extends Character { constructor(name) { super(name); this.stamina = 100; } fight() { console.log(`${this.name} takes a mighty swing!`); this.stamina--; } } ``` \ And a mage can cast spells that spend some amount of mana: ```javascript class Mage extends Character { constructor(name) { super(name); this.mana = 100; } cast() { console.log(`${this.name} casts a fireball!`); this.mana--; } } ``` ## Paladin Class Problem Now, let’s introduce a new class, **Paladin**. A Paladin can both fight and cast spells. How can we solve this? Here are a couple of solutions that share the same lack of elegance: \ * We can make **Paladin** a descendant of **Character** and implement both the `fight()` and `cast()` methods in it from scratch. In this case, the **DRY** principle is violated because each of the methods will be duplicated upon creation and will need constant synchronization with the methods of the Mage and Fighter classes to track changes. \ * The `fight()` and `cast()` methods can be implemented at the **Character** class level so that all three character types have them. This is a slightly better solution, but in this case, the developer must override the `fight()` method for the mage and the cast() method for the warrior, replacing them with empty methods or consoling an error. ## Composition These problems can be solved with a functional approach using composition. It is enough to start not from their types, but from their functions. Basically, we have two key features that determine the abilities of the characters - the ability to fight and the ability to cast spells. \ These features can be set using factory functions that extend the state that defines the character: ```javascript const canCast = (state) => ({ cast: (spell) => { console.log(`${state.name} casts ${spell}!`); state.mana--; } }) const canFight = (state) => ({ fight: () => { console.log(`${state.name} slashes at the foe!`); state.stamina--; } }) ``` \ Thus, a character is defined by a set of these abilities and initial properties, both general (name and health) and private (stamina and mana): ```javascript const fighter = (name) => { let state = { name, health: 100, stamina: 100 } return Object.assign(state, canFight(state)); } const mage = (name) => { let state = { name, health: 100, mana: 100 } return Object.assign(state, canCast(state)); } const paladin = (name) => { let state = { name, health: 100, mana: 100, stamina: 100 } return Object.assign(state, canCast(state), canFight(state)); } ``` ## Conclusion With composition, you can avoid inheritance problems, and javascript is the perfect language to do so.