首页 > 技术文章 > 设计模式之策略模式(Strategy Pattern)

yimeixiaobai1314 2021-04-18 17:31 原文

策略模式(Strategy Pattern)

1.场景引入

大家外出旅游时肯定要事先规划行程,那乘坐的交通工具便是不得不考虑的问题。我们出门时会有好多交通工具作为候选,比如公交、飞机、火车、步行、脚踏车等等。再比如,计算1+1时,我们可以选择珠心算、计算器、列竖式等等来解决。

上述公交、飞机、火车、步行、脚踏车便是出行方式的多种策略(方法),同样珠心算、计算器、列竖式也是计算1+1=2的多种策略。

外出旅游的多种方式

2.定义

定义算法(策略)族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。(来自于《Head First 设计模式》)

稍微解释一下:主体对象(即上述图片中的人)可以有多种策略(即出行方式)。主体对象可以根据环境或者条件的不同选择不同的策略来完成所需要的功能,但是如果将这些不同的策略都硬编码到一个类中,通过if…else…或者case等条件判断语句来进行选择,后果是代码耦合度过大,维护较为困难。而策略模式的意图主要就是将主体对象(上述图片中的人)和策略尽可能地分离开来,将这些策略定义为单独的类(同时这些策略实现相同的接口)。通过将策略对象组合进主题对象来达到解耦效果。

3.组成部分

策略模式把对象本身和策略区分开来,因此该模式也分为两个部分:

环境类(Context):用来操作策略的上下文环境,也就是人。

策略类:

  • 抽象策略类(Strategy):策略的抽象,也就各种出行方式的父类。

  • 具体策略类(ConcreteStrategy):具体的策略实现,每一种具体的出行方式,如自行车、汽车等等。

策略模式类图

4.具体实现

现在我们按照上述组成部分来实现一个外出旅游选择出行方式的功能。

第一步:定义抽象策略接口

/**
 * IVehicle.java
 * 交通工具-抽象策略接口
 * 包含一个出行方法goingOut()
 */
public interface IVehicle {
    void goingOut();
}

第二步:实现具体策略类

/**
 * Bicycle.java
 * 自行车-具体策略类
 * 重写出行方法goingOut()
 */
public class Bicycle implements IVehicle {
    @Override
    public void goingOut() {
        System.out.println("骑着自行车---哐哧哐哧。");
    }
}

/**-------------------------------------------------*/
/**
 * Train.java
 * 火车-具体策略类
 * 重写出行方法goingOut()
 */
public class Train implements IVehicle {
    @Override
    public void goingOut() {
        System.out.println("坐着火车---咔哧咔哧。");
    }
}

/**-------------------------------------------------*/
/**
 * Plane.java
 * 飞机-具体策略类
 * 重写出行方法goingOut()
 */
public class Plane implements IVehicle {
    @Override
    public void goingOut() {
        System.out.println("乘着飞机---扑哧扑哧。");
    }
}

第三步:实现环境类

/**
 * Traveller.java
 * 环境类(Context):旅行者
 * 可以用来使用出行策略的主体类
 */
public class Traveller {

    private String name; // 旅行者的姓名,用来标识具体的出行者,与策略模式无关,也可没有此属性。
    private IVehicle vehicle; // 出行策略

    public Traveller(String name) {
        this.name = name;
    }
    /**
     * 设置出行策略
     * @param vehicle 交通工具
     */
    public void setVehicle(IVehicle vehicle) {
        this.vehicle = vehicle;
    }

    /**
     * 乘坐交通工具外出旅行
     */
    public void travel() {
        System.out.print(this.name + "要出门了。");
        vehicle.goingOut();
    }
}

第四步:功能测试



/**
 * TravelStrategyTest.java
 * 功能测试类
 */
public class TravelStrategyTest {
    public static void main(String[] args) {
        // 自行车出行策略对象
        IVehicle vehicle1 = new Bicycle();
        // 火车出行策略对象
        IVehicle vehicle2 = new Train();
        // 飞机出行策略对象
        IVehicle vehicle3 = new Plane();
        // 旅行者环境类
        Traveller traveller = new Traveller("小明");
        // 设置旅行者的出行策略
        traveller.setVehicle(vehicle1);
        // 旅行者使用出行策略
        traveller.travel();

        // 改变旅行者的出行策略
        traveller.setVehicle(vehicle2);
        // 旅行者使用出行策略
        traveller.travel();

        // 改变旅行者的出行策略
        traveller.setVehicle(vehicle3);
        // 旅行者使用出行策略
        traveller.travel();
    }
}

测试结果:

出行策略测试结果

5.分析

  • 策略模式使用到的设计原则:(来源于《Head First 设计模式》)

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

      将出行策略单独提取出来建立一组新类

    • 针对接口编程,而不是针对实现编程。

      将出行策略放在分开的类中,此类专门提供某策略的实现。

    • 多用组合,少用继承。

      将策略类组合到出行者类中,而不是出行者直接继承自IVehicle接口。

  • 使用场景:

    • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
    • 一个系统需要动态地在几种算法中选择一种。
    • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • 优缺点:

    • 优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
    • 缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

结尾词

策略模式算是相当简单的一种模式了,也是《Head First 设计模式》中第一个介绍的模式。

它将策略类和环境类分离开来,使OO设计更加简洁明了,减少了类之间的耦合性,策略类的增加和减少均不会影响到环境类。

这也是我设计模式的第一篇文章,希望后面能有时间继续写下去。如果不当之处,请大家批评斧正。

推荐阅读