java 与设计模式学习之策略模式

前言:由业务驱动对设计模式的需求会让学习起来更加深刻。

一、策略模式

因为业务需要,接入不同的推广商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;
		}		
}

总结,后续运营部门继续增加推广渠道,只要增加一个渠道枚举定义,和实现的渠道推广即可。虽然前期写多点代码,后续维护起来就不需要这么费劲了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值