策略模式在播放器调试中的运用

需求:最近在视频播放中,遇到了播放器来回切换调试的问题,那么接下来,会介绍一下怎么做才会使代码更加适用于业务且改动较小的方案。

场景一

  • 只有腾讯播放SDK
方案一

直接在ViewController初始化TencentSDK,并实现对应的播放暂停停止重置等功能。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tencentSDK = [TencentSDK new];
}

- (void)onActionPlay:(id)sender {
    [self.tencentSDK play];
}

- (void)onActionPause:(id)sender {
    [self.tencentSDK pause];
}

- (void)onActionStop:(id)sender {
    [self.tencentSDK stop];
}

- (void)onActionResume:(id)sender {
    [self.tencentSDK resume];
}
复制代码

这种使用方式是我刚进入职场使用的一种傻瓜式方案,开发快速,简单明了,适用于一次性开发,确定播放类型,且后期不会再改动的代码。

场景二

  • 腾讯播放SDK
  • 新浪播放SDK
  • 最开始使用腾讯SDK,后面由于业务需求,接入新浪SDK
方案一

ViewController里面初始化TencentSDK。因为业务更改,ViewController里面初始化SinaSDK,在原来的业务逻辑里面替换代码。

- (void)viewDidLoad {
    [super viewDidLoad];
//    self.tencentSDK = [TencentSDK new];
    self.sinaSDK = [SinaSDK new];
}

- (void)onActionPlay:(id)sender {
//    [self.tencentSDK play];
    [self.sinaSDK play];
}

- (void)onActionPause:(id)sender {
//    [self.tencentSDK pause];
    [self.sinaSDK play];
}

- (void)onActionStop:(id)sender {
//    [self.tencentSDK stop];
    [self.sinaSDK stop];
}

- (void)onActionResume:(id)sender {
//    [self.tencentSDK resume];
    [self.sinaSDK resume];
}
复制代码

这种方案也可以完成我们更改播放SDK的需求,但是一个播放界面的业务不仅仅是播放暂停停止重置等功能,还会涉及到弹幕礼物分享等其他业务,随着业务增多,ViewController的代码也会随之变大,有时候我们在执行暂停时还会有其它操作,这样频繁去更改代码的行为并不友好,也增加了我们的工作量,出错机率也会随之增大。

方案二

工作中我们在使用第三方框架时,提倡加一层中间层,这样在替换第三方框架时,可以减少业务代码的更改,只需要中间层替换底层代码,保持上层业务代码不变。

创建PlayerInter作为播放器的中间层,对之前的方案一进行更改

- (instancetype)initWithType:(NSNumber *)type {
    if (self = [super init]) {
        _type = type;
    }
    return self;
}

- (void)interPlay {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK play];
    } else {
        [self.tencentSDK play];
    }
}

- (void)interPause {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK pause];
    } else {
        [self.tencentSDK pause];
    }
}

- (void)interStop {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK stop];
    } else {
        [self.tencentSDK pause];
    }
}

- (void)interResume {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK resume];
    } else {
        [self.tencentSDK resume];
    }
}
复制代码

播放器SDK懒加载,根据PlayerInter初始化传进来的type类型,进行对应的播放器使用。ViewController的代码属于上层业务,当播放更换时,只需要PlayerInter做更改,上层业务不需要更改。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.playerInter = [[PlayerInter alloc]initWithType:@1];
}

- (void)onActionPlay:(id)sender {
    [self.playerInter interPlay];
}

- (void)onActionPause:(id)sender {
    [self.playerInter interPause];
}

- (void)onActionStop:(id)sender {
    [self.playerInter interStop];
}

- (void)onActionResume:(id)sender {
    [self.playerInter interResume];
}
复制代码

加了中间层,感觉方案上已经尽善尽美,当播放器更换时,只需要修改type类型即可,并且也可以做到后台控制使用哪种播放器。可是作为一个居安思危的程序员,我怎么能在这个时候就红枣枸杞茶喝起来呢?我设想如果再有一种新的播放器的话代码会怎么样。。。

  • 假设新增优酷播放SDK

PlayerInter代码如下

