# 继承
使用继承来提供子类的行为,会导致一些缺点
- 运行时的行为不容易改变
- 改变会牵一发而动全身,造成其他子类不想要的改变
但如果全部使用接口的方式进行实现,接口不具有实现代码,会导致代码无法复用。
# 设计原则
找出应用中可能 需要变化 之处,把它们 独立 出来, 不要和那些 不需要变化的代码混在一起
- 把会变化的部分取出来,封装起来,以后可以轻易地改动或扩充此不符,不影响不需要变化的其他部分。
- 所有的设计模式都提供了一套方法,让系统中的某部分改变而不会影响其他部分。
针对 接口编程, 而不是针对 实现编程。
利用接口来代表每个行为,若要实现一个行为,不要在子类中去实现这个接口,而是专门制造一组其他类来专门实现这些接口,由行为类,而不是对象类来实现行为接口。
Dog d = new Dog(); d.bark(); // 针对实现编程 Animal animal = new Dog(); animal.makeSound(); // 针对接口编程
1
2
3
4
5增加一个新的行为,应该怎么做: 应该定义一个新的类(FlyWithRocket),实现
FlyBehavior
接口,实现fly()
方法
重要原则:多用组合,少用继承
- 使用组合建立的系统有很大的弹性,可以 将算法族封装成类,更可以在运行时动态地改变行为
# 策略模式
定义并封装一组算法,让他们之间可以相互替换,策略模式让算法的变化独立于使用算法的客户。
# 具体实现
在父类(Duck)类中,加入实例变量,声明为接口类型(而不是具体实现类型)
具体的行为,不在父类中具体实现,而是通过调用实例变量,委托给该变量引用的对象来完成
class Duck { FlyBehavior flyBehavior; public void performFly() { flyBehavior.fly() // 我们不在乎 flyBehavior 到底是什么,只知道该对象知道怎么去飞就够了 } }
1
2
3
4
5
6对于一个具体的类,在初始化时,对实例变量进行初始化
public class MallardDuck extends Duck { public MallardDuck() { flyBehavior = new FlyWithWings(); } }
1
2
3
4
5
# 动态设定行为(运行时改变对象的行为)
- 增加对实例变量的设定方法
setFlyBehavior()
- 在对象实例(duck)的生命周期中,可以 随时调用该方法,对行为的具体实现进行设定