iOS設(shè)計模式(5)策略模式

設(shè)計模式系列文章

《iOS設(shè)計模式(1)簡單工廠模式》
《iOS設(shè)計模式(2)工廠模式》
《iOS設(shè)計模式(3)適配器模式》
《iOS設(shè)計模式(4)抽象工廠模式》
《iOS設(shè)計模式(6)模板模式》
《iOS設(shè)計模式(7)建造者模式》

今天我們來學(xué)習(xí)一下策略模式,談到策略模式我看到大家一般舉例的使用場景是:超市促銷打折的例子宝冕,在iOS中則是用戶登錄驗(yàn)證的例子比較多。模式一般都是為解決需求服務(wù)的沦童,我還是想從實(shí)際需求中為大家介紹策略模式的使用場景讨彼,當(dāng)然也不是說前面舉得例子就不是項(xiàng)目需求的實(shí)際需要略贮。

上次在《iOS設(shè)計模式(3)適配器模式》中掉房,我們提出了一個項(xiàng)目開發(fā)中需求變更的情況。就是在視頻開發(fā)中颤芬,需要替換原有播放器的場景悲幅。在上篇文章中我提到,由于項(xiàng)目進(jìn)度緊站蝠,而且老項(xiàng)目前期規(guī)劃不足汰具,導(dǎo)致項(xiàng)目需求變更時代碼擴(kuò)展性非常差,牽一發(fā)而動全身菱魔。這種情況下我們用了適配器模式解決該問題留荔。

現(xiàn)在我們重新考慮這個問題,如果是有時間重做或者重構(gòu)的話澜倦,咱們怎么來設(shè)計代碼聚蝶?其實(shí)方法有很多,也沒有哪一種是最好的藻治。那么用策略模式能否達(dá)到我們的目的碘勉?我們先來看一下策略模式的定義:

1.概念

策略模式定義了一系列的算法,并將每一個算法封裝起來桩卵,而且使它們還可以相互替換验靡。策略模式讓算法獨(dú)立于使用它的客戶而獨(dú)立變化。
(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)
Context(應(yīng)用場景):
1雏节、需要使用ConcreteStrategy提供的算法胜嗓。
2、內(nèi)部維護(hù)一個Strategy的實(shí)例矾屯。
3兼蕊、負(fù)責(zé)動態(tài)設(shè)置運(yùn)行時Strategy具體的實(shí)現(xiàn)算法。
4件蚕、負(fù)責(zé)跟Strategy之間的交互和數(shù)據(jù)傳遞。
Strategy(抽象策略類):
1产禾、 定義了一個公共接口排作,各種不同的算法以不同的方式實(shí)現(xiàn)這個接口,Context使用這個接口調(diào)用不同的算法亚情,一般使用接口或抽象類實(shí)現(xiàn)妄痪。
ConcreteStrategy(具體策略類):
2、 實(shí)現(xiàn)了Strategy定義的接口楞件,提供具體的算法實(shí)現(xiàn)衫生。---百度百科

根據(jù)策略模式的定義裳瘪,我們結(jié)合自己的需求,可以想到把不同播放器的調(diào)用方法封裝成不同的算法罪针,一個播放器對應(yīng)一個播放算法彭羹、策略Strategy(LHAVPlayer、LHIJKPlayer)泪酱,再封裝一個供客戶端調(diào)用的通用的播放器Context應(yīng)用場景(LHPlayer)來負(fù)責(zé)Strategy之間的交互和數(shù)據(jù)傳遞派殷。

2.應(yīng)用場景

我們再來看策略模式的使用場景:

1、 多個類只區(qū)別在表現(xiàn)行為不同墓阀,可以使用Strategy模式毡惜,在運(yùn)行時動態(tài)選擇具體要執(zhí)行的行為。
2斯撮、 需要在不同情況下使用不同的策略(算法)经伙,或者策略還可能在未來用其它方式來實(shí)現(xiàn)。
3勿锅、 對客戶隱藏具體策略(算法)的實(shí)現(xiàn)細(xì)節(jié)橱乱,彼此完全獨(dú)立。---百度百科

