策略模式

场景


设计鸭子模拟器系统,实现具有各种行为组合的鸭子

  • 刚开始设计时,此系统设计了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承此超类

问题引入 : "让鸭子会飞! 此程序需要会飞的鸭子(火箭喷射鸭)" "鸭子的叫声不同(橡皮鸭, 模型鸭)"

较差的实现方式

  • 在超类中增加fly()方法,并给予实现.

    • 所有的鸭子都将拥有飞行的能力,但是并不是所有的鸭子都能飞(橡皮鸭)
    • 解决: 让不会飞的鸭子重写fly方法,什么也不做
    • 思考: 如果系统后续有各种各样的行为要增加(假设有50个), 并且每种行为有多种实现,并且一个鸭子可能有各种行为的组合 , 那么不具备这些能力的鸭子都要进行重写,这是一个好的设计吗?
  • 将飞行行为定义成接口,让鸭子子类去实现

    • 假设现在有100种鸭子,定义一个飞行的接口, 需要让有飞行能力的鸭子全部进行重写, 这样一来代码会重复很多,这是一个差劲的设计.

如何解决


  • 问题出现在哪里?
继承的问题:对类的局部改动,尤其超类的局部改动,会影响所有子类部分。影响会有溢出效果

超类挖的一个坑,每个子类都要来填,增加工作量,复杂度O(N^2) (增加N个行为,每个行为N个类来改)。不是好的设计方式

  • 如何设计

    • 分离可变部分和不可变部分

      • 不经常变动部分: Duck类
      • 可变部分: 可能新增的各种行为 fly() , quack()
    • 这次鸭子类不会负责实现Flying和Quacking接口,反而是由我们制造一组其他类专门实现FlyingBehavior和QuackBehavior接口, 这称为"行为类", 由行为类而不是Duck类来实现行为接口
    • 关键在于,鸭子现在会将飞行和呱呱叫的动作"委托"(delegate) 别人代理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法
  • 类图设计

  • 重构后的分析

    • 鸭子需要什么行为组合都能自行决定
    • 可以在运行中动态改变自己的行为( setBehavior )

策略模式总结


定义:策略模式定义了算法族(行为族),分别封装起来,让它们之间可以相互替换,此模式让算法的变化部分 (鸭子行为) 独立于算法的客户(鸭子)
  • 模式的理解

    • 角色

      • 拥有行为的主体(鸭子)
      • 各种不同的行为,每种行为都有各种实现(飞行 / 叫声)
    • 细节

      • 主体使用组合行为的方式, 让主体任意的组合需要的行为
      • 可以在运行中动态改变自己的行为( setBehavior )

核心代码部分

  • 鸭子类
public abstract class Duck {
    FlyBehavior flyBehavior;        //飞行行为
    QuackBehavior quackBehavior;    //叫声行为

    public Duck() {
    }

    public void setFlyBehavior(FlyBehavior fb) {
        flyBehavior = fb;
    }

    public void setQuackBehavior(QuackBehavior qb) {
        quackBehavior = qb;
    }

    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 interface FlyBehavior {
        public void fly();
    }
    
    public interface QuackBehavior {
        public void quack();
    }
  • 实现行为接口

    //不会飞行
    public class FlyNoWay implements FlyBehavior {
        public void fly() {
            System.out.println("I can't fly");
        }
    }
    //呱呱叫
    public class Quack implements QuackBehavior {
        public void quack() {
            System.out.println("Quack");
        }
    }
  • 具体的鸭子

    //模型鸭, 不会飞,只会呱呱叫
    public class ModelDuck extends Duck {
        public ModelDuck() {
            flyBehavior = new FlyNoWay();
            quackBehavior = new Quack();
        }
    
        public void display() {
            System.out.println("I'm a model duck");
        }
    }
  • 鸭子模拟器(主程序)

    public class MiniDuckSimulator {
    
        public static void main(String[] args) {
    
            MallardDuck    mallard = new MallardDuck();
            RubberDuck    rubberDuckie = new RubberDuck();
            DecoyDuck    decoy = new DecoyDuck();
    
    
            mallard.performQuack();
            rubberDuckie.performQuack();
            decoy.performQuack();
    
            Duck     model = new ModelDuck();
            model.performFly();
            //运行中改变行为
            model.setFlyBehavior(new FlyRocketPowered());
            model.performFly();
        }
    }
  • 输出结果

    Quack
    Squeak
    << Silence >>
    I can't fly
    I'm flying with a rocket

参考

​ 书籍: HeadFirst设计模式

​ 代码参考地址: 我就是那个地址


你的头发真的好长
42 声望5 粉丝