iOS设计模式(5)策略模式

设计模式系列文章

《iOS设计模式(1)简单工厂模式》
《iOS设计模式(2)工厂模式》
《iOS设计模式(3)适配器模式》
《iOS设计模式(4)抽象工厂模式》
《iOS设计模式(6)模板模式》
《iOS设计模式(7)建造者模式》

今天我们来学习一下策略模式,谈到策略模式我看到大家一般举例的使用场景是:超市促销打折的例子,在iOS中则是用户登录验证的例子比较多。模式一般都是为解决需求服务的,我还是想从实际需求中为大家介绍策略模式的使用场景,当然也不是说前面举得例子就不是项目需求的实际需要。

上次在《iOS设计模式(3)适配器模式》中,我们提出了一个项目开发中需求变更的情况。就是在视频开发中,需要替换原有播放器的场景。在上篇文章中我提到,由于项目进度紧,而且老项目前期规划不足,导致项目需求变更时代码扩展性非常差,牵一发而动全身。这种情况下我们用了适配器模式解决该问题。

现在我们重新考虑这个问题,如果是有时间重做或者重构的话,咱们怎么来设计代码?其实方法有很多,也没有哪一种是最好的。那么用策略模式能否达到我们的目的?我们先来看一下策略模式的定义:

1.概念

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
(原文: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(应用场景):
1、需要使用ConcreteStrategy提供的算法。
2、内部维护一个Strategy的实例。
3、负责动态设置运行时Strategy具体的实现算法。
4、负责跟Strategy之间的交互和数据传递。
Strategy(抽象策略类):
1、 定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
ConcreteStrategy(具体策略类):
2、 实现了Strategy定义的接口,提供具体的算法实现。---百度百科

根据策略模式的定义,我们结合自己的需求,可以想到把不同播放器的调用方法封装成不同的算法,一个播放器对应一个播放算法、策略Strategy(LHAVPlayer、LHIJKPlayer),再封装一个供客户端调用的通用的播放器Context应用场景(LHPlayer)来负责Strategy之间的交互和数据传递。

2.应用场景

我们再来看策略模式的使用场景:

1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。---百度百科

不同的第三方播放器只区别在播放、暂停、停止等一系列方法的实现和调用上的不同。我们的需求就是在未来可能会使用不同的播放器,而这些对客户来说应该是隐藏的,不关心具体细节的,彼此完全独立的。所以,完全可以通过策略模式来解决我们的需求。下面我们看其代码实现。

3.代码实现

(1)策略模式的核心就是对算法变化的封装。
定义一个通用算法协议,让每个算法遵守其规则。

@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播放器自身的协议
}
@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播放器自身的协议
}
@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的定义。根据不同的策略选择不同的算法。

#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

下面看客户端的调用。

#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

大家可以看到,客户端切换播放器只要替换一下枚举值就可以了轻松切换了,而且已经哪个播放器火了,扩展新的播放器也是轻而易举,不对客户端造成任何影响。这就是策略模式的好处所在。

4.策略模式的优点缺点

任何模式都不是十全十美,都有可圈可点,都有美中不足,至于策略模式的优缺点我们还是来看百度百科给出的解释:

优点:
1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。

3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点:
1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2、 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。百度百科

好了,策略模式介绍完了,如果大家觉得我说的不对,或者有什么纰漏之处敬请指正,共同学习、共同进步!谢谢大家。

联系方式:QQ 616867091

源码下载

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,724评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,104评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,142评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,086评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,076评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,914评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,220评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,871评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,318评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,834评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,951评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,574评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,162评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,162评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,383评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,349评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,652评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 1 场景问题# 1.1 报价管理## 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的...
    七寸知架构阅读 5,047评论 9 62
  • 1 场景问题 1.1 报价管理 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的客户要...
    4e70992f13e7阅读 3,071评论 2 16
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,903评论 1 15
  • 定义对象之间的一对多依赖,这样当一个对象改变状态时,他的所有依赖者都会收到通知并完成更新。 有一天,老板兴高采烈的...
    莮亾阅读 643评论 0 2