设计模式之策略模式(Strategy)

一、概述

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的类而变化。

二、适用性

1.许多相关的类仅仅是行为有异。"策略"提供了一种用多个行为中的一个行为来配置一个类的方法。

2.需要使用一个算法的不同变体。

3.使用算法的类不应该知道数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。

4.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。 将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

三、参与者

1.Strategy 定义所有支持的算法的公共接口。Context使用这个接口来调用某个ConcreteStrategy定义的算法。

2.ConcreteStrategy实现Strategy接口实现某具体算法。

3.Context 用一个ConcreteStrategy对象来配置。 维护一个Strategy对象的引用。 可定义一个接口让Strategy访问它的数据。

四、类图

五、示例

Strategy

java 复制代码
public interface Strategy {
    void method();
}

ConcreteStrategy

java 复制代码
public class StrategyImplA implements Strategy{
    @Override
    public void method() {
        System.out.println("这是第一个实现");
    }
}
java 复制代码
public class StrategyImplB implements Strategy{
    @Override
    public void method() {
        System.out.println("这是第二个实现");
    }
}
java 复制代码
public class StrategyImplC implements Strategy{
    @Override
    public void method() {
        System.out.println("这是第三个实现");
    }
}

Context

java 复制代码
public class Context {

    private Strategy stra;

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

    public void doMethod() {
        stra.method();
    }
}

自测

java 复制代码
   @Test
   public void strategyTest() {
      Context ctx = new Context(new StrategyImplA());
      ctx.doMethod();
      ctx = new Context(new StrategyImplB());
      ctx.doMethod();
      ctx = new Context(new StrategyImplC());
      ctx.doMethod();
  }

自测结果

bash 复制代码
Connected to the target VM, address: '127.0.0.1:7252', transport: 'socket'
这是第一个实现
这是第二个实现
这是第三个实现
Disconnected from the target VM, address: '127.0.0.1:7252', transport: 'socket'

六、实践

支付策略模式相关文件目录

PayStrategy

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月25日
 * @Description
 */
public interface PayStrategy {
    /**
     * 支付
     */
    void pay(BigDecimal money);
    /**
     * 申请退款
     */
    void refund(BigDecimal money);
    /**
     * 查询退款
     */
    void refundQuery();
    /**
     * 关闭订单
     */
    void close();
    /**
     * 查询订单
     */
    void query();
}

阿里支付相关-AliPayStrategy

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月25日
 * @Description
 */
@Service("AliPayStrategy")
public class AliPayStrategy implements PayStrategy {
     /**
     * 根据app_id获取auth_code
     */
    public String getAuthCode(){
        return aliAuthUrl +"?app_id="+ali_app_id+"&redirect_uri="+ali_redirect_uri;
    }
    
    /**
     * 根据auth_code获取auth_token或刷新auth_token    grant_type:换取令牌    authorization_code;刷新令牌refresh_token
     */
    public String getAuthToken(String grant_type ,String auth_code)  {
        AlipayClient alipayClient = new DefaultAlipayClient(aliServiceUrl , ali_app_id , alipay_private_key,json, AboutCharset.UTF8,alipay_public_key,signType);
        AlipayOpenAuthTokenAppRequest request = new AlipayOpenAuthTokenAppRequest();
        request.setBizContent(
                //换取令牌
                "{\"grant_type\":"+grant_type+"\",\"code\":"+ auth_code +"}");
        AlipayOpenAuthTokenAppResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        //略过判断response。获取appAuthToken
        String appAuthToken = response.getAppAuthToken();
        //查询授权信息
        //AlpayOpenAuthTokenAppQuery(appAuthToken);
        return appAuthToken;
    }

    @Override
    public void pay(BigDecimal money) {
       System.out.printf("支付宝支付了:%.2f元%n", money);
    }

    @Override
    public void refund(BigDecimal money) {
        System.out.printf("支付宝申请退款:%.2f元", money);
    }

    @Override
    public void refundQuery() {
        System.out.println("支付宝申请退款查询");
    }

    @Override
    public void close() {
        System.out.println("支付宝关闭订单");
    }

    @Override
    public void query() {
        System.out.println("支付宝查询订单");
    }
}

微信支付相关-WeChatPayStrategy

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月25日
 * @Description
 */
@Service("WeChatPayStrategy")
public class WeChatPayStrategy implements PayStrategy {
    @Override
    public void pay(BigDecimal money) {
        System.out.printf("微信支付了:%.2f元%n", money);
    }

    @Override
    public void refund(BigDecimal money) {
        System.out.printf("微信申请退款:%.2f元", money);
    }

    @Override
    public void refundQuery() {
        System.out.println("微信申请退款查询");
    }

    @Override
    public void close() {
        System.out.println("微信关闭订单");
    }

    @Override
    public void query() {
        System.out.println("微信查询订单");
    }
}

建行龙支付

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月26日
 * @Description
 */
@Service("CCBPayStrategy")
public class CCBPayStrategy implements PayChannelStrategy {
    String bankURL="https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_00_ENPAY"; //建行支付默认网关
    String branchId = "xxxx"; //分行号固定

    public boolean signVerify(String pubKey, String src, String sign) {
        RSASig rsaSig = new RSASig();
        rsaSig.setPublicKey(pubKey);
        return rsaSig.verifySigature(sign,src);
    }

