什么是策略模式缭黔?
生活中的策略
策略模式在生活中體現(xiàn)很多食茎。
我們要去旅游,我們可以選擇不同的出行方式:飛機(jī)馏谨,火車别渔,大巴,自駕等惧互,這是不同的策略哎媚。
雙十一當(dāng)當(dāng)網(wǎng)購買滿減活動,滿 100 減 50喊儡,滿 200 減 100拨与,滿 400 減 250 等,這也是不同的策略艾猜。
抑或是我們在追求女生時买喧,針對不同性格的女孩子采用不同的方式,這還是不同的策略匆赃。
程序中的策略
策略模式在程序中的體現(xiàn)依然淋漓盡致淤毛。
比如我們的圖片加載,Android 上有 Fresco
算柳,Picasso
低淡,Glide
,Universal-Image-Loader
等,iOS 上有 SDWebImage
蔗蹋、AFNetworking
事期、FastImageCache
等。
所以纸颜,假設(shè)讓你來設(shè)計一個圖片加載上層框架兽泣,要求可以底層可以使用 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
參數(shù)判斷是否使用 A 框架,為 true
使用 A涮较,否則使用 B 框架進(jìn)行加載稠鼻。
使用簡單工廠模式應(yīng)對
但假設(shè)我們現(xiàn)在需要再支持一個 C 框架的使用,你可能想到了狂票,那就再加一個 boolean 參數(shù) useB
即可候齿,或者直接使用一個 int 參數(shù) loadType
,宏定義 0 代表 A 框架闺属,1 代表 B 框架慌盯,2 代表 C 框架,這樣如果需要增加方式則更新取值即可掂器。
設(shè)計模式不過是我們寫程序的招式亚皂,由于之前大家可能還學(xué)習(xí)過了簡單工廠模式,我們不妨在這里進(jìn)行實(shí)戰(zhàn)国瓮。
// 抽象圖片加載類
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
類孵睬,實(shí)現(xiàn) loadImage()
加載方法播歼,再修改工廠類 ImageLoadFactory
即可。
相信你也發(fā)現(xiàn)了肪康,這個方式只能解決對象的創(chuàng)建問題荚恶,我們每次新增方式的時候都會新增一個類,而且需要對工廠類進(jìn)行代碼修改磷支,顯然是違反了開閉原則谒撼。
策略模式
人生處處有策略,上面的不同的加載方式其實(shí)就是不同的「策略」雾狈。
策略模式是對 算法的封裝廓潜,它將每一個算法封裝到具有共同接口的獨(dú)立的類中,從而使得它們可以獨(dú)立變換。
策略模式的特點(diǎn)
- 是一種行為模式辩蛋,對算法封裝呻畸,使得客戶端獨(dú)立于各個策略;
- 擴(kuò)展性強(qiáng)悼院,添加策略無非就是添加一個具體的實(shí)現(xiàn)類而已伤为,代價非常低;
策略模式的結(jié)構(gòu)
策略模式做實(shí)現(xiàn)
要學(xué)習(xí)一個設(shè)計模式据途,先要學(xué)會臨摹绞愚,所以上面的需求,我們可以實(shí)現(xiàn)為:
- 定義抽象策略
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();
}
注意: 策略的核心不是如何實(shí)現(xiàn)算法位衩,而是如何更優(yōu)雅的把這些算法組織起來,讓客戶端非常好調(diào)用「雖然策略非常多熔萧,可以自由切換糖驴,但是同一時間客戶端只能調(diào)用一個策略,其實(shí)也很好理解佛致,你不可能同時既坐飛機(jī)贮缕,又坐火車」。
策略模式的優(yōu)點(diǎn)
- 策略類可以互相替換
由于策略類都實(shí)現(xiàn)同一個接口晌杰,因此他們能夠互相替換跷睦。 - 耦合度低,方便擴(kuò)展
增加一個新的策略只需要添加一個具體的策略類即可肋演,基本不需要改變原有的代碼,符合開閉原則烂琴。 - 避免使用多重條件選擇語句(
if-else
或者switch
)爹殊。
策略模式的缺點(diǎn)
- 策略的增多會導(dǎo)致子類的也會變多。比如上方再增加加載方式必須增加類奸绷。
- 客戶端必須知道所有的策略類梗夸,并自行決定使用哪一個策略類。比如上方必須知道有哪些加載策略号醉,這樣我們才能調(diào)用到正確的加載方式反症。
你有想到如何解決「客戶端必須知道所有的策略類」這個缺點(diǎn)么?
策略模式的應(yīng)用場景
- 同一個問題具有不同算法時畔派,即僅僅是具體的實(shí)現(xiàn)細(xì)節(jié)不同時铅碍,如各種排序算法等等。
- 對客戶隱藏具體策略(算法)的實(shí)現(xiàn)細(xì)節(jié)线椰,彼此完全獨(dú)立胞谈;提高算法的保密性與安全性。
- 一個類擁有很多行為,而又需要使用
if-else
或者switch
語句來選擇具體行為時烦绳。使用策略模式把這些行為獨(dú)立到具體的策略類中卿捎,可以避免多重選擇的結(jié)構(gòu)。
源碼中的策略模式
想必大家已經(jīng)很清楚上面的策略模式了径密,下面源碼中用到策略模式了嗎午阵?
- Android 的動畫插值器;
- Android 中
ListView
的ArrayAdapter
享扔、SimpleAdapter
趟庄;
寫在最后
總的來說,策略模式還算我們項(xiàng)目開發(fā)中會使用非常頻繁的模式伪很,你學(xué)會了么戚啥?如有疑問,請在評論區(qū)留言锉试。