不同的第三方播放器只區(qū)別在播放粱甫、暫停泳叠、停止等一系列方法的實(shí)現(xiàn)和調(diào)用上的不同。我們的需求就是在未來可能會使用不同的播放器茶宵,而這些對客戶來說應(yīng)該是隱藏的危纫,不關(guān)心具體細(xì)節(jié)的,彼此完全獨(dú)立的乌庶。所以种蝶,完全可以通過策略模式來解決我們的需求。下面我們看其代碼實(shí)現(xiàn)瞒大。

3.代碼實(shí)現(xiàn)

(1)策略模式的核心就是對算法變化的封裝螃征。
定義一個通用算法協(xié)議,讓每個算法遵守其規(guī)則透敌。

@protocol LHPlayerProtocol <NSObject>

/**
 *  Player開啟視頻
 *
 *  @return 描述(只為舉例需要)
 */
- (NSString *)lh_play;

/**
 *  Player暫停視頻
 *
 *  @return 描述(只為舉例需要)
 */
- (NSString *)lh_pause; 

/**
 *  Player停止播放
 *
 *  @return 描述(只為舉例需要)
 */
- (NSString *)lh_stop; 

AVPlayer的算法封裝

#import <Foundation/Foundation.h>
#import "LHPlayerProtocol.h"

@interface LHAVPlayer : NSObject<LHPlayerProtocol>

@end

#import "LHAVPlayer.h"
#import "AVPlayer.h"

@interface LHAVPlayer ()
{
    id<AVPlayerProtocol> player;// AVPlayer播放器自身的協(xié)議
}
@end

@implementation LHAVPlayer

- (instancetype)init
{
    self = [super init];
    if (self) {
        player = [[AVPlayer alloc] init];// 初始化AVPlayer播放器對象
    }
    return self;
}

// 播放
- (NSString *)lh_play{
    return [player a_play];
}

// 暫停
- (NSString *)lh_pause{
    return [player a_pause];
}

// 停止
- (NSString *)lh_stop{
    return [player a_stop];
}

- (void)dealloc
{
    player = nil;
}

@end

IJKPlayer的算法封裝

#import <Foundation/Foundation.h>
#import "LHPlayerProtocol.h"

@interface LHIJKPlayer : NSObject<LHPlayerProtocol>

@end

#import "LHIJKPlayer.h"
#import "Ijkplayer.h"

@interface LHIJKPlayer ()
{
    id<IjkplayerProtocol> player;// IJKPlayer播放器自身的協(xié)議
}
@end

@implementation LHIJKPlayer

- (instancetype)init
{
    self = [super init];
    if (self) {
        player = [[Ijkplayer alloc] init];// 初始化IJKPlayer播放器對象
    }
    return self;
}

// 播放
- (NSString *)lh_play{
    return [player i_play];
}

// 暫停
- (NSString *)lh_pause{
    return [player i_pause];
}

// 停止
- (NSString *)lh_stop{
    return [player i_stop];
}

- (void)dealloc
{
    player = nil;
}

@end

(2)策略模式中另一個核心類Context的定義
通用播放器類LHPlayer的定義盯滚。根據(jù)不同的策略選擇不同的算法。

#import <Foundation/Foundation.h>
#import "LHPlayerProtocol.h"

// 播放器的類型
typedef enum : NSUInteger {
    EPlayerType_AVPlayer,
    EPlayerType_IJKPlayer
} EPlayerType;

@interface LHPlayer : NSObject

- (instancetype)initWithType:(EPlayerType)type;

/**
 *  開啟視頻
 *
 *  @return 描述(只為舉例需要)
 */
- (NSString *)play;

/**
 *  暫停視頻
 *
 *  @return 描述(只為舉例需要)
 */
- (NSString *)pause; 

/**
 *  停止播放
 *
 *  @return 描述(只為舉例需要)
 */
- (NSString *)stop; 

@end

#import "LHPlayer.h"
#import "LHPlayerProtocol.h"
#import "LHAVPlayer.h"
#import "LHIJKPlayer.h"