- (instancetype)initWithType:(NSNumber *)type {
    if (self = [super init]) {
        _type = type;
    }
    return self;
}

- (void)interPlay {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK play];
    } else if ([self.type isEqualToNumber:@2]) {
        [self.tencentSDK play];
    } else {
        [self.youkuSDK play];
    }
}

- (void)interPause {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK pause];
    } else if ([self.type isEqualToNumber:@2]) {
        [self.tencentSDK pause];
    } else {
        [self.youkuSDK pause];
    }
}

- (void)interStop {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK stop];
    } else if ([self.type isEqualToNumber:@2]) {
        [self.tencentSDK pause];
    } else {
        [self.youkuSDK stop];
    }
}

- (void)interResume {
    if ([self.type isEqualToNumber:@1]) {
        [self.sinaSDK resume];
    } else if ([self.type isEqualToNumber:@2]) {
        [self.tencentSDK resume];
    } else {
        [self.youkuSDK stop];
    }
}
复制代码

除了频繁的if else改动之外,ViewController的上层业务代码没有做出任何改变,这种中间层的方案也还是可以解决我刚才设想的问题,可是作为一个有强迫症的程序员,if else过多也觉得会增加错误机率,如果接下来有第四、第五个播放器呢?

方案三

播放器功能

  • 播放
  • 暂停
  • 停止
  • 重置

每个播放器都不外乎有以上几个功能,那么是否可以通过一种新的方法来改变之前的策略呢?

创建PlayProtocol协议,协议有playpausestopresume等通用method

@protocol PlayProtocol <NSObject>
- (void)play;
- (void)pause;
- (void)stop;
- (void)resume;
复制代码

创建TencentPlayObject类,遵守PlayProtocol协议,在TencentPlayObject初始化TencentSDK,并实现PlayProtocol协议制定的方法。

- (instancetype)init {
    if (self = [super init]) {
        //初始化SDK
        _tencentSDK = [[TencentSDK alloc]init];
    }
    return self;
}

#pragma mark - PlayProtocol
- (void)play {
    [self.tencentSDK play];
}

- (void)pause {
    [self.tencentSDK pause];
}

- (void)stop {
    [self.tencentSDK stop];
}

- (void)resume {
    [self.tencentSDK resume];
}
复制代码

创建SinaPlayObjectYouKuObject,遵守PlayProtocol,初始化相对应的SDK,并实现协议方法,方法同TencentPlayObject一样。

创建PlayerFactory类,根据type类型,用来生产对应的playObject

@implementation PlayerFactory

- (instancetype)initWithType:(NSNumber *)type {
    if (self = [super init]) {
        _type = type;
    }
    return self;
}

- (id<PlayProtocol>)productionPlayer {
    if ([self.type isEqualToNumber:@1]) {
        return self.tencentPlayer;
    } else if ([self.type isEqualToNumber:@2]) {
        return self.sinaPlayer;
    } else {
        return self.youkuPlayer;
    }
}
复制代码

接下来对ViewController的代码进行调整

@interface ViewController ()
@property (nonatomic , strong) id<PlayProtocol> player;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    PlayerFactory *factory = [[PlayerFactory alloc]initWithType:@2];
    self.player = [factory productionPlayer];
}

- (void)onActionPlay:(id)sender {
    [self.player play];
}

- (void)onActionPause:(id)sender {
    [self.player pause];
}

- (void)onActionStop:(id)sender {
    [self.player stop];
}

- (void)onActionResume:(id)sender {
    [self.player resume];
}
复制代码

相比较之前的中间层方案,协议的方法更直接明了,从PlayerFactory类中可以看出,只有在生产player的时候用到了if else判断,其它只要各自遵守协议即可,代码上更简单流程,出错机率大大降低。ViewController业务层的代码也保持了不变的原则。如果需要删除哪个播放器,只要对应删除SDKObject类即可,不用像在中间层一样在代码中删除,出错机率也大大降低。

如果在使用协议方案的同时再加一层中间层会怎样?

可以把协议比作一套算法,上层业务直接与中间层关联,在中间层可以去替换不同协议,就做到了算法上的改变,这种方案使程序结构更加严谨,替换方案更多,维护更加方便简洁。

项目demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值