前言:由业务驱动对设计模式的需求会让学习起来更加深刻。
一、策略模式
因为业务需要,接入不同的推广商IOS设备积分墙功能,业务流程为,我方提供一个判断IOS设备IDFA是否存在的排重接口,一个点击上报IDFA接口;
1、推广商调用排重接口确认该设备在我方还未存在则允许用户做任务
2、做任务过程调用点击上报idfa接口,我方记录点击上报的idfa
3、当用户下载我方app比启动激活后,由我方app调用idfa激活接口
4、收到该idfa激活后回到对应的推广商接口
需要整合的点是:各个推广商回调接口的返回结果定义不一致,有的返回字符串“OK”表示成功,有的返回json格式{"success":true}表示成功。如果在激活方法中通过if判断不同的推广商处理不同的返回接口,代码不易扩展,而且新加推广商必须修改激活接口,容易导致老逻辑出问题。决定用策略模式,策略模式具体类图如下
通过上图可以看出策略模式有以下角色构成:
1、抽象策略(Strategy)角色:抽象策略角色由抽象类或接口来承担,它给出具体策略角色需要实现的接口;
2、具体策略(ConcreteStrategy)角色:实现封装了具体的算法或行为;
3、场景(Context)角色:持有抽象策略类的引用。
具体代码实现:
1、抽象策略(渠道)Channel
public interface Channel {
public boolean callBack(String callBackUrl);
}
2、封装一个http统一请求的继承类(这一步看业务是否需要)
public abstract class BaseChannel implements Channel {
private Logger logger = Logger.getLogger(BaseChannel.class);
public abstract boolean callBack(String callBackUrl);
private HttpClientUtil httpClient = new HttpClientUtil() ;
/**回调
* @param url
*/
public String httpPostRequest(String url){
try {
String result = httpClient.postHttpRequest(url, null);
logger.error("app推广激活回调result:"+result);
return result;
} catch (Exception e) {
logger.error("", e);
}
return null;
}
}
3、具体推广渠道回调,目前只写了两个,其他推广渠道一样接口,callBack方法具体实现不一致
public class YYYChannel extends BaseChannel {
private Logger logger = Logger.getLogger(BaseChannel.class);
@Override
public boolean callBack(String callBackUrl) {
boolean result = false;
try {
String resultStr = this.httpPostRequest(callBackUrl);
if(StringUtils.isNotBlank(resultStr)){
JSONObject jsonObj = JSON.parseObject(resultStr);
String success = (String)jsonObj.get("success");
if("true".equalsIgnoreCase(success)){
result = true;
}else{
logger.error("调用YYY回调异常:"+result);
}
}else{
logger.error("YYY回调接口返回结果null"+resultStr);
}
} catch (Exception e) {
logger.error("YYY回调解析异常", e);
}
return result;
}
}
public class XYChannel extends BaseChannel {
private Logger logger = Logger.getLogger(BaseChannel.class);
@Override
public boolean callBack(String callBackUrl) {
boolean result = false;
try {
String resultStr = this.httpPostRequest(callBackUrl);
if(StringUtils.isNotBlank(resultStr)){
if(resultStr.equalsIgnoreCase("OK")){
//更新回调状态
result = true;
}else{
logger.error("调用XY回调异常:"+result);
}
}else{
logger.error("XY回调接口返回结果null"+resultStr);
}
} catch (Exception e) {
logger.error("XY回调解析异常", e);
}
return result;
}
}
4、策略引入Context
public class CallContext {
Logger logger = Logger.getLogger(CallContext.class);
private Channel channel;
/**回调
* @param url
* @param key
* @return
*/
public boolean callBack(String callBackUrl,String key){
channel = ChannelFactory.getInstance().getChannel(key);
if(channel == null){
logger.error("获取推广渠道对象异常,key:"+key);
}
return channel.callBack(callBackUrl);
}
}
5、增加了一个工厂类实例化对应的推广渠道(这一步看业务是否需要,不是必须),key为具体渠道的长包名+类名格式:com.test.YYYChannel
public class ChannelFactory {
private static ChannelFactory channelFactory = new ChannelFactory();
/**
* 渠道集合
*/
private static ConcurrentHashMap<String, Channel> channels = new ConcurrentHashMap<String, Channel>();
/**获取渠道处理对象,规定,key为具体渠道类名
* @param key
* @return
*/
public Channel getChannel(String key){
try {
if(!channels.containsKey(key)){
Channel channel = (Channel)Class.forName(key).newInstance();
if(channel != null){
channels.put(key, channel);
}
}
return channels.get(key);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static ChannelFactory getInstance(){
return channelFactory;
}
}
6、测试类,在这里就是激活的接口,部分逻辑
CallContext callContext = new CallContext();
try {
FsourceEnum sourceEnum = FsourceEnum.valueOf(source);
boolean result = callContext.callBack(idfaChannel.getCallBackUrl(), sourceEnum.getInstanceName());
if(result){
//更新回调状态
idfaChannel.setCallStatus(sourceEnum.toString());
idfaService.updateIdfaByIdChannel(idfaChannel);
}
} catch (Exception e) {
e.printStackTrace();
logger.error("未知回调渠道类型:"+source);
}
7、渠道枚举定义:
public enum FsourceEnum{
XY("***","com.test.XYChannel"),
YYY("***","com.test.YYYChannel"),
TEST2("****","com.test.test2Channel");
private String name;
private String instanceName; //渠道类名
private FsourceEnum(String name,String instanceName){
this.name = name;
this.instanceName = instanceName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInstanceName() {
return instanceName;
}
public void setInstanceName(String instanceName) {
this.instanceName = instanceName;
}
}
总结,后续运营部门继续增加推广渠道,只要增加一个渠道枚举定义,和实现的渠道推广即可。虽然前期写多点代码,后续维护起来就不需要这么费劲了。