设计模式之策略模式

2019-12-08  本文已影响0人  戴先森Davi

策略模式的定义

策略模式是一种对象"行为型"模式。

策略模式的使用场景

策略模式的UML类图

策略模式UML类图1-0.png

角色介绍:

策略模式示例:

图1-2.png 图1-4.png 图1-5.png 图1-6.png
改进继承
Joe认识到继承可能不是一个好的解决办法,因为他刚刚拿到来自主管的备忘录,希望以后每六个月更新产品(至于更新办法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检视并可能需要覆盖fly()和quack().....这简直是无穷尽的噩梦。所以,他需要一个更清晰的方法,让某些(而不是全部)鸭子类型可飞或可叫。
图1-7.png 图1-8.png 图1-9.png

第一个设计原则

设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这个概念很简单,几乎是每个设计模式背后的精神所在,所有的模式都提供了一套方法让系统中的某部分改变不会影响其它部分。

1. 分开变化和不会变化的部分

2. 设计鸭子的行为

第二个设计原则

设计原则:针对接口编程,而不是针对实现编程。

针对接口编程,而不是针对实现编程,举例:

// 针对实现编程
// 声明变量“d”为Dog类型,会造成我们必须针对具体实现编码
Dog d = new Dog();
d.bark();

// 针对接口/超类型编程
// 我们知道该对象是狗,但是我们现在利用animal进行多态的调用
Animal animal = new Dog();
animal.makeSound();

// 更棒的是,子类实例化的动作不再需要在代码中硬编码,
// 例如new Dog(),而是“在运行时才指定具体实现的对象”
// 我们不知道实际的子类型是“什么”,我们只关心它知道如何正确地进行makeSound()的动作就够了
a = getAnimal();
a.makeSound();
针对接口编程.png

所以我们可以利用接口代表行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都将实现其中一个接口。
所以这次鸭子类不会负责实现Flying与Quacking接口,反而是由我们制造一组其他类专门实现FlyBehavior与QuackBehavior,这就成为“行为”类。

设计鸭子的行为.png

这样的做法迥异于以往,以前的做法是:行为来自Duck超类的具体实现,或是继承某个接口并由子类自行实现。这两种做法都是依赖于“实现”,我们被“实现”绑得死死的,没办法更改行为。
在我们的新设计中,鸭子的子类将使用接口(FlyBehavior与QuackBehavior)所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中。

3.实现鸭子的行为

实现鸭子的行为.png
这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子无关了。而我们可以新增一些行为,不会影响到既有的行为类,也不会影响有使用到飞行行为的鸭子类。

4. 集成鸭子的行为

5.动态的设定行为
在Duck类中,加入两个新方法:

 public void setFlyBehavior(FlyBehavior fb){
        flyBehavior=fb;
 }
  public void setQuackBehavior(QuackBehavior qb){
        quackBehavior=qb;
 }

从此以后,我们可以“随时”调用这两个方法改变鸭子的行为。

代码示例:

Duck.java

/**
 * 抽象鸭子超类
 */
public abstract class Duck {
    protected FlyBehavior flyBehavior;
    protected QuackBehavior quackBehavior;

    public Duck() {

    }

    public abstract void display();

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

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

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

}

DecoyDuck.java

/**
 * 诱饵鸭
 */
public class DecoyDuck extends Duck {

    public DecoyDuck() {
        setFlyBehavior(new FlyNoWay());
        setQuackBehavior(new MuteQuack());
    }

    @Override
    public void display() {
        System.out.print("I'm a duck Decoy");
    }

}

ModelDuck.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");
    }
}

MallardDuck.java

/**
 * 绿头鸭
 */
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");
    }
}

RubberDuck.java

/**
 * 橡皮鸭
 */
public class RubberDuck extends Duck{
    public RubberDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Squeak();
    }

    @Override
    public void display() {
        System.out.println("I'm a rubber duckie");
    }
}

RedHeadDuck.java

/**
 * 红头鸭
 */
public class RedHeadDuck extends Duck {
    public RedHeadDuck() {
        flyBehavior = new FlyWithWings();
        quackBehavior = new Quack();
    }

    @Override
    public void display() {
        System.out.println("I'm a real Red Headed duck");
    }
}

FlyBehavior.java

/**
 * 飞行行为接口
 */
public interface FlyBehavior {
    void fly();
}

FlyRocketPowered.java

/**
 * 火箭动力飞行
 */
public class FlyRocketPowered implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I'm flying with a rocket");
    }
}

FlyNoWay.java

/**
 * 不会飞
 */
public class FlyNoWay implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("I can't fly");
    }
}

FlyWithWings.java

/**
 * 实现鸭子飞行
 */
public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.print("I'm flying!!");
    }
}

QuackBehavior.java

/**
 * 叫的行为接口
 */
public interface QuackBehavior {
    void quack();
}

Quack.java

/**
 * 嘎嘎叫
 */
public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Quack");
    }
    
}

FakeQuack.java

/**
 * 假嘎嘎叫
 */
public class FakeQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Qwak");
    }
}

Squeak.java

/**
 * 吱吱叫
 */
public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Squeak");
    }
}

MuteQuack.java

/**
 * 禁音
 */
public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("<< Silence >>");
    }
}

测试程序 MiniDuckSimulator.java

/**
 * 迷你鸭子模拟器测试程序
 */
public class MiniDuckSimulator {
    public static void main(String[] args){
        MallardDuck mallard = new MallardDuck();
        RubberDuck rubberDuckie = new RubberDuck();
        DecoyDuck decoy = new DecoyDuck();

        Duck model = new ModelDuck();

        mallard.performQuack();
        rubberDuckie.performQuack();
        decoy.performQuack();

        model.performQuack();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();
    }
}

测试结果:

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

总结

“有一个”(has a)可能比“是一个”(is a)更好
有一个关系相当有趣:每一鸭子都有一个FlyBehavior且有一个QuackBehavior,让鸭子将飞行和呱呱叫委托它们代为处理。

如果将两个类结合起来使用(如同本例),这就是组合(Composition)。这种作法和继承不同的地方在于:鸭子的行为不是继承而来,而是和适当的行为对象组合而来。

第三个设计原则

设计原则:

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

优点

缺点

Android源码中的策略模式实现

参考资料
《Head First 设计模式》
《Android 源码设计模式解析与实战》

上一篇 下一篇

猜你喜欢

热点阅读