Strategy design pattern

With design patterns, instead of code reuse, we get experience reuse. For this pattern, we need to identify the aspects of our code that are changing with every new requirement and we need to pull it out and separate it from all other stuff that doesn't change.

Let's say we want to design two Duck classes: MallardDuck and RedheadDuck. Normally since they both share some methods, we'll define a superclass Duck and then sub-classes will inherit from this superclass:

class Duck {
  quack();
  swim();
  display();
}

class MallardDuck: Duck {
  display();
}

class RubberDuck: Duck {
  display();
}

If we want to add Fly behaviour to our Duck class, we can easily add it to our base class and all the subclasses will be able to fly:

class Duck {
  quack();
  swim();
  fly();
  display();
}

But RubberDuck cannot fly and adding fly to the superclass will break this class. Based on "Favor composition over inheritance" principle you might think it's a better idea to pull out the fly and quack methods from the superclass and create two separate interfaces for these behaviours:

class Duck {
  swim();
  display();
}

interface Flyable {
  fly();
}

interface Quackable {
  quack();
}

With this approach, only the ducks that can fly will implement the Flyable interface and only the ducks that can quack will implement the Quackable interface. The problem with this approach is code duplication. If we want to change the fly or quack behaviour, then we need to change it in all subclasses. To fix this issue we need to rewrite our Duck class as below:

abstract class Duck {
  FlyBehavior flyBehavior;
  QuackBehavior quackBehavior;
  abstract display();

  performFly() {
    flyBehavior.fly();
  }

  performQuack() {
    quackBehavior.quack;
  }

  swim() {
    print "all ducks swim!";
  }
}

In this class, we left the method shared between all subclasses (Swim) in the superclass but we took out the changing behaviours and moved it into separate interfaces (Fly and Quack). Then we created classes for the behaviours and we're going to only use these behaviours rather than duplicating them in subclasses.

interface FlyBehavior {
  fly();
}

interface QuackBehavior {
  quack();
}

class FlyWithWings: FlyBehavior {
  fly() {
    print "Flying with wings!";
  }
}

class FlyNoWay: FlyBehavior {
  fly() {
    print "Cannot fly!"
  }
}

class Quack: QuackBehavior {
  quack() {
    print "quack!";
  }
}

class MuteQuack: QuackBehavior {
  quack() {
    print "Cannot quack!";
  }
}

class Squeak: QuackBehavior {
  quack() {
    print "Squeak!";
  }
}

And we can create sub-classes without the code duplication of the interface approach or the limitation of the inheritance approach:

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

  display() {
    print "MallardDuck"
  }
}

class RubberDuck: Duck {
  RubberDuck() {
    quackBehavior = new Quack();
    flyBehavior = new FlyNoWay();
  }

  display() {
    print "RubberDuck"
  }
}