@interface LHPlayer ()
{
    id<LHPlayerProtocol>  player;
}
@end

@implementation LHPlayer

- (instancetype)initWithType:(EPlayerType)type
{
    self = [super init];
    if (self) {
        [self initPlayerWithType:type];
    }
    return self;
}

// 初始化播放器
- (void)initPlayerWithType:(EPlayerType)type{
    switch (type) {
        case EPlayerType_AVPlayer:
        {
            player = [[LHAVPlayer alloc] init];
            break;
        }
        case EPlayerType_IJKPlayer:
        {
            player = [[LHIJKPlayer alloc] init];
            break;
        }
    }
}

//開啟視頻
- (NSString *)play{
    return [player lh_play];
}

//暫停視頻
- (NSString *)pause{
    return [player lh_pause];
}

//停止播放
- (NSString *)stop{
    return [player lh_stop];
}

@end

下面看客戶端的調(diào)用酗电。

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

#import "ViewController.h"
#import "LHPlayer.h"

@interface ViewController ()
{
    LHPlayer *player;
}

@property (weak, nonatomic) IBOutlet UIButton *btnAVPlayer;
@property (weak, nonatomic) IBOutlet UIButton *btnIjkplayer;
@property (weak, nonatomic) IBOutlet UILabel *lbState;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initPlayerWithType:EPlayerType_IJKPlayer];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

// 初始化播放器
- (void)initPlayerWithType:(EPlayerType)type{
    if (player) {
        player = nil;
    }
    player = [[LHPlayer alloc] initWithType:type];
}

#pragma mark -
#pragma makr Button Event

// 選擇AVPlayer
- (IBAction)btnAVPlayerEvent:(UIButton *)sender {
    sender.selected = YES;
    _btnIjkplayer.selected = NO;
    
    [self initPlayerWithType:EPlayerType_AVPlayer];
}

// 選擇Ijkplayer
- (IBAction)btnIjkplayerEvent:(UIButton *)sender {
    sender.selected = YES;
    _btnAVPlayer.selected = NO;
    
    [self initPlayerWithType:EPlayerType_IJKPlayer];
}

// 播放器播放視頻
- (IBAction)btnPlayerEvent:(UIButton *)sender {
    _lbState.text = player ? [player play] : @"播放器為空";
}

// 播放器暫停視頻
- (IBAction)btnPauseEvent:(UIButton *)sender {
    _lbState.text = player ? [player pause] : @"播放器為空";
}

// 播放器停止視頻
- (IBAction)btnStopEvent:(UIButton *)sender {
    _lbState.text = player ? [player stop] : @"播放器為空";
}

@end

大家可以看到魄藕,客戶端切換播放器只要替換一下枚舉值就可以了輕松切換了,而且已經(jīng)哪個播放器火了撵术,擴(kuò)展新的播放器也是輕而易舉背率,不對客戶端造成任何影響。這就是策略模式的好處所在。

4.策略模式的優(yōu)點(diǎn)缺點(diǎn)

任何模式都不是十全十美寝姿,都有可圈可點(diǎn)交排,都有美中不足,至于策略模式的優(yōu)缺點(diǎn)我們還是來看百度百科給出的解釋:

優(yōu)點(diǎn):
1饵筑、 策略模式提供了管理相關(guān)的算法族的辦法埃篓。策略類的等級結(jié)構(gòu)定義了一個算法或行為族。恰當(dāng)使用繼承可以把公共的代碼轉(zhuǎn)移到父類里面翻翩,從而避免重復(fù)的代碼都许。
2、 策略模式提供了可以替換繼承關(guān)系的辦法嫂冻。繼承可以處理多種算法或行為胶征。如果不是用策略模式,那么使用算法或行為的環(huán)境類就可能會有一些子類桨仿,每一個子類提供一個不同的算法或行為睛低。但是,這樣一來算法或行為的使用者就和算法或行為本身混在一起服傍。決定使用哪一種算法或采取哪一種行為的邏輯就和算法或行為的邏輯混合在一起钱雷,從而不可能再獨(dú)立演化。繼承使得動態(tài)改變算法或行為變得不可能吹零。