    @Override
    public void pay(BigDecimal money) {
        System.out.printf("建行龙支付支付了:%.2f元%n", money);
    }

    @Override
    public void refund(BigDecimal money) {
        System.out.printf("建行龙支付申请退款:%.2f元", money);
    }

    @Override
    public void refundQuery() {
        System.out.println("建行龙支付申请退款查询");
    }

    @Override
    public void close() {
        System.out.println("建行龙支付关闭订单");
    }

    @Override
    public void query() {
        System.out.println("建行龙支付查询订单");
    }
}

支付工厂

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月26日
 * @Description
 */
@Component
public class PayStrategyFactory {

    private PayStrategyFactory(){}
    private static Map<String, PayChannelStrategy> map = new ConcurrentHashMap<>();

   static {
        map.put("wechat", new WeChatPayStrategy());
        map.put("ali", new AliPayStrategy());
        map.put("ccb", new CCBPayStrategy());
    }

    public static PayChannelStrategy getPayStrategy(String payType){
        return map.get(payType);
    }
}

自测

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月25日
 * @Description
 */
public class TestStrategy {
    @Resource
    private PayStrategyFactory payStrategyFactory;
    @Test
    public void strategyTest() throws Exception {
       PayChannelStrategy payStrategy = payStrategyFactory.getPayStrategy("ali");
        if(null == payStrategy){
            throw new Exception( "支付类型为空") ;
        }
        BigDecimal money = new BigDecimal("12.01");
        payStrategy.pay(money);
    }
}

测试结果

java 复制代码
Connected to the target VM, address: '127.0.0.1:11660', transport: 'socket'
支付宝支付了:12.01元
Disconnected from the target VM, address: '127.0.0.1:11660', transport: 'socket'

通过使用策略模式抽取公共接口,有统一的对外接口,在路由层通过查库获取配置的支付通道、使用不同的支付策略,实现业务和支付的分离,只用关注支付的不同实现,不用太多关注路由配置业务。

后续:PayStrategyFactory 支付工厂的改进。

java 复制代码
/**
 * @author lyon
 * @createTime 2018年04月27日
 * @Description PayStrategy交由spring管理,使用动态代理,实现工厂支付接口
 *              使用@Scope("prototype")注解,可以通知Spring把被注解的Bean变成多例,防止线程不安全问题
 */
@Configuration
public class PayContextConfig {
    @Bean
    public ServiceLocatorFactoryBean serviceLoaderFactoryBean(){
        ServiceLocatorFactoryBean loaderFactoryBean = new ServiceLocatorFactoryBean();
        loaderFactoryBean.setServiceLocatorInterface(PayChannelStrategy.class);
        return loaderFactoryBean;
    }

    @Bean("wechat")
    @Scope("prototype")
    public WeChatPayStrategy weChatPayStrategy(){
        return  new WeChatPayStrategy();
    }

    @Bean("ali")
    @Scope("prototype")
    public AliPayStrategy aliPayStrategy(){
        return  new AliPayStrategy();
    }

    @Bean("ccb")
    @Scope("prototype")
    public CCBPayStrategy ccbPayStrategy(){
        return  new CCBPayStrategy();
    }
}

测试:

java 复制代码
@Test
public void strategyTest() throws Exception {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PayContextConfig.class);
    try {
            PayChannelStrategy payStrategy = (PayChannelStrategy) applicationContext.getBean("ccb");
            BigDecimal money = new BigDecimal("12.01");
            payStrategy.pay(money);
        }catch (Exception e){
            throw new Exception( "支付类型为空") ;
     }
}

测试结果:

java 复制代码
14:50:50.998 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'serviceLoaderFactoryBean'
建行龙支付支付了:12.01元

PS:不止微服务可高并发,单机也可高并发。微服务还是单机,跟业务和团队现状有关。架构是跟着业务需求和现实问题持续演进。有人说马斯克推特单体,但是推特有钱啊、另外推特除了300字等推文、图片和评论业务,还有更多其他的业务吗?

相关推荐
LB_bei3 小时前
设计模式-行为型模式-命令模式
设计模式·命令模式
心之语歌7 小时前
设计模式 享元模式(Flyweight Pattern)
java·设计模式·享元模式
我要20010 小时前
工厂模式,策略模式,代理模式,单例模式在项目中的应用
单例模式·代理模式·策略模式
G皮T10 小时前
【设计模式】创建型模式(三):单例模式
单例模式·设计模式·singleton
未来可期LJ18 小时前
【C++ 设计模式】单例模式的两种懒汉式和饿汉式
c++·单例模式·设计模式
丶白泽1 天前
重修设计模式-结构型-组合模式
设计模式·组合模式
yunhuibin1 天前
ffmpeg面向对象——参数配置秘密探索及其设计模式
学习·设计模式·ffmpeg
_祝你今天愉快1 天前
技术成神之路:设计模式(十四)享元模式
java·设计模式
蔚一1 天前
Java设计模式—面向对象设计原则(三) -----> 依赖倒转原则DIP(完整详解,附有代码+案例)
java·开发语言·设计模式·intellij-idea·依赖倒置原则
丶白泽1 天前
重修设计模式-概览
java·设计模式