阅读 《Head First设计模式》的个人笔记
简单的模拟鸭子应用做起
公司做了一套相当成功的模拟鸭子游戏:SimUDuck
游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。此游戏的内部设计使用了标准的OO技术,设计了一个鸭子超类(superclass),并让各种鸭子继承此超类。
现在我们得让鸭子能飞
会飞的鸭子来将竞争者抛在后头。这有什么困难?
我只需要在Duck类中加上fly()方法,然后所有的鸭子都会继承fly()。这是我大显身手,真是OO才华的时候了。
但是可怕的问题发生了
股东会议上,有很多“橡皮鸭子”在屏幕上飞来飞去
怎么回事?
并非所有鸭子都会飞,即并非所有Duck的子类都会飞。
在Duck超类中加上新的行为,会使得某些并不合适该行为的子类也具有该行为。
(确实有一点小疏失。但是,他们怎么不干脆把这当成一种“特色”,其实还挺有趣的呀…)
可以把fly()方法覆盖掉,变成什么都不做,橡皮鸭就不会飞了。
但是如果以后加入了诱饵鸭,又会怎么样?诱饵鸭不会飞,也不会叫。
利用接口如何?
继承可能不是答案,因为以后可能会定期更新产品。入果每次都有新的鸭子出现,就要检查并可能覆盖fly()和quark()…这简直是无穷无尽的噩梦。
所以,需要一个更清晰的方法,让“某些“(而不是全部)鸭子类型可飞或可叫。
这样一来,重复代码会变多,如果后面有几十上百种鸭子,那么对于每个Duck的子类都要修改。
有代码不能复用的问题。
第二个设计原则:针对接口编程,而不是针对实现编程。
实现鸭子的行为
在此,我们有两个接口,FlyBehavior和QuackBehavior,还有它们对应的类,负责实现具体的行为:
这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子类无关了。
而我们可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为的鸭子类
整合鸭子的行为
关键在于,鸭子现在会将飞行和呱呱叫的动作“委托”别人处理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法。
测试Duck的代码
鸭子抽象类
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
public abstract void display();
public void perFormFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
野鸭类
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
@Override
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
飞行行为
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying");
}
}
呱呱叫行为
public interface QuackBehavior {
public void quack();
}
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("Quack");
}
}
测试类
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.perFormFly();
}
}
动态设定行为
在鸭子里建立了一堆动态的功能没有用到,就太可惜了!假设我们想在鸭子子类中通过“设定方法”来设定鸭子的行为,而不是在鸭子的构造器内实例化。
在Duck类总加入两个新方法:
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
制造一个新的鸭子类型: 模型鸭(Model Duck.java)
public class ModelDuck extends Duck{
public ModelDuck(){
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a model duck");
}
}
建立一个新的FlyBehavior类型
public class FlyRocketPowered implements FlyBehavior{
@Override
public void fly() {
System.out.println("I'm flying with a rocket!");
}
}
改变测试类,加上模型鸭,并使模型鸭具有火箭动力
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.perFormFly();
//测试模型鸭,如果成功了,意味着可以动态地改变它的飞行行为。
//如果把行为的实现绑死在鸭子类中,可就无法做到这样了
Duck model = new ModelDuck();
model.perFormFly();
model.setFlyBehavior(new FlyRocketPowered());
model.perFormFly();
}
}