前面也給大家說了,掃盲系列第一個專題是:設計模式,今天它來啦。設計模式是我們開發的內功,很多同學都知道設計模式很有用,但我們卻總是很難記住它,尤其是對於定義。其實,我們從來都不需要去記住它,但學習是必要的,我們需要把每一個招式化為自己的血肉,在編寫代碼的時候多一種設計手段。
設計模式來源於生活,我們更應該用生活和它類比起來。掃盲系列只會講重點的設計模式,對於部分簡單的設計模式,不會花太多篇幅。
此外,對於設計模式,本身都很相似,很難界定,如果你有自己的想法,一定要勇敢站出來和大家 argue。
好的,就這樣,今天就帶來第一篇設計模式:策略模式。
什麼是策略模式?生活中的策略策略模式在生活中體現很多。
我們要去旅遊,我們可以選擇不同的出行方式:飛機,火車,大巴,自駕等,這是不同的策略。
雙十一當當網購買滿減活動,滿 100 減 50,滿 200 減 100,滿 400 減 250 等,這也是不同的策略。
抑或是我們在追求女生時,針對不同性格的女孩子採用不同的方式,這還是不同的策略。
程序中的策略策略模式在程序中的體現依然淋漓盡致。
比如我們的圖片加載,Android 上有 Fresco,Picasso,Glide,Universal-Image-Loader 等,iOS 上有 SDWebImage、AFNetworking、FastImageCache 等。
所以,假設讓你來設計一個圖片加載上層框架,要求可以底層可以使用 A B 兩種加載策略,你會怎麼做呢?
// 加載類A
public class ImageLoadServiceA {
public void loadImage() {
System.out.println("使用 A 加載框架");
}
}
// 加載類B
public class ImageLoadServiceB {
public void loadImage() {
System.out.println("使用 B 加載框架");
}
}
// 使用
public void loadNetImage(boolean useA) {
if(useA){
new ImageLoadServiceA().loadImage();// 使用A加載方式
} else {
new ImageLoadServiceB().loadImage();// 使用B加載方式
}
}
可以看到,上述通過一個 useA 參數判斷是否使用 A 框架,為 true 使用 A,否則使用 B 框架進行加載。
使用簡單工廠模式應對但假設我們現在需要再支持一個 C 框架的使用,你可能想到了,那就再加一個 boolean 參數 useB 即可,或者直接使用一個 int 參數 loadType,宏定義 0 代表 A 框架,1 代表 B 框架,2 代表 C 框架,這樣如果需要增加方式則更新取值即可。
設計模式不過是我們寫程序的招式,由於之前大家可能還學習過了簡單工廠模式,我們不妨在這裡進行實戰。
// 抽象圖片加載類
public abstract class ImageLoadService {
public abstract void loadImage();
}
// 具體加載類A
public class ImageLoadServiceA extends ImageLoadService {
@Override
public void loadImage() {
System.out.println("使用 A 加載框架");
}
}
//具體加載類B
public class ImageLoadServiceB extends ImageLoadService {
@Override
public void loadImage() {
System.out.println("使用 B 加載框架");
}
}
//具體加載類C
public class ImageLoadServiceC extends ImageLoadService {
@Override
public void loadImage() {
System.out.println("使用 C 加載框架");
}
}
public class ImageLoadFactory {
public static ImageLoadService create(int loadType) {
ImageLoadService loadService = null;
switch (loadType) {
case 0:
loadService = new ImageLoadServiceA();
break;
case 1:
loadService = new ImageLoadServiceB();
break;
case 2:
loadService = new ImageLoadServiceC();
break;
}
return loadService;
}
}
// 使用
public void loadNetImage(int loadType) {
ImageLoadFactory.create(loadType).loadImage();
}
可以看到,我們使用簡單工廠模式後,在處理新增其他加載方式的問題的時候,不會再去影響原有的加載類代碼,如果新增一種加載方式的話,我們只需要新增 ImageLoadXXX 類,實現 loadImage() 加載方法,再修改工廠類 ImageLoadFactory 即可。
相信你也發現了,這個方式只能解決對象的創建問題,我們每次新增方式的時候都會新增一個類,而且需要對工廠類進行代碼修改,顯然是違反了開閉原則。
策略模式人生處處有策略,上面的不同的加載方式其實就是不同的「策略」。
策略模式是對 算法的封裝,它將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以獨立變換。
策略模式的特點策略模式的結構要學習一個設計模式,先要學會臨摹,所以上面的需求,我們可以實現為:
定義抽象策略
public interface ImageLoadStrategy {
void loadImage() ;
}
定義具體的策略
// 具體加載類A
public class ImageLoadStrategyA implements ImageLoadStrategy {
@Override
public void loadImage() {
System.out.println("使用 A 加載框架");
}
}
//具體加載類B
public class ImageLoadStrategyB implements ImageLoadStrategy {
@Override
public void loadImage() {
System.out.println("使用 B 加載框架");
}
}
//具體加載類C
public class ImageLoadStrategyC implements ImageLoadStrategy {
@Override
public void loadImage() {
System.out.println("使用 C 加載框架");
}
}
定義上下文,選擇方式
public class ContextImageLoadStrategy {
private ImageLoadStrategy strategy ;
public ContextImageLoadStrategy(ImageLoadStrategy strategy){
this.strategy = strategy ;
}
public void loadImage(){
strategy.loadImage();
}
}
使用
public void loadImage(ImageLoadStrategy imageLoadStrategy){
ContextImageLoadStrategy contextStrategy = new ContextImageLoadStrategy(imageLoadStrategy);
contextStrategy.loadImage();
}
注意:策略的核心不是如何實現算法,而是如何更優雅的把這些算法組織起來,讓客戶端非常好調用「雖然策略非常多,可以自由切換,但是同一時間客戶端只能調用一個策略,其實也很好理解,你不可能同時既坐飛機,又坐火車」。
策略模式的優點策略類可以互相替換
由於策略類都實現同一個接口,因此他們能夠互相替換。
耦合度低,方便擴展
增加一個新的策略只需要添加一個具體的策略類即可,基本不需要改變原有的代碼,符合開閉原則。
避免使用多重條件選擇語句(if-else 或者 switch)。
策略模式的缺點你有想到如何解決「客戶端必須知道所有的策略類」這個缺點麼?
策略模式的應用場景同一個問題具有不同算法時,即僅僅是具體的實現細節不同時,如各種排序算法等等。
對客戶隱藏具體策略(算法)的實現細節,彼此完全獨立;提高算法的保密性與安全性。
一個類擁有很多行為,而又需要使用 if-else 或者 switch 語句來選擇具體行為時。使用策略模式把這些行為獨立到具體的策略類中,可以避免多重選擇的結構。
源碼中的策略模式想必大家已經很清楚上面的策略模式了,下面源碼中用到策略模式了嗎?
寫在最後總的來說,策略模式還算我們項目開發中會使用非常頻繁的模式,你學會了麼?如有疑問,請在評論區留言。
—————END—————
我是南塵,只做比心的公眾號,歡迎關注我。
推薦閱讀:
nanchen,是一個怎樣的公眾號?
歡迎關注南塵的公眾號:nanchen
做不完的開源,寫不完的矯情,只做比心的公眾號,如果你喜歡,你可以選擇分享給大家。如果你有好的文章,歡迎投稿,讓我們一起來分享。 長按上方二維碼關注 做不完的開源,寫不完的矯情 一起來看 nanchen 同學的成長筆記