設(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