在上篇文章模式方法里我们谈到”开闭原则”,今天来看遵循”开闭原则”的另一种设计模式——策略模式。
策略模式准备了一组算法,并将每一个算法封装起来,使算法之间可以互换。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。下面就以实名认证调用第三方实名渠道的实现来讲解策略模式实例的结构,而且这个实例结合运用了模版方法。
策略模式的实现代码(entity包下的实体类略去了):
RealNameAuthService
package realnameAuth.service;
import realnameAuth.entity.RealNameAuthenticateResult;
public interface RealNameAuthService {
// 进行实名认证
public RealNameAuthenticateResult authenticate(String realName,
String identity);
}
这个只定义了authenticate()方法。
AbstractRealNameAuthService
package realnameAuth;
import realnameAuth.entity.RealNameAuthRequest;
import realnameAuth.entity.RealNameAuthenticateResult;
import realnameAuth.service.RealNameAuthService;
public abstract class AbstractRealNameAuthService implements
RealNameAuthService {
@Override
public RealNameAuthenticateResult authenticate(String realName,
String identity) {
System.out.println("realName:" + realName + ",identity:" + identity);
RealNameAuthenticateResult realNameAuthenticateResult = new RealNameAuthenticateResult();
// 准备请求参数
RealNameAuthRequest realNameAuthRequest = prepareRequest(realName,
identity);
// 是否准许调用第三方接口
if (isAllowRemoteCall()) {
try {
// 调用第三方实名认证接口
String realNameAuthResponse = authRequest(realNameAuthRequest);
// 对返回结果进行解析
realNameAuthenticateResult = parseResponse(realNameAuthResponse);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return realNameAuthenticateResult;
};
/**
* 准备请求参数
*
* @param realName
* @param identity
* @return
*/
protected abstract RealNameAuthRequest prepareRequest(String realName,
String identity);
/**
* 进行实名认证
*
* @param realNameAuthRequest
* @return
*/
protected abstract String authRequest(
RealNameAuthRequest realNameAuthRequest) throws Exception;
/**
* 解析实名认证响应信息
*
* @param realNameAuthResponse
* @return
*/
protected abstract RealNameAuthenticateResult parseResponse(
String realNameAuthResponse);
/**
* 是否允许远程调用
*
* @return
*/
protected boolean isAllowRemoteCall() {
return true;
}
}
这个是抽象策略类,它实现了RealNameAuthService接口的authenticate()方法,这个方法体现了”模版方法”的思想,将实名的流程固定下来,包括准备请求参数、是否准许调用第三方接口、进行实名认证、解析实名认证响应信息等。而这些抽象方法都由继承这个抽象策略类的算法实现类去实现,例如下方贴出的RealNameAuthServiceImplD类。
CompositeRealNameAuthServiceImpl
package realnameAuth.service.impl;
import java.util.HashMap;
import java.util.Map;
import realnameAuth.entity.RealNameAuthChannelConfig;
import realnameAuth.entity.RealNameAuthenticateResult;
import realnameAuth.entity.RealnameAuthDic.RealNameAuthChannel;
import realnameAuth.service.RealNameAuthService;
public class CompositeRealNameAuthServiceImpl implements RealNameAuthService {
public Map<RealNameAuthChannel, RealNameAuthService> realNameAuthServiceMap;
public CompositeRealNameAuthServiceImpl() {
realNameAuthServiceMap = new HashMap<RealNameAuthChannel, RealNameAuthService>();
realNameAuthServiceMap.put(RealNameAuthChannel.A,
new RealNameAuthServiceImplA());
realNameAuthServiceMap.put(RealNameAuthChannel.B,
new RealNameAuthServiceImplB());
realNameAuthServiceMap.put(RealNameAuthChannel.C,
new RealNameAuthServiceImplC());
realNameAuthServiceMap.put(RealNameAuthChannel.D,
new RealNameAuthServiceImplD());
}
@Override
public RealNameAuthenticateResult authenticate(String realName,
String identity) {
// 获取开启的第一优先级实名渠道
RealNameAuthChannelConfig realNameAuthChannelConfig = findFirstChannel();
String channelName = realNameAuthChannelConfig.getChannelName();
// 获取认证通道枚举
RealNameAuthChannel realNameAuthChannel = RealNameAuthChannel
.getChannel(channelName);
// 获取实名认证渠道对象
RealNameAuthService realNameAuthService = realNameAuthServiceMap
.get(realNameAuthChannel);
// 进行实名认证
RealNameAuthenticateResult realNameAuthenticateResult = realNameAuthService
.authenticate(realName, identity);
return realNameAuthenticateResult;
}
private RealNameAuthChannelConfig findFirstChannel() {
RealNameAuthChannelConfig realNameAuthChannelConfig = new RealNameAuthChannelConfig();
realNameAuthChannelConfig.setChannelName("Channel D");
return realNameAuthChannelConfig;
}
public static void main(String[] args) {
RealNameAuthService realNameAuthService = new CompositeRealNameAuthServiceImpl();
RealNameAuthenticateResult realNameAuthenticateResult = realNameAuthService
.authenticate("费德勒", "320802199001220528");
System.out.println(realNameAuthenticateResult.getResult() == 0 ? "实名失败"
: "实名成功");
}
}
这是具体策略类,它根据一些因素,比如费率、第三方是否可用、认证成功率等,决定具体启用哪个渠道。所有的算法实现类被组织在了realNameAuthServiceMap里,实现类可以new出来,亦可以从容器中取(从容器中取就需要实现ApplicationContextAware接口,容器启动时就记载该Map),见下例。
@PostConstruct
public void init() {
Map<String, RealNameAuthService> map = applicationContext
.getBeansOfType(RealNameAuthService.class);
realNameAuthServiceMap = new HashMap<RealNameAuthChannel, RealNameAuthService>();
realNameAuthServiceMap.put(RealNameAuthChannel.A,
map.get("realNameAuthServiceImplA"));
realNameAuthServiceMap.put(RealNameAuthChannel.B,
map.get("realNameAuthServiceImplB"));
realNameAuthServiceMap.put(RealNameAuthChannel.C,
map.get("realNameAuthServiceImplC"));
realNameAuthServiceMap.put(RealNameAuthChannel.D,
map.get("realNameAuthServiceImplD"));
}
RealNameAuthServiceImplD
package realnameAuth.service.impl;
import realnameAuth.AbstractRealNameAuthService;
import realnameAuth.entity.RealNameAuthRequest;
import realnameAuth.entity.RealNameAuthenticateResult;
public class RealNameAuthServiceImplD extends AbstractRealNameAuthService {
@Override
protected RealNameAuthRequest prepareRequest(String realName,
String identity) {
System.out.println("RealNameAuthServiceImplD prepareRequest...");
return null;
}
@Override
protected String authRequest(RealNameAuthRequest realNameAuthRequest)
throws Exception {
System.out.println("RealNameAuthServiceImplD authRequest...");
return null;
}
@Override
protected RealNameAuthenticateResult parseResponse(
String realNameAuthResponse) {
System.out.println("RealNameAuthServiceImplD parseResponse...");
RealNameAuthenticateResult result = new RealNameAuthenticateResult();
result.setResult(1);
return result;
}
}
这是具体的算法实现类,类似的还有RealNameAuthServiceImplA、RealNameAuthServiceImplB、RealNameAuthServiceImplC,这个就不一一贴出了。
执行结果如下:
realName:费德勒,identity:320802199001220528
RealNameAuthServiceImplD prepareRequest...
RealNameAuthServiceImplD authRequest...
RealNameAuthServiceImplD parseResponse...
实名成功
运行结果表明,当CompositeRealNameAuthServiceImpl类中findFirstChannel()的方法确定了使用哪个实名渠道,最终就会去调用该实名渠道的实现类。当某一个渠道的逻辑需要修改,那只需要修改该渠道的实现;当新增一个渠道,那就新增一个实现类,同时put到realNameAuthServiceMap里。这样无论哪一个渠道发生变动都不会影响到其它渠道,测试时也就只需要修改这一实现类。