headFirst设计模式——策略模式

一、引入

  继承的最大弊端:

    改变会牵一发而动全身,造成其他子类不想要的改变。

    无限的重写方法会让人崩溃。

  利用接口:

    实现接口,也要重写方法,每个实现类都重写这个方法,代码无法复用,工作量更大。

  解决:

    将变化的行为和不变的行为分开:把变化的行为独立出来

    针对接口编程而不是对实现编程:把变化的接口独立出来,实现不同的行为,不同的实体用到该行为时,也可以复用

    

 

 

    超类中加入变化的接口,每个变量会利用多态的方式在运行时引用正确的行为类型(变化的接口的实现类)

public abstract class Duck {

    FlyBehavior flyBehavior;
    
    QuackBehavior quackBehavior;
    
    public Duck() {
    }

    public void performFly(){
        flyBehavior.fly();
    }
    
    public void performQuack(){
        quackBehavior.quack();
    }
    
    public void swim() {
        System.out.println("Duck 会游泳");
    }
    
    public abstract void display();          //抽象的方法,每个鸭子都不一样的外貌
    
}

 

 

     实现接口

    

 

     具体子类继承

     

public class MallardDuck extends Duck {

    @Override
    public void display() {
        // TODO Auto-generated method stub
        System.out.println("绿头鸭");
    }

    public MallardDuck() {
        flyBehavior = new FlyWithWings(); 
        quackBehavior = new Quack();
    }

}

    因为子类继承父类,所以具有接口的实例变量,后面会有更多模式可以用。

    仍 请 注 意 , 虽 然 我 们 把 行 为 设 定 成 具体的类(通过实例化类似 Q u a c k

    或F l y W i t h W i n g s的行为类,并指定到行为引用变量中),但是还是可以在运行时『轻易

    地』改变该行为。
    所以,目前的作法还是很有弹性的,只是初始化实例变量的作法不够弹性罢了。
    但是想一想,因为 q u a c k B e h a v i o r 实例变量是一个接口类型,能够在运行时,
    透过多态的魔法动态地指定不同的 Q u i c k B e h a v i o r 实现类给它。
    想想,如何实现鸭子,好让其行为可以在运行时改变。(再几页以后,你就会看到做这件事的代码。)
 1 public class MiniDuckSimulator {
 2 
 3     public static void main(String[] args) {
 4         Duck mallardDuck = new MallardDuck();
 5         mallardDuck.performFly();             // 实现鸭子飞行
 6         mallardDuck.performQuack();           // 实现鸭子呱呱叫
 7         mallardDuck.display();              // 绿头鸭
 8     }
 9     
10 }

 

     动态的改变行为

     在Duck中放开修改行为的公共方法。

1 //    动态设定行为
2     public void setFlyBehavior(FlyBehavior flyBehavior) {
3         this.flyBehavior = flyBehavior;
4     }
5 
6     public void setQuackBehavior(QuackBehavior quackBehavior) {
7         this.quackBehavior = quackBehavior;
8     }
9 //    从此以后,我们可以「随时」调用这两个方法改变鸭子的行为。

    

public class FlyRocketPowered implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("火箭动力飞行");
    }

}

 

1 public class MiniDuckSimulator {
2 
3     public static void main(String[] args) {
4         Duck modelDuck = new ModelDuck();
5         modelDuck.performFly();                             //不会飞
6         modelDuck.setFlyBehavior(new FlyRocketPowered());
7         modelDuck.performFly();                 //火箭动力飞行
8     }
9 }

 

二、总结

   大局观:

    鸭子子类继承Duck,飞行行为实现FlyBehavior接口,鸭子叫行为实现QuackBehavior接口。

    我们描述鸭子的行为时,不再说成「一组行为」,开始把行为想成是「一族算法」。

    算法代表鸭子能做的事(不同的叫法和飞行法)。

    特别注意类之间的关系。可以是 IS-A(是一个)、HAS-A(有一个)、IMPLEMENTS(实现)。

    

     有一个的关系:每一个鸭子都有一个FlyBehavior 且有一个 QuackBehavior,鸭子将飞行和叫都委托它们代为处理。

    将两个类结合起来使用,如同本例一般就是组合(composition)。这种做法和继承不同的地方在于,鸭子行为不是继承而来,

    而是和适当的行为对象组合而来。

    本例用到三个设计原则:

    1、找出应用中可能变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    2、针对接口编程而不是针对实现编程。

    3、多用组合,少用继承。

    使用组合建立系统具有很大的弹性,不仅可以将算法族封装成类,更可以『 在 运 行 时 动 态 地 改 变 行为』,

    只要组合的行为对象,符合正确的接口标准即可。

     

 

 

    这种模式就是『策略模式』Strategy Pattern定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的用户。

    v>
    知道 O O基础(封装、抽象、继承、多态),并不足以让你设计出良好的 O O系统。
    v>
    良好的 O O设计必须具备可 复 用 、 可 扩 充 、 可 维 护三个特性。
    v>
    模式可以让我们建造出具 有 良 好 O O 设 计 质 量 的 系统。
    v>
    模式被认为是历经验证的 O O设计经验。
    v>
    模式不是代码,而是针对 设 计 问 题 的 通 用 解 决 方案 。 你 把 它 们 应 用 到 特 定的应用中。
    v>
    模式不是被发明,而是被发现。
    v>
    大多数的模式和原则,都着眼于软件变化的主题。
    v>
    大多数的模式都允许系统 局 部 改 变 独 立 于 其 他 部分。
    v>
    我们常把系统中,会变化的部分抽出来封装。
    v>
    模式让开发人员之间有共 享 的 语 言 , 最 大 化 沟 通的价值。
 
posted @ 2020-05-15 17:08  归零19  阅读(385)  评论(0编辑  收藏  举报