策略模式屬於對象的行爲模式。其用意是針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響到客戶端的狀況下發生變化。——《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
網上關於策略模式的文章很是之多,但大多都是介紹了策略模式是什麼,爲了解決什麼問題的,可是少有結合具體業務來說解的,看完以後感受是明白了原理,但總感受有些困惑,但願看完個人重構經歷,可以進一步加深大家的理解。函數