1) Start with a case
We start with a case, say we've got a super class called "Duck" from which all other duck types inherit. We've also got subclasses such as MallardDuck, WoodenDuck and RubberDuck etc.
2) Problem we have
Now assume there are already some common methods that all the ducks share such as display(). Now here comes the problem, if we want to implement a fly() method for only some of the duck types (e.g. MallardDuck), how do we implement this?
3) Using inheritance
We can put the fly() method in the Duck class, but RubberDuck cannot fly. No problem, we can override the fly() method to do nothing in RubberDuck.
These are the disadvantages of using inheritance to provide Duck behavior:
- Duplicate code ==> if we want to add a quack() method in the Duck class, since WoodenDuck cannot fly and quack, it has to override all these useless methods.
- Changes affect others ==> if we want to add a quack() method in the Duck class, for every duck subclasses, we have to override it and let it do nothing.
4) How about interface?
we can create interfaces called "IFlyable" and "IQuackable", so only ducks that are supposed to fly will implement the IFlyable interface.
There are the disadvantages of using interface to provide Duck behavior:
- Duplicate code ==> since interfaces have no implementation code, so no code reuse.
- Changes affect others ==> there might be more than one kind of flying behavior even among the ducks that do fly, e.g. FlyWithWings, FlyNoWay, you want to change the flyable behavior, you need to change all the subclasses accordingly.
5) Design Principle
Now we've come to the point where we need a pattern to solve this problem. Before we start looking at the pattern, we need to understand our first of many design principles here:
Design Principle 1: Identify the aspects of your application that vary and separate them from what stays the same.
6) Designing the Duck Behaviors