一、概述
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的类而变化。
二、适用性
1.许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
2.需要使用一个算法的不同变体。
3.使用算法的类不应该知道数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。 将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
三、参与者
1.Strategy 定义所有支持的算法的公共接口。Context使用这个接口来调用某个ConcreteStrategy定义的算法。
2.ConcreteStrategy实现Strategy接口实现某具体算法。
3.Context 用一个ConcreteStrategy对象来配置。 维护一个Strategy对象的引用。 可定义一个接口让Strategy访问它的数据。
四、类图
五、示例
Strategy
public interface Strategy {void method();
}
ConcreteStrategy
public class StrategyImplA implements Strategy{@Overridepublic void method() {System.out.println("这是第一个实现");}
}
public class StrategyImplB implements Strategy{@Overridepublic void method() {System.out.println("这是第二个实现");}
}
public class StrategyImplC implements Strategy{@Overridepublic void method() {System.out.println("这是第三个实现");}
}
Context
public class Context {private Strategy stra;public Context(Strategy stra) {this.stra = stra;}public void doMethod() {stra.method();}
}
自测
@Testpublic void strategyTest() {Context ctx = new Context(new StrategyImplA());ctx.doMethod();ctx = new Context(new StrategyImplB());ctx.doMethod();ctx = new Context(new StrategyImplC());ctx.doMethod();}
自测结果
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
/*** @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
/*** @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。获取appAuthTokenString appAuthToken = response.getAppAuthToken();//查询授权信息//AlpayOpenAuthTokenAppQuery(appAuthToken);return appAuthToken;}@Overridepublic void pay(BigDecimal money) {System.out.printf("支付宝支付了:%.2f元%n", money);}@Overridepublic void refund(BigDecimal money) {System.out.printf("支付宝申请退款:%.2f元", money);}@Overridepublic void refundQuery() {System.out.println("支付宝申请退款查询");}@Overridepublic void close() {System.out.println("支付宝关闭订单");}@Overridepublic void query() {System.out.println("支付宝查询订单");}
}
微信支付相关-WeChatPayStrategy
/*** @author lyon* @createTime 2018年04月25日* @Description*/
@Service("WeChatPayStrategy")
public class WeChatPayStrategy implements PayStrategy {@Overridepublic void pay(BigDecimal money) {System.out.printf("微信支付了:%.2f元%n", money);}@Overridepublic void refund(BigDecimal money) {System.out.printf("微信申请退款:%.2f元", money);}@Overridepublic void refundQuery() {System.out.println("微信申请退款查询");}@Overridepublic void close() {System.out.println("微信关闭订单");}@Overridepublic void query() {System.out.println("微信查询订单");}
}
建行龙支付
/*** @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);}@Overridepublic void pay(BigDecimal money) {System.out.printf("建行龙支付支付了:%.2f元%n", money);}@Overridepublic void refund(BigDecimal money) {System.out.printf("建行龙支付申请退款:%.2f元", money);}@Overridepublic void refundQuery() {System.out.println("建行龙支付申请退款查询");}@Overridepublic void close() {System.out.println("建行龙支付关闭订单");}@Overridepublic void query() {System.out.println("建行龙支付查询订单");}
}
支付工厂
/*** @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);}
}
自测
/*** @author lyon* @createTime 2018年04月25日* @Description*/
public class TestStrategy {@Resourceprivate PayStrategyFactory payStrategyFactory;@Testpublic void strategyTest() throws Exception {PayChannelStrategy payStrategy = payStrategyFactory.getPayStrategy("ali");if(null == payStrategy){throw new Exception( "支付类型为空") ;}BigDecimal money = new BigDecimal("12.01");payStrategy.pay(money);}
}
测试结果
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 支付工厂的改进。
/*** @author lyon* @createTime 2018年04月27日* @Description PayStrategy交由spring管理,使用动态代理,实现工厂支付接口* 使用@Scope(“prototype”)注解,可以通知Spring把被注解的Bean变成多例,防止线程不安全问题*/
@Configuration
public class PayContextConfig {@Beanpublic 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();}
}
测试:
@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( "支付类型为空") ;}
}
测试结果:
14:50:50.998 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'serviceLoaderFactoryBean'
建行龙支付支付了:12.01元
PS:不止微服务可高并发,单机也可高并发。微服务还是单机,跟业务和团队现状有关。架构是跟着业务需求和现实问题持续演进。有人说马斯克推特单体,但是推特有钱啊、另外推特除了300字等推文、图片和评论业务,还有更多其他的业务吗?