策略模式属于对象的行为模式。其用意是针对一组算法,将每个算法封装到具备共同接口的独立的类中,从而使得它们能够相互替换。策略模式使得算法能够在不影响到客户端的状况下发生变化。——《JAVA与设计模式》java
下面是策略模式的类图: 算法
在个人工程中,有这样一处场景,在App文章详情页面的正文中有时候会有一些超连接,点击这个连接要求可以跳转到另外约定好的界面,那这个约定是怎样的呢?在此以前咱们先看一下Uri的结构。设计模式
[scheme:][//host:port][path][?query][#fragment]
复制代码
public class DeepLinkUtils {
public static void jumpToTarget(Context context, Uri uri) {
if (uri == null || uri.getHost() == null) {return;}
switch (uri.getHost()) {
case "news":
String newsPath = uri.getPath();
if ("/detail".equals(newsPath)) {
..........
} else if ("/topic".equals(newsPath)) {
.........
} else {
.........
}
break;
case "flash":
.........
break;
default:
.........
break;
}
}
}
复制代码
然而可达鸭以为事情没有这么简单,很快头疼的事就来了,业务老是会膨胀的,没多久就收到了通知,这个界面也要支持下连接跳转……省略N屡次这种支持,而后这个方法就变成将近二十个case,某些case中还有数量不等的if…else…,此处就不贴那恐怖的代码了,发挥大家的聪明才智想象一下吧。长长的条件分支结构不只视觉上震撼,修改和新增代码都让人如履薄冰,生怕一个眼花写到了错误的分支中,并且找到对应的分支就足够让人头晕目眩了,此刻我只以为有一只苍蝇一直在个人代码里嗡嗡乱飞。浏览器
这样下去和咸鱼有什么分别(主要是这咸鱼当的恶心啊),我决定重构The Fucking Codes。缓存
可达鸭眉头一皱,计上心头,鸭有一计,可平代码之乱,很差意思串戏了。首先咱们分析下咱们这个需求,虽然有不少的页面须要跳转,可是他们的跳转咱们能够抽象出相同点,而后根据上面的URI规则能够经过host+path来肯定一个具体的跳转,而后依赖咱们的抽象接口实现各页面的具体跳转须要。听个人!用策略模式,敲定方案后咱们说干就干。首先上个类图看下: bash
public interface IDeepLinkStrategy {
Uri uriTransformer(Uri uri);
Map<String,String> navigationParams(Uri uri);
boolean needSendEvents(Uri uri);
}
复制代码
public abstract class SimpleDeepLinkStrategy implements IDeepLinkStrategy {
@Override
public boolean needSendEvents(Uri uri) {
return false;
}
}
复制代码
public class StrategyFactory {
private StrategyFactory(){}
/** 缓存策略类实例,因为一个策略可能要屡次使用,若是不作缓存,每次都要经过反射实例化一个,内存中的无用对象也会 * 愈来愈多 */
private static HashMap<String,IDeepLinkStrategy> strategyCaches = new HashMap<>();
private static class StrategyFactoryHolder{
public static final StrategyFactory INSTANCE = new StrategyFactory();
}
public static StrategyFactory getInstance(){
return StrategyFactoryHolder.INSTANCE;
}
private static HashMap<String,String> classMap = new HashMap<>();
static {
classMap.put("news", ADeepLinkStrategy.class.getName());
classMap.put("news/detail", BDeepLinkStrategy.class.getName());
.......
}
public IDeepLinkStrategy creator(String key){
//若是实例已经存在则直接取出
if (strategyCaches.get(key) != null){
return strategyCaches.get(key);
}
//实例不存在经过策略类名反射获得实例并缓存到strategyCaches里面
String className = classMap.get(key);
if (TextUtils.isEmpty(className)){
className = WebStrategy.class.getName();
}
try {
IDeepLinkStrategy iDeepLinkStrategy = (IDeepLinkStrategy) Class.forName(className).newInstance();
strategyCaches.put(key,iDeepLinkStrategy);
return iDeepLinkStrategy;
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
return null;
}
}
}
复制代码
public class DeepLinkContext {
private static IDeepLinkStrategy mIDeepLinkStrategy;
public static Uri getUri(Uri uri){
return getStrategyInstance(uri).uriTransformer(uri);
}
public static Map<String,String> getParams(Uri uri){
return getStrategyInstance(uri).navigationParams(uri);
}
public static boolean needPostEvents(Uri uri){
return getStrategyInstance(uri).needSendEvents(uri);
}
private static IDeepLinkStrategy getStrategyInstance(Uri uri){
String key = uri.getHost()+uri.getPath();
mIDeepLinkStrategy = StrategyFactory.getInstance().creator(key);
return mIDeepLinkStrategy;
}
}
复制代码
而后咱们来看看上面的工具类变成啥样了:框架
public class DeepLinkUtils {
public static void jumpToTarget(Context context, Uri uri) {
if (uri == null || uri.getHost() == null) {return;}
Uri routUri =DeepLinkContext.getUri(uri);
if (routUri != null){
Postcard postcard = ARouter.getInstance().build(routUri);
Map<String,String> params = DeepLinkContext.getParams(uri);
if (params!=null){
for (Map.Entry<String, String> entry : params.entrySet()) {
postcard.withString(entry.getKey(),entry.getValue());
}
postcard.navigation(context);
}else {
postcard.navigation(context);
}
}
DeepLinkContext.needPostEvents(uri);
}
}
复制代码
是否是清爽了许多,并且就算增长再多的跳转界面,这里的代码也不用变化,之后可达鸭不再用担忧新增跳转界面了。ide
网上关于策略模式的文章很是之多,但大多都是介绍了策略模式是什么,为了解决什么问题的,可是少有结合具体业务来说解的,看完以后感受是明白了原理,但总感受有些困惑,但愿看完个人重构经历,可以进一步加深大家的理解。函数