学习设计模式——策略模式

575 阅读3分钟

概述

策略模式:(Strategy Design Pattern)定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。

策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。

打个比方说,我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。

何时使用:

  • 一个系统有许多许多类,而区分它们的只是他们直接的行为。

UML 类图:

image.png

角色组成:

  1. Context上下文: 也叫Context封装角色,起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
  2. 抽象策略角色:抽象策略角色,是对策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
  3. 具体策略角色: 用于实现抽象策略中的操作,即实现具体的算法。

通用代码

Context上下文:

public class Context {

    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    // 上下文接口
    public void contextInterface() {
        strategy.algorithmInterface();
    }

}

抽象策略角色:

public interface Strategy {

    // 算法方法
    void algorithmInterface();

}

具体策略角色

public class ConcreteStrategyA implements Strategy {
    @Override
    public void algorithmInterface() {
        System.out.println("算法A实现");
    }
}


public class ConcreteStrategyB implements Strategy {
    @Override
    public void algorithmInterface() {
        System.out.println("算法B实现");
    }
}


public class ConcreteStrategyC implements Strategy {
    @Override
    public void algorithmInterface() {
        System.out.println("算法C实现");
    }
}

使用:

public class Test {
    public static void main(String[] args) {

        Context context;

        context = new Context(new ConcreteStrategyA());
        context.contextInterface();

        context = new Context(new ConcreteStrategyB());
        context.contextInterface();

        context = new Context(new ConcreteStrategyC());
        context.contextInterface();

    }
}

结果:

算法A实现
算法B实现
算法C实现

如何利用策略模式避免分支判断

看这样一个案例:给不同的订单不同的折扣策略。不使用策略模式时,我们是这样写代码的。

public class OrderService {
    public double discount(Order order) {
        double discount = 0.0;
        OrderType type = order.getType();
        if (type.equals(OrderType.NORMAL)) {    // 普通订单
            // 省略折扣计算算法代码
        } else if (type.equals(OrderType.GROUPON)) {    // 团购订单
            // 省略折扣计算算法代码
        } else if (type.equals(OrderType.PROMOTION)) {  // 促销订单
            // 省略折扣计算算法代码
        }
        return discount;
    }
}


public enum OrderType {

    NORMAL,     // 普通订单

    GROUPON,    // 团购订单

    PROMOTION   // 促销订单

}


public class Order {
    OrderType type;

    public Order(OrderType type) {
        this.type = type;
    }

    public OrderType getType() {
        return type;
    }

    public void setType(OrderType type) {
        this.type = type;
    }
}

如何来移除掉分支判断逻辑呢?那策略模式就派上用场了。我们使用策略模式对上面的代码重构,将不同类型订单的打折策略设计成策略类,并由工厂类来负责创建策略对象。具体的代码如下所示:

public interface DiscountStrategy {

    double calDiscount(Order order);

}


public class NormalDiscountStrategy implements DiscountStrategy {
    @Override
    public double calDiscount(Order order) {
        System.out.println("经过计算,该订单不打折!");
        return 1;
    }
}


public class GrouponDiscountStrategy implements DiscountStrategy {
    @Override
    public double calDiscount(Order order) {
        System.out.println("经过计算,该订单打八折!");
        return 0.8;
    }
}


public class PromotionDiscountStrategy implements DiscountStrategy {
    @Override
    public double calDiscount(Order order) {
        System.out.println("经过计算,该订单打七折!");
        return 0.7;
    }
}
public class DiscountStrategyFactory {
    private static final Map<OrderType, DiscountStrategy> strategies = new HashMap<>();

    static {
        strategies.put(OrderType.NORMAL, new NormalDiscountStrategy());
        strategies.put(OrderType.GROUPON, new GrouponDiscountStrategy());
        strategies.put(OrderType.PROMOTION, new PromotionDiscountStrategy());
    }

    public static DiscountStrategy getDiscountStrategy(OrderType type) {
        return strategies.get(type);
    }
}
public class OrderService {
    public double discount(Order order) {
        OrderType type = order.getType();
        DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type);
        return discountStrategy.calDiscount(order);
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        orderService.discount(new Order(OrderType.NORMAL));
        orderService.discount(new Order(OrderType.GROUPON));
        orderService.discount(new Order(OrderType.PROMOTION));
    }
}

测试结果:

经过计算,该订单不打折!
经过计算,该订单打八折!
经过计算,该订单打七折!

测试用例的两种写法结果都是一样的

总结

策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。

策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。

  • 策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。
  • 策略的创建由工厂类来完成,封装策略创建的细节。
  • 策略模式包含一组策略可选,客户端代码如何选择使用哪个策略,有两种确定方法:编译时静态确定和运行时动态确定。其中,“运行时动态确定”才是策略模式最典型的应用场景。