ikki@github.io:~$

策略模式

策略模式

策略模式属于 23 中设计模式中 行为模式 的一种, 其目的是封装类似算法的不同实现, 根据策略选择其中一种去执行

关键

特点

  • 一个行为具有不同的实现方法
  • 不同的实现方法之间的选择是互斥的,只能使用一种

优点

  • 干掉语法中的 if...else..., 方便维护
  • 可以任意扩展相同的实现

实践的关键

  • 不同的实现方式需要抽象出一个实现接口

业务场景

原始实现

我们有一个支付场景的实现,用户可以选择 支付宝微信支付花呗 进行支付

那么,我们的实现可能像下面这个样子


public class AliPay {

    public void spendMoney(BigDecimal amount) {
        System.out.println("用 支付宝 支付");
    }

    public void ohNo() {
        System.out.println("下个月要吃土了");
    }

}

public class WeChatPay {

    public void costMoney(BigDecimal amount) {
        System.out.println("使用 微信 支付");
    }

    public void notPay() {
        System.out.println("东西太贵了");
    }

}

public class JustPay {

    public void huaQian(BigDecimal amount) {
        System.out.println("使用 花呗 支付");
    }

    public void buMaiLe() {
        System.out.println("冲动型消费是不科学的");
    }

}

这里故意胡乱命名 以模仿在项目中各种支付方式的乱糟糟的实现

优化实现

不难看出,这里所有的支付场景 都有2个动作,一个是 支付 、一个是取消支付

那么,我们可以抽象出以下接口并重构已有的实现类

public interface PaymentStrategy {
    
    public void payment(BigDecimal amount);
    
    public void cancel();
     
}

这样,我们的各种支付实现就看起来舒服多了

public class AliPayStrategy implements PaymentStrategy {

    @Override
    public void payment(BigDecimal amount) {
        System.out.println("用 支付宝 支付");
    }

    @Override
    public void cancel() {
        System.out.println("取消 支付宝 支付");
    }

}

public class WeChatStrategy implements PaymentStrategy {

    @Override
    public void payment(BigDecimal amount) {
        System.out.println("使用 微信 支付");
    }

    @Override
    public void cancel() {
        System.out.println("取消 微信 支付");
    }

}

public class JustPayStrategy implements PaymentStrategy {

    @Override
    public void payment(BigDecimal amount) {
        System.out.println("使用 花呗 支付");
    }

    @Override
    public void cancel() {
        System.out.println("取消 花呗 支付");
    }

}

关键点

那么,如何将这些策略放到一起,并方便查找和抉择呢

首先我们肯定希望通过接口调用,而不是显式的声明策略的具体实现类


// 我们不期望的实现方式
PaymentStrategy paymentStrategy = new AliPay();

paymentStrategy.payment(new BigDecimal("3.14"));


// 我们期望的使用方式
PaymentStrategy paymentStrategy == null;
// ... 神秘代码
paymentStrategy.payment(new BigDecimal("3.14"));

我们期望通过一个集合之类的数据结构,拿到我们想用的具体实现,那么Map 是一个不错的选择

/**
 *  "aliPay" --> AliPayStrategy
 *  "wechat" --> WeChatStrategy
 *  "justPay" --> JustPayStrategy
 */
Map<String, PaymentStrategy> paymentStrategy;

那么,我们是否需一个初始化的方法,或者工厂模式之类的去归置这些实现呢?

答案是否定的,这会导致代码变的复杂和不容易维护,我们应该考虑使用 依赖注入 实现,如果你是在使用Spring 框架的话, 这一切将变的非常轻松

/**
 *  "aliPayStrategy" --> AliPayStrategy
 *  "weChatStrategy" --> WeChatStrategy
 *  "justPayStrategy" --> JustPayStrategy
 */
@Autowired
Map<String, PaymentStrategy> paymentStrategy;

不难发现, 这里key 就是Spring bean 的名称,自然,我们可以联想到

@Service("aliPay")
public class AliPayStrategy implements PaymentStrategy {
//... 省略
}

@Service("weChat")
public class WeChatStrategy implements PaymentStrategy {
//... 省略
}

@Service("justPay")
public class JustPayStrategy implements PaymentStrategy {
//... 省略
}

这样,可以轻松的定义策略名称并使用依赖注入完成 策略模式 的实现