Overview

Purpose

Use When

Example

Original Design

Original
Our Original Design

Requirement Changes...

First
Our First Attempt

Add a Rubber Duck

Override
Overriding RubberDuck

More and More Ducks to Override

What we have here

Our Second Attempt: How about an Interface?

Second
Our Second Attempt

Design Principle

Encapsulate What Varies

Seperating What Changes From What Stays The Same

Program to and interface, not an implementation

Interface
Dog d = new Dog();
d.bark();
Animal a = new Dog();
a.makeSound();
Animal a = getAnimal("Dog");
a.makeSound();

Improved Design

Improved

Class Duck

public abstract class Duck {
    FlyBehavior flyBehavior; // object composition
    QuackBehavior quackBehavior; // object composition

    public Duck() {
    }

    public abstract void display();

    public void performFly() {
        flyBehavior.fly(); // delegation
    }

    public void performQuack() {
        quackBehavior.quack(); // delegation
    }

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}

Interface FlyBehavior

public interface FlyBehavior {
    public void fly();
}

public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("I'm flying!!");
    }
}

public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("I can't fly");
    }
}

Interface QuackBehavior

public interface QuackBehavior {
    public void quack();
}

public class Quack implements QuackBehavior {
    public void quack() {
        System.out.println("Qauck");
    }
}

public class MuteQuack implements QuackBehavior {
    public void quack() {
        System.out.println("<<silence>>");
    }
}

public class Squeak implements QuackBehavior {
    public void quack() {
        System.out.println("Squeak");
    }
}

Concrete Ducks

public class MallardDuck extends Duck {
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

    public void display() {
        System.out.println("I'm a real Mallard Duck");
    }
}

Test Drive

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();
    }
}

Setting behavior dynamically

public abstract class Duck {
    FlyBehavior flyBehavior; 
    QuackBehavior quackBehavior; 

    ...

    public void setFlyBehavior(FlyBehavior fb) {
        this.flyBehavior = fb;
    }

    public void setQuackBehavior(QuackBehavior qb) {
        this.quackBehavior = qb;
    }
}

The Big Picture

BigPicture

Design principle: Favor Composition Over Inheritance (or Composing Objects Principle)

Eg

Reuse in Object-Oriented System

  1. Class Inheritance
    • subclass' implementation is defined in terms of the parent class' implementation.
    • the parent class implementation is often visible to the subclasses.
    • While-box reuse
    • pros
      • done at compile-time and is easy to use.
    • cons
      • the subclass becomes dependent on the parent class implementation.
      • the inherited implementation cannot be changed at run-time.
  2. Object Composition
    • object are composed to achieve more complex functionality.
    • needs well-defined interfaces since the internals of the objects are unknown.
    • functionality is acquired dynamically at run-time by utilizing references to other objects.
    • Black-box reuse
    • pros
      • implementations can be replaced at runtime.
      • less implementation dependencies.
      • harder to understand.

The Strategy Pattern

StrategyPattern
Strategy Pattern

Sorting Example

StrategyPattern
Sorting Example