父类
class Duck{
void quack();
void swim();
void fly();
void display();
};
子类
- 覆盖父类的display()函数,实现自己的display(),每种鸭子都有自己的外观特征
- 继承父类的quack()、swim()、fly()
class MallardDuck : public Duck {
void display() {
// 外观是绿头
}
};
class RedheadDuck : public Duck {
void display() {
// 外观是红头
}
};
问题
- 并不是所有的鸭子都会飞,橡皮鸭子就不会飞, 诱饵鸭不会飞
- 并不是所有的鸭子都会叫,诱饵鸭就不会叫
所以子类直接继承父类的fly()和swim()是不合适的。
解决方案
- 在和父类不同时,子类覆盖掉父类的fly()和swim(),实现自己的fly()和swim()
- 该方案存在的问题
- 代码在多个子类中重复,可能多个子类的fly()相同,但是与父类的不同,不得不在每个子类中都实现一个相同的fly()
- 运行时的行为不容易改变,如果一只鸭子刚开始是咕咕叫,后来变成过呱呱叫,该怎么办?
- 很难知道所有鸭子的全部行为,父类可能不囊括鸭子的所有有行为
- 改变会牵一发动全身,造成其他鸭子不想要的改变
- 该方案存在的问题
class RubberDuck : public Duck{
void fly() {
// 覆盖掉父类的fly(), 变成什么时都不做
}
};
class DecoyDuck : public Duck {
void fly() {
// 覆盖,变成什么事都不做
}
void quack() {
// 覆盖,变成什么事都不做
}
};
- 把fly()从超类中取出来放进一个"Flyable接口"中。同样的方法,把quack()从超类中取出来放进一个"Quackable接口"。
- 这么一来,只有会飞的鸭子才实现"Flyable接口"。只有会叫的鸭子才实现"Quackable()"
- 代码难以复用,每种鸭子都得实现自己的fly()或wim。当有很多鸭子的实现方式一样时,会出现很多相同的代码。只是从一个噩梦跳到另一个噩梦中。
- 分开变化和不会变化的部分。
- 准备两组类(完全远离Duck类),一个是"fly"相关的,一个是"quack"相关的,每一组都实现各自的动作。
- 接口"Flyable", 具体实现由子类完成。(“Quackable类似”)
- 在Duck类中,加入两个实例变量,分别为"Flyable()“和"Quackable()”
- 分析优势
- 鸭子类中只使用了接口,没有实现,代码得到了复用。
- 还可以在运行时改变状态,赋值一个新的子类对象给Flyable接口或Quackable接口即可。
- 当需要添加新的飞行方式或叫的方式时,只需要继承接口,在子类中实现即可。Duck类不需要做任何改动。
设计原则1
找出应用中可能需要的变化之处,把它独立出来,不要和那些需要变化的代码混在一起
设计原则2
针对接口编程,而不是针对实现编程
原则3
多用组合,少拥继承