Java 设计模式之路——策略模式

策略模式

策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

模拟鸭子的例子。

现在公司的需求是生产一批鸭子。有绿头鸭子,红头鸭子,橡皮鸭子,诱饵鸭。

分析一下我们的项目,我们可以设计一个抽象类的鸭子来让所有鸭子都继承,但是很明显,这样子的做法是很不合理,并且一旦有新的需求加进来,修改的代码量太大,而且不易修改。

仔细分析了以后,会发现,上面的鸭子,飞行的方式不一样和发出声音的方式不一样。因此我们可以抽取飞行和发出声音这两个形式作为两个策略,这就轮到我们的策略模式上场了。

《Java 设计模式之路——策略模式》

当有新的需求叫进来以后,我们利用接口引用,建立新的算法族或者原有的算法,这样完成了程序的解耦,废话不多说了,直接上代码。

抽象duck类

 * 
 * Created by Archer on 2016/11/16.
 */
 public abstract class Duck {
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
     protected FlyBehavior flyBehavior;  
     protected   QuackBehavior quackBehavior;
    public void swim() {
        System.out.println("不管是什么鸭子,总会是游泳的");
    }
    public abstract void display();
    public void performQuack() {
        quackBehavior.quack();
    }
    public  void performFly(){
        flyBehavior.fly();
    }
    }

可以看到的是duck这个抽象类,里面有两个接口类型的声明变量,这就是我们所说的两个策略,我们通过接口引用变量的方式,将两个策略独立的封装起来,当我们需要调用的时候,直接面向接口编程,通过已经实现接口的类(我们的策略),来向客户类(鸭子),提供策略,解决问题(即调用performQuack和performFly方法),然后委托给实现接口的类去完成业务逻辑。

在这复习一下继承的知识,由于所有鸭子都会游泳,因此swim方法是有所有鸭子的继承的,这也少些了很多代码,在里面定义了一个display的抽象方法,是因为每一只鸭子的外观都是不一样的,所以要求实现的子类里面都要重写display方法。接口引用变量默认应该是private的设置,只有设置为protected的类型,才能被子类使用,不然会报空指针异常。

飞行接口(策略一)

 * Created by Archer on 2016/11/16.
 */
 public  interface  FlyBehavior {
    void  fly();
    }

叫声接口(策略二)

 * Created by Archer on 2016/11/16.
 */
 public interface QuackBehavior {
    void quack();
    }

实现飞行接口的类

 * Created by Archer on 2016/11/16.
 */
 public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("我不会飞哦");
    }
    }
 * Created by Archer on 2016/11/16.
 */
 public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println(" 我正在使用翅膀高飞");
    }
    }
 * Created by Archer on 2016/11/16.
 */
 public class FlyWithRocekt implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("我正在使用火箭飞天");
    }
    }

实现叫声的类

public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("不会叫哦");
    }
    }
 * Created by Archer on 2016/11/16.
 */
 public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("实现鸭子的呱呱叫");
    }
    }
 * Created by Archer on 2016/11/16.
 */
 public class QuckWithTools implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("我正在使用虚拟的发声器");
    }
    }

实例化一只模型鸭

 * Created by Archer on 2016/11/16.
 */
 class ModeDuck  extends Duck{
    ModeDuck() {
        flyBehavior =new FlyNoWay();
        quackBehavior=new MuteQuack();
    }
    @Override
    public void display() {
        System.out.println("我是模型鸭子");
    }
    }

在这只模型鸭子中,我们在构造器中,初始化了飞行和叫声的实例化,直接指定为 “不会叫”和“无法飞翔”。

模拟鸭子的场景

public class miniDuckSimulator {
    public static void main(String[] args) {
        Duck modeDuck= new ModeDuck();
        modeDuck.display();
        modeDuck.performFly();
        modeDuck.performQuack();
       modeDuck.setFlyBehavior(new FlyWithRocekt());
        modeDuck.performFly();
   modeDuck.setQuackBehavior(new QuckWithTools());
       modeDuck.performQuack();
    }
    }

是时候生产一只模型鸭子了,注意我们采用的是针对接口或者抽象类编程,而不是针对实现编程,这样子的做法利用了java的多态性,因此只要是实现或者继承了这个接口或者抽象类的子类产生的对象,都可以指定给这个变量。

当我们有个新的需求是要让模型鸭子飞起来和叫出声音来的时候,我们直接调用setFlyBehavior,setQuackBehavior的方法,只要求传入一个实现了对应接口的对象,即可实现相应的功能而不影响其他类。这就是策略模式。

效果:

《Java 设计模式之路——策略模式》

策略模式的优点

  1. 算法可以自由切换
  2. 避免使用多重条件判断
  3. 扩展性良好

策略模式缺点

  1. 策略类数量增多
  2. 所有策略类都需要对外暴露

策略模式的使用场景

  1. 多个类只有在算法或者行为上稍有不同的场景
  2. 算法需要自由切换的场景
  3. 需要屏蔽算法规则的场景

更多源码

github.com/YanHuiLi/DP…

    原文作者:算法小白
    原文地址: https://juejin.im/entry/583a9dada22b9d006dc7d91e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