一、概述
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的类而变化。
二、适用性
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字等推文、图片和评论业务,还有更多其他的业务吗?