策略模式(Strategy)

155

策略模式(Strategy)

Strategy策略模式是一种行为设计模式,定义一系列算法,分别封装起来,让它们之间可以相互替换(变化)。此模式使得算法可独立于使用它的客户程序而变化,不会影响到使用算法的客户。

/**
 * 策略模式
 *
 * @author CAI
 * @time 2020/10/28
 */
public class StrategyPattern {

    public static void main(String[] args) {
        ShoppingCart shoppingCart = new ShoppingCart();

        Item item1 = new Item("1000", 24);
        Item item2 = new Item("1001", 45);

        shoppingCart.add(item1);
        shoppingCart.add(item2);

        shoppingCart.pay(new AliPayStrategy("hello@mail.com", "world"));
        shoppingCart.pay(new CreditCardStrategy("Hello", "1000001", "2022/1/31"));
    }
}

/**
 * 支付策略接口
 */
interface PaymentStrategy {

    /**
     * 支付
     *
     * @param amount 金额
     */
    void pay(int amount);
}

/**
 * 信用卡支付
 */
class CreditCardStrategy implements PaymentStrategy {

    /**
     * 持卡名
     */
    private String name;

    /**
     * 卡号
     */
    private String cardNumber;

    /**
     * 过期日期
     */
    private String dateOfExpiry;

    public CreditCardStrategy(String name, String cardNumber, String dateOfExpiry) {
        this.name = name;
        this.cardNumber = cardNumber;
        this.dateOfExpiry = dateOfExpiry;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit card.");
    }
}

/**
 * 支付宝支付
 */
class AliPayStrategy implements PaymentStrategy {

    private String emailId;

    private String password;

    public AliPayStrategy(String emailId, String password) {
        this.emailId = emailId;
        this.password = password;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using AliPay.");
    }
}

/**
 * 消费记录
 */
class Item {

    /**
     * 账单编号
     */
    private String id;

    /**
     * 金额
     */
    private Integer price;

    public Item(String id, Integer price) {
        this.id = id;
        this.price = price;
    }

    public String getId() {
        return id;
    }

    public Integer getPrice() {
        return price;
    }
}

/**
 * 购物卡
 */
class ShoppingCart {

    private List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<>();
    }

    /**
     * 增加
     *
     * @param item 消费记录
     */
    public void add(Item item) {
        this.items.add(item);
    }

    /**
     * 减少
     *
     * @param item 消费记录
     */
    public void remove(Item item) {
        this.items.remove(item);
    }

    /**
     * 计算总额
     *
     * @return 购物总额
     */
    public int calculateTotal() {
        int sum = 0;

        for (Item item : items) {
            sum += item.getPrice();
        }

        return sum;
    }

    /**
     * 支付
     *
     * @param paymentStrategy 支付方式
     */
    public void pay(PaymentStrategy paymentStrategy) {
        int amount = calculateTotal();
        paymentStrategy.pay(amount);
    }
}
  1. 上下文(ShoppingCart)类:维护指向具体策略的引用,仅通过策略接口与该对象进行交互。
  2. 策略(PaymentStrategy)接口:是所有具体策略的通用接口,声明一个上下文用于执行策略的方法。
  3. 具体策略(credit crad/Alipay)类:实现上下文所用算法的各种不同实现。
    • 当上下文需要运行算法时,会通过已连接的策略对象调用执行方法。上下文不清楚其涉及的策略类型与算法的执行方式。
  4. 客户端:创建一个特定策略对象并将其传递给上下文,上下文则会提供一个设置器以便客户端在运行时替换相关的策略。

应用场景

  1. 当希望在对象中使用各种不同的算法变体,并希望在运行时能够切换算法时,可以使用策略模式。
    • 策略模式让你能够将对象关联至可以按不同方式执行特定子任务的不同子对象;从而以间接的方式在运行时更改对象行为。
  2. 当你有许多仅在执行某些行为时略有不同的相似类时,可以使用策略模式。
    • 策略模式能够将不同行为抽取到一个独立类层次结构中,并将原始类组合成同一个,从而减少重复代码。
  3. 如果算法在上下文的逻辑中,使用该模式可以将类的业务逻辑与算法实现细节隔离开。
    • 策略模式能将各种算法的代码、内部数据和依赖关系与其代码隔离开。客户端仅通过一个简单的接口接口就能执行算法,并能在运行时切换。
  4. 当类中使用了复杂条件运算时,希望在同一算法的不同切换时,可以使用策略模式。
    • 策略模式将所有继承自同样接口的算法抽离到独立的类中,不需要使用条件语句判断;原始对象并不实现所有算法的变体,而是将执行工作委派给其中一个独立的算法对象。

优缺点

  • 优点:
    • 可以在运行时,切换对象内的算法。
    • 可以将算法的实现和使用算法的代码隔离开来。
    • 可以使用组合来替代继承。
    • 无需对上下文进行修改就可以引入新的策略,满足开闭原则。
  • 缺点:
    • 如果算法极少发生改变,使用该模式会让程序变得复杂。
    • 客户端需知晓策略间的不同,需要选择合适的策略。

在 Java 中的应用

Java的核心库为例:

  1. java.util.Comparator: compare()