Class composition is a design pattern used to build complex classes by combining behaviours from multiple sources rather than relying solely on inheritance.
- Class composition enables combining multiple functionalities in a single object.
- It is a flexible alternative to inheritance, allowing for better code reuse.
- Composition avoids tight coupling between classes and promotes modular design.
- Behavior from multiple classes can be added to a single object.
const CanFly = {
fly() {
console.log(`${this.name} is flying!`);
}
};
const CanSwim = {
swim() {
console.log(`${this.name} is swimming!`);
}
};
class Bird {
constructor(name) {
this.name = name;
}
}
// Adding behaviors through composition
Object.assign(Bird.prototype, CanFly);
const eagle = new Bird("Eagle");
eagle.fly();
Output
Eagle is flying!
- CanFly is an object containing the fly() method.
- CanSwim is another object that could be assigned similarly for swimming behavior.
- The Bird class is created with just a name property, and then fly() is added to its prototype through Object.assign(), giving the Bird class the ability to fly.
- The eagle.fly() call demonstrates how the behavior is mixed into the Bird instance.
Key Concepts
- Favor Composition Over Inheritance: Inheritance creates tightly coupled structures that can be hard to manage in complex systems. Composition is more flexible, allowing dynamic assembly of features.
- Mixin Pattern: A common way to achieve composition in JavaScript by adding methods from one object to another.
- Delegation: Using objects or functions to delegate tasks without creating a deep inheritance chain.
Advantages of Composition
- Flexibility: Easily add or remove functionality without affecting other parts of the code.
- Reusability: Modular code is easier to reuse across different projects.
- Avoids Fragile Base Class Problem: Changes in base classes won't ripple unpredictably through subclasses.
When to Use Class Composition?
- When multiple classes share behaviors but don’t belong to the same inheritance hierarchy.
- When you want to avoid deep inheritance chains.
- For adding independent features, like logging or event handling, without altering existing structures.
Potential Drawbacks of Composition
- Overhead in managing multiple independent components.
- Naming conflicts if multiple mixins or modules define methods with the same name.
Real-World Use Cases
- UI Components: React components often use composition to share logic (e.g., Higher Order Components or Hooks).
- Middleware in Frameworks: Express.js uses composition to stack middleware for request/response handling.
- Game Development: Characters with behaviors like CanJump, CanShoot, or CanSwim can be built using composition.
Class Composition vs Inheritance
| Aspect | Composition | Inheritance |
|---|---|---|
| Flexibility | Allows combining any set of behaviors from multiple sources. | A class is bound to a single parent and can only inherit from one class. |
| Code Reusability | Provides flexibility to reuse specific behaviors across classes. | Reuse is confined to a single parent, leading to potential complexity. |
| Encapsulation | Behaviors are encapsulated within individual classes and are independent. | Tight coupling between parent and child classes can reduce flexibility. |
| Maintainability | Easier to maintain, as changes in one class don’t affect others unexpectedly. | Changes in the parent class can propagate to all child classes. |