Composing Objects Principle

Favor composition over inheritance

Overview

"Favor Composition Over Inheritance" is a fundamental design principle. Instead of using inheritance to reuse code, compose objects by including instances of other classes as fields.

Composition provides greater flexibility because it allows you to change behavior at runtime and avoids the tight coupling that inheritance creates.

Key Concepts

Composition: Has-a relationship

Inheritance: Is-a relationship

Composition is more flexible than inheritance

Allows runtime behavior changes

Avoids fragile base class problem

Code Example

java
1// ❌ Using inheritance - inflexible
2abstract class Bird {
3    abstract void move();
4}
5
6class Sparrow extends Bird {
7    void move() { System.out.println("Flying"); }
8}
9
10class Penguin extends Bird {
11    void move() { System.out.println("Walking"); }  // Penguins can't fly!
12}
13
14// What about a bird that can both fly and swim? Multiple inheritance issues!
15
16// ✅ Using composition - flexible
17interface MovementBehavior {
18    void move();
19}
20
21class FlyingBehavior implements MovementBehavior {
22    public void move() { System.out.println("Flying through the air"); }
23}
24
25class WalkingBehavior implements MovementBehavior {
26    public void move() { System.out.println("Walking on ground"); }
27}
28
29class SwimmingBehavior implements MovementBehavior {
30    public void move() { System.out.println("Swimming in water"); }
31}
32
33class Bird {
34    private MovementBehavior movementBehavior;
35    private String name;
36    
37    public Bird(String name, MovementBehavior behavior) {
38        this.name = name;
39        this.movementBehavior = behavior;
40    }
41    
42    public void move() {
43        System.out.print(name + " is ");
44        movementBehavior.move();
45    }
46    
47    // Can change behavior at runtime!
48    public void setMovementBehavior(MovementBehavior behavior) {
49        this.movementBehavior = behavior;
50    }
51}
52
53// Usage
54public class Main {
55    public static void main(String[] args) {
56        Bird sparrow = new Bird("Sparrow", new FlyingBehavior());
57        Bird penguin = new Bird("Penguin", new WalkingBehavior());
58        Bird duck = new Bird("Duck", new FlyingBehavior());
59        
60        sparrow.move();  // Sparrow is Flying through the air
61        penguin.move();  // Penguin is Walking on ground
62        
63        // Duck lands in water - change behavior at runtime!
64        duck.setMovementBehavior(new SwimmingBehavior());
65        duck.move();  // Duck is Swimming in water
66    }
67}

Birds have movement behaviors rather than inheriting them. This allows runtime changes and avoids inheritance hierarchies.

When to Use

  • When you need runtime flexibility

  • When "is-a" relationship doesn't truly apply

  • To avoid deep inheritance hierarchies

  • When subclasses need different combinations of behaviors

  • To make testing easier (inject mock behaviors)

Advantages

  • Runtime flexibility - swap behaviors

  • Avoids inheritance hierarchy problems

  • Easier to test with mock objects

  • Clearer dependencies

  • No fragile base class problem

Best Practices

  • 1.

    Use inheritance only for true "is-a" relationships

  • 2.

    Prefer composition for code reuse

  • 3.

    Use interfaces for composed behaviors

  • 4.

    Apply Strategy pattern for interchangeable behaviors

  • 5.

    Consider composition when designing new classes

💡 Interview Tips

  • Explain why inheritance creates tight coupling

  • Show Bird example with behaviors

  • Discuss Strategy pattern as composition

  • Mention runtime flexibility advantage

  • Know when inheritance IS appropriate

AI Tutor

Ask about the topic

Sign in Required

Please sign in to use the AI tutor

Sign In
Composing Objects Principle - Design Principles | LLD | Revise Algo