设计模式入门——Head First
设计模式是被前人发现、经过总结形成了一套某一类问题的一般性解决方案。使用模式最好的方式是:把模式装进脑子,然后在设计和已有的应用中,寻找何处可以使用它们。以往是代码复用,现在是经验复用。
从模拟鸭子游戏说起
初始需求:各种鸭子(野鸭MallardDuck、红头鸭RedheadDuck等),一边游泳swim,一边呱呱叫quack,每种鸭子外观都不同display。
初始方案:设计一个鸭子超类,并让各种鸭子继承此超类。
需求变更:让鸭子能够飞fly。
方案1:在Duck类中加上fly()。
结果:所有鸭子都继承fly,但并非所有鸭子都会飞,比如橡皮鸭RubberDuck(继承牵一发动全身)。
方案1_1:对于不能飞的鸭子,在子类中覆盖fly()。
结果:每当有新的鸭子出现,就要*检查并可能需要覆盖fly()。同理,对于叫声quack也一样。
方案2:把fly()和quack()从超类中取出,放进Flyable和Quackable接口中,只会飞的鸭子实现Flyable,会叫的实现Quackable。
结果:不会再有会飞的橡皮鸭,但由于接口不具有实现代码,造成代码无法复用(接口无法达到代码复用)。
方案2_1:设计两个接口FlyBehavior和QuackBehavior,还有它们对应的类,负责实现具体行为。
结果:飞行和呱呱叫动作可以被其他的对象复用,因为这些行为已经与鸭子类无关;可以新增一些行为,不会影响到既有的行为类,也不会影响使用飞行行为的鸭子类(有了继承的复用好处,没了继承所带来的包袱)。
完善方案2_1:在Duck类中加入两个变量“flyBehavior”和“quackBehavior”,声明为接口类型,用performFly和performQuack分别取代fly和quack;同时加入两个新方法setFlyBehavior和setQuackBehavior,用来随时改变鸭子的行为。
测试Duck的代码
Duck类(Duck.java)
public abstract class Duck { //所有鸭子子类都继承它 FlyBehavior flyBehavior; QuackBehavior quackBehavior; public Duck(){ } public abstract void display(); public void perfmormFly(){ //委托给行为类 flyBehavior.fly(); } public void performQuack(){ //委托给行为类 quackBehavior.quack(); } public void setFlyBehavior(FlyBehavior fb){ flyBehavior=fb; } public void setQuackBehavior(QuackBehavior cb){ quackBehavior=cb; } public void swim(){ System.out.println("All ducks float,even decoys!!"); } }
MallardDuck类和ModelDuck类(MallardDuck.java与ModelDuck)
public class MallardDuck extends Duck{ public MallardDuck(){ quackBehavior=new Quack(); flyBehavior=new FlyWithSwings(); } public void display(){ System.out.println("I'm a real Mallard Duck"); } } public class ModelDuck extends Duck{ public ModelDuck(){ flyBehavior=new FlyNoWay(); quackBehavior=new MuteQuack(); } public void display(){ System.out.println("I'm a model duck!!"); } }