设计模式系列文章
《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