文章目录
一.概述
策略模式,个人理解,当我们的业务逻辑需要走不同的分支,而具体走哪个分支由业务决定,这时我们就需要用到策略模式.比如,我网购买一件衣服,付款时,我可以选择用支付宝,也可以用微信.那么付款方式就是我的业务需要走的不同的业务逻辑.
策略模式是我认为平时开发时用到的最为常见的设计模式之一,所以一定要好好掌握.
策略模式可以省略大部分 if/else ,并可以使代码变得易于拓展
二. 实战应用
针对上面的例子,做思考:
- 针对不同的付款方式,首先,他们肯定要有一个付款的接口,这个接口要有不同的实现类.
- 客户端在选择付款方式,只需提供该付款方式的唯一标识用于识别,然后调用这个统一的接口.
- 系统会根据该唯一标识,走该付款方式的实现类,所以需要提供一个选择分支的方法.
做了以上思考,现在开始编程:
1.最基本的例子
(1) 付款接口与子类
首先是统一接口:
public interface PayStrategy {
/**
* @param money 需要付款的金额
*/
boolean pay(int money);
/**
* @param accountNo 需要查询的账户
*/
BigDecimal queryBalance(String accountNo);
}
接下来是各个付款方式的子类:
/**
* 支付宝支付
*/
public class AliPayStrategy implements PayStrategy{
@Override
public boolean pay(int money) {
System.out.println("支付宝支付成功");
return true;
}
@Override
public BigDecimal queryBalance(String accountNo) {
System.out.println("支付宝余额10元");
return new BigDecimal(10);
}
}
/**
* 微信支付
*/
public class WechatPayStrategy implements PayStrategy{
@Override
public boolean pay(int money) {
System.out.println("微信支付成功");
return true;
}
@Override
public BigDecimal queryBalance(String accountNo) {
System.out.println("微信余额10元");
return new BigDecimal(10);
}
}
其它支付方式,只需要在这里新增子类就可以.
(2) 提供每种付款方式的标识
这里采用枚举方式
public enum PayEnum {
/**
* 支付宝支付
*/
AliPay("aliPay",new AliPayStrategy()),
/**
* 微信支付
*/
WechatPay("wechatPay",new WechatPayStrategy());
private final String key;
private final PayStrategy value;
PayEnum(String key, PayStrategy value) {
this.key = key;
this.value = value;
}
public static PayStrategy getValue(String key){
for (PayEnum payEnum : PayEnum.values()){
if (payEnum.key.equals(key)){
return payEnum.value;
}
}
//没有合适key则默认阿里支付
return new AliPayStrategy();
}
}
(3) 根据标识走不同的实现类
public class Test {
public static void main(String[] args) {
//这里是客户端提供的唯一标识
String payType = "alipay";
PayStrategy payStrategy = PayEnum.getValue(payType);
payStrategy.pay(10);
payStrategy.queryBalance("xxx");
}
}
上面只是为了理解策略模式的一个简单的例子,main方法里提供了思路,下面是进阶教程.
三.进阶教程
1. 采用jdk的plugin实现策略模式
同样是上面的例子,我们可以采用spring提供的plugin包实现策略模式
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-metadata</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
(1) 统一付款接口
public interface PayStrategy extends Plugin<PayEnum> {
boolean pay(int money);
}
public enum PayEnum {
ALI_PAY,WECHAT_PAY;
}
不同的是需要继承Plugin抽象方法
(2) 提供配置类
@Configuration
@EnablePluginRegistries({PayStrategy.class})
public class PayTypeConfig {
}
(3) 不同付款方式的子类
@Service
public class AliPayStrategy implements PayStrategy {
@Override
public boolean pay(int money) {
System.out.println("支付宝支付成功");
return true;
}
@Override
public boolean supports(@NonNull PayEnum payEnum) {
return PayEnum.ALI_PAY.equals(payEnum);
}
}
@Service
public class WechatPayStrategy implements PayStrategy {
@Override
public boolean pay(int money) {
System.out.println("微信支付成功");
return true;
}
@Override
public boolean supports(@NonNull PayEnum payEnum) {
return PayEnum.WECHAT_PAY.equals(payEnum);
}
}
注意需要重写 support方法,用来判断走该实现类的条件.
(4) 服务端接口
@Service
public class PayTypeService {
@Resource
PluginRegistry<PayStrategy, PayEnum> registry;
public boolean pay(PayEnum payEnum,int payNumber) {
Optional<PayStrategy> pluginFor = registry.getPluginFor(payEnum);
PayStrategy payStrategy = pluginFor.get();
return payStrategy.pay(payNumber);
}
}
根据枚举获取到对应的实例,并执行pay方法.
(5) 写个测试类
@SpringBootTest(classes = App.class)
public class PayTest {
@Autowired
PayTypeService payTypeService;
@Test
public void name() {
payTypeService.pay(PayEnum.ALI_PAY, 10);
}
}
入参是客户端传过来的,这样就实现了策略模式的基本思路.
2.采用注解的方式实现策略模式
采用注解的方式实现策略模式思路大体都是相同的,下面看具体实现
(1) 定义公共接口
public interface PayStrategy {
/**
* @param money 需要付款的金额
*/
boolean pay(int money);
}
(2) 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface PayType {
PayEnum payType();
}
public enum PayEnum {
ALI_PAY,WECHAT_PAY;
}
(3) 定义子类
子类需要用@PayType
标识属于哪一个分支
@PayType
中的标识必须唯一
/**
* 支付宝支付
*/
@PayType(payType = PayEnum.ALI_PAY)
@Service
public class AliPay implements PayStrategy {
@Override
public boolean pay(int money) {
System.out.println("支付宝支付成功");
return true;
}
}
/**
* 微信支付
*/
@PayType(payType = PayEnum.WECHAT_PAY)
@Service
public class WechatPay implements PayStrategy {
@Override
public boolean pay(int money) {
System.out.println("微信支付成功");
return true;
}
}
(4) 选择器选择具体执行的子类
@Service
public class PayTypeSelector {
private Map<PayEnum, PayStrategy> strategyMap;
@Autowired
private void setPayStrategies(List<PayStrategy> payStrategies) {
strategyMap = payStrategies.stream()
.collect(Collectors.toMap(p -> Objects.requireNonNull(AnnotationUtils.findAnnotation(p.getClass(), PayType.class)).payType(), p -> p));
}
public void pay(PayEnum payType,int amount){
PayStrategy payStrategy = strategyMap.get(payType);
payStrategy.pay(amount);
}
}
(5) 写个测试类
@Autowired
PayTypeSelector selector;
@Test
void commentTest() {
selector.pay(PayEnum.ALI_PAY, 10);
}
同样入参是客户端传过来的,注解方式实现策略模式完成
四.结语
实现策略模式的方法很多,更多的还是需要我们将策略模式的思想应用到项目中,来追求更完美的代码,如果您还有更好的实现策略模式的方法,欢迎分享出来.