设计模式实战-策略模式

一.概述

策略模式,个人理解,当我们的业务逻辑需要走不同的分支,而具体走哪个分支由业务决定,这时我们就需要用到策略模式.比如,我网购买一件衣服,付款时,我可以选择用支付宝,也可以用微信.那么付款方式就是我的业务需要走的不同的业务逻辑.

策略模式是我认为平时开发时用到的最为常见的设计模式之一,所以一定要好好掌握.
策略模式可以省略大部分 if/else ,并可以使代码变得易于拓展

二. 实战应用

针对上面的例子,做思考:

  1. 针对不同的付款方式,首先,他们肯定要有一个付款的接口,这个接口要有不同的实现类.
  2. 客户端在选择付款方式,只需提供该付款方式的唯一标识用于识别,然后调用这个统一的接口.
  3. 系统会根据该唯一标识,走该付款方式的实现类,所以需要提供一个选择分支的方法.

做了以上思考,现在开始编程:

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);
    }

同样入参是客户端传过来的,注解方式实现策略模式完成

四.结语

实现策略模式的方法很多,更多的还是需要我们将策略模式的思想应用到项目中,来追求更完美的代码,如果您还有更好的实现策略模式的方法,欢迎分享出来.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值