3罩抗、 使用策略模式可以避免使用多重條件轉(zhuǎn)移語句。多重轉(zhuǎn)移語句不易維護(hù)灿椅,它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起套蒂,統(tǒng)統(tǒng)列在一個多重轉(zhuǎn)移語句里面,比使用繼承的辦法還要原始和落后茫蛹。
缺點(diǎn):
1操刀、客戶端必須知道所有的策略類,并自行決定使用哪一個策略類婴洼。這就意味著客戶端必須理解這些算法的區(qū)別骨坑,以便適時選擇恰當(dāng)?shù)乃惴悺Q言之柬采,策略模式只適用于客戶端知道所有的算法或行為的情況欢唾。
2、 策略模式造成很多的策略類警没,每個具體策略類都會產(chǎn)生一個新類匈辱。有時候可以通過把依賴于環(huán)境的狀態(tài)保存到客戶端里面,而將策略類設(shè)計成可共享的杀迹,這樣策略類實(shí)例可以被不同客戶端使用。換言之,可以使用享元模式來減少對象的數(shù)量树酪。百度百科

好了浅碾,策略模式介紹完了,如果大家覺得我說的不對续语,或者有什么紕漏之處敬請指正垂谢,共同學(xué)習(xí)、共同進(jìn)步疮茄!謝謝大家滥朱。

聯(lián)系方式:QQ 616867091

源碼下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市力试,隨后出現(xiàn)的幾起案子徙邻,更是在濱河造成了極大的恐慌,老刑警劉巖畸裳,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缰犁,死亡現(xiàn)場離奇詭異,居然都是意外死亡怖糊,警方通過查閱死者的電腦和手機(jī)帅容,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伍伤,“玉大人并徘,你說我怎么就攤上這事∪呕辏” “怎么了麦乞?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阅爽。 經(jīng)常有香客問我路幸,道長,這世上最難降的妖魔是什么付翁? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任简肴,我火速辦了婚禮,結(jié)果婚禮上百侧,老公的妹妹穿的比我還像新娘砰识。我一直安慰自己,他們只是感情好佣渴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布辫狼。 她就那樣靜靜地躺著,像睡著了一般辛润。 火紅的嫁衣襯著肌膚如雪膨处。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音真椿,去河邊找鬼鹃答。 笑死,一個胖子當(dāng)著我的面吹牛突硝,可吹牛的內(nèi)容都是我干的测摔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼解恰,長吁一口氣:“原來是場噩夢啊……” “哼锋八!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起护盈,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤挟纱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后黄琼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體樊销,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年脏款,在試婚紗的時候發(fā)現(xiàn)自己被綠了围苫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡撤师,死狀恐怖剂府,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剃盾,我是刑警寧澤腺占,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站痒谴,受9級特大地震影響衰伯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜积蔚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一意鲸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尽爆,春花似錦怎顾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至幅狮,卻和暖如春募强,著一層夾襖步出監(jiān)牢的瞬間株灸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工钻注, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚂且,地道東北人配猫。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓幅恋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泵肄。 傳聞我的和親對象是個殘疾皇子捆交,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)腐巢,斷路器品追,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 1 場景問題# 1.1 報價管理## 向客戶報價,對于銷售部門的人來講冯丙,這是一個非常重大肉瓦、非常復(fù)雜的問題,對不同的...
    七寸知架構(gòu)閱讀 5,077評論 9 62
  • 1 場景問題 1.1 報價管理 向客戶報價胃惜,對于銷售部門的人來講泞莉,這是一個非常重大、非常復(fù)雜的問題船殉,對不同的客戶要...
    4e70992f13e7閱讀 3,084評論 2 16
  • 設(shè)計模式匯總 一鲫趁、基礎(chǔ)知識 1. 設(shè)計模式概述 定義:設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 3,939評論 1 15
  • 定義對象之間的一對多依賴利虫,這樣當(dāng)一個對象改變狀態(tài)時挨厚,他的所有依賴者都會收到通知并完成更新。 有一天糠惫,老板興高采烈的...
    莮亾閱讀 652評論 0 2