接着我的上一篇文章策略模式学习1。
让我们来回顾一下上面设计中橡皮鸭类的代码,并提出一下问题。
/** * 橡皮鸭 * @author Administrator * */public class RubberDuck extends Duck { @Override void display() { System.out.PRintln("橡皮鸭..."); } /* * 橡皮鸭子不会呱呱叫 * 但是问题来了,这个橡皮鸭子会飞? * 那设计人员会说:一样覆盖掉就好 */ @Override public void quack() { //覆盖超类的呱呱叫 System.out.println("吱吱叫..."); } @Override void fly() { //覆盖,变成什么事儿都不做 ; } /* * 那么问题来了,以后每个月更新,常常会出现新的鸭子子类,这要被迫每次检查 * 并可能覆盖quack() 和 fly() 这简直就是无穷无尽的噩梦!!!! * 这样设计的缺点: * 1.代码在多个子类中重复 * 2.运行时的行为不容易被改变 * 3.改变会牵一发动全身,造成其他鸭子不想要的改变 * 4.很难知道鸭子的全部行为,因为有了不属于它本身的行为。 */ //重复代码变多,那么你认为覆盖几个方法就算是差劲,那么对于48个Duck子类都要稍微修改一下 //飞行的行为,你又怎么说?需求:会飞的鸭子中,飞的动作可能有多种变化. }因此我们可以看到,利用继承来提供Duck的行为,会导致以上所述的问题以及缺点。那利用接口如何?将飞行方法和呱呱叫的方法做成一个接口,分别让具备这两个行为的鸭子实现这个接口,就能够解决不再会有会飞的鸭子。但是缺点是:造成了代码无法复用(虽然鸭子类中实现了飞行的方法,但是这个鸭子在某个场景下又想要其他类型的飞行方法呢?这有点类似于游戏中的升级,解锁了某技能或在某个副本中,会以新的行为出现)。这又跳进了另外一个坑。另外,如果飞行的动作可能还有多种变化呢?比如火箭式飞行,螺旋式飞行等等。
软件开发的一个不变的真理:改变(change).不管软件设计得多好,一段时间之后,总是需要成长和改变,否则软件就会“死亡”。
利用Flyable和Quackable一开始似乎还不错,解决了只有会飞的鸭子才实现Flyable,但是java接口不具有实现代码,所以实现接口无法达到代码的复用。这就意味着无论何时你需要修改某个行为,你必须得往下追踪到并在每一个定义此行为的类中修改它,一不小心,可能会造成新的错误。
幸运的是,这里有一个设计原则:
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。换句话说:如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分的代码需要被抽出来,和其他稳定的代码有所区分。对这个原则的另外一种思考方式:把会变化的部分取出并封装出来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。
我们知道Duck类的fly()和quack()会随着鸭子的不同而改变。因此我们把这两个行为从Duck类中分离出来,建立一组新类来代表每个行为。如何设计鸭子的行为?我们想要产生一个新的绿头鸭实例,并指定特定的“类型”的飞行行为给它。那么干脆顺便让鸭子的行为可以动态的改变好了。换句话说:我们应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态地“改变”绿头鸭的飞行行为。
设计原则:针对接口编程,而不是针对实现编程。
public abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior; // 行为变量被声明为行为 "接口" 类型 public void swim() { System.out.println("游来游去..."); } abstract void display(); //鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior引用的对象去执行 void performQuack() { quackBehavior.quack(); } void performFly() { flyBehavior.fly(); } public FlyBehavior getFlyBehavior() { return flyBehavior; } public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public QuackBehavior getQuackBehavior() { return quackBehavior; } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } /*public void quack() { System.out.println("呱呱叫..."); } void fly(){ System.out.println("飞来飞去..."); }*/ //鸭子的其他方法}public interface FlyBehavior { void fly();}public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("这里实现了所有有翅膀的鸭子飞行动作..."); }}public class FlyNoWay implements FlyBehavior { @Override public void fly() { //什么都不做,不会飞 System.out.println("I can't fly!"); }}public class FlyByRocky implements FlyBehavior { @Override public void fly() { // 新增了一个火箭式的飞行 System.out.println("火箭式的飞行..."); }}public interface QuackBehavior { void quack();}public class Quack implements QuackBehavior { @Override public void quack() { // 实现鸭子呱呱叫 System.out.println("呱呱叫..."); }}public class Squeak implements QuackBehavior { @Override public void quack() { // 橡皮鸭子吱吱叫 System.out.println("吱吱叫..."); }}public class MallardDuck extends Duck { @Override void display() { System.out.println("绿头鸭.."); }}public class ModelDuck extends Duck { public ModelDuck(){ this.flyBehavior = new FlyNoWay(); this.quackBehavior = new MuteQuack(); } @Override void display() { // 这是一只模型鸭 System.out.println("模型鸭..."); } }// 新增一个鸭鸣器,用来捕获其他真正鸭子public class DuckCall implements QuackBehavior{ QuackBehavior quack; public QuackBehavior getQuack() { return quack; } public void setQuack(QuackBehavior quack) { this.quack = quack; } @Override public void quack() { quack.quack(); } }//测试类public class MiniDuckSimulator { public static void main(String[] args) { /*Duck mallard = new MallardDuck(); mallard.setFlyBehavior(new FlyWithWings()); mallard.setQuackBehavior(new Quack()); mallard.performFly(); mallard.performQuack();*/ /*Duck modelDuck = new ModelDuck(); modelDuck.performFly(); modelDuck.setFlyBehavior(new FlyByRocky());//模型鸭可以动态地改变飞行行为 modelDuck.setQuackBehavior(new MuteQuack()); modelDuck.performFly();*/ //实现一个鸭鸣器用来诱捕野鸭 DuckCall duckCall = new DuckCall(); duckCall.setQuack(new Quack()); duckCall.quack();//利用多态,可以动态的模拟各种叫声 }}public class MuteQuack implements QuackBehavior { @Override public void quack() { // 什么都不做,不会叫 ; } /* * 这样设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这样行为已经与鸭子无关了 * 而我们可以新增一些行为,不会影响到既有的行为类,不会影响“使用”到飞行行为的鸭子类了。 * 有了继承的“复用”的好处,却没有了继承所带来的包袱 */}这里也用到另外一个设计原则:多用组合,少用继承。“有一个”可能比“是一个”更好。有一个关系相当有趣,每个鸭子都有一个FlyBehavior和QuackBehavior,当你将两个类结合起来使用,如通本例一般,这就是用到了组合。
新闻热点
疑难解答