需求:最近在視頻播放中撮慨,遇到了播放器來回切換調(diào)試的問題,那么接下來,會介紹一下怎么做才會使代碼更加適用于業(yè)務(wù)且改動較小的方案柏卤。
場景一
- 只有騰訊播放SDK
方案一
直接在ViewController
初始化TencentSDK
省容,并實現(xiàn)對應(yīng)的播放
抖拴、暫停
、停止
腥椒、重置
等功能阿宅。
- (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];
}
這種使用方式是我剛進入職場使用的一種傻瓜式方案,開發(fā)快速笼蛛,簡單明了洒放,適用于一次性開發(fā),確定播放類型滨砍,且后期不會再改動的代碼往湿。
場景二
- 騰訊播放SDK
- 新浪播放SDK
- 最開始使用騰訊SDK榨为,后面由于業(yè)務(wù)需求,接入新浪SDK
方案一
ViewController
里面初始化TencentSDK
煌茴。因為業(yè)務(wù)更改随闺,ViewController
里面初始化SinaSDK
,在原來的業(yè)務(wù)邏輯里面替換代碼。
- (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的需求蔓腐,但是一個播放界面的業(yè)務(wù)不僅僅是播放
矩乐、暫停
、停止
回论、重置
等功能散罕,還會涉及到彈幕
、禮物
傀蓉、分享
等其他業(yè)務(wù)欧漱,隨著業(yè)務(wù)增多,ViewController
的代碼也會隨之變大葬燎,有時候我們在執(zhí)行暫停
時還會有其它操作误甚,這樣頻繁去更改代碼的行為并不友好,也增加了我們的工作量谱净,出錯機率也會隨之增大窑邦。
方案二
工作中我們在使用第三方框架時,提倡加一層中間層壕探,這樣在替換第三方框架時冈钦,可以減少業(yè)務(wù)代碼的更改,只需要中間層替換底層代碼李请,保持上層業(yè)務(wù)代碼不變瞧筛。
創(chuàng)建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懶加載导盅,根據(jù)PlayerInter
初始化傳進來的type
類型较幌,進行對應(yīng)的播放器使用。ViewController
的代碼屬于上層業(yè)務(wù)认轨,當播放更換時绅络,只需要PlayerInter
做更改月培,上層業(yè)務(wù)不需要更改嘁字。
- (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];
}
加了中間層,感覺方案上已經(jīng)盡善盡美杉畜,當播放器更換時纪蜒,只需要修改type
類型即可,并且也可以做到后臺控制使用哪種播放器此叠〈啃可是作為一個居安思危的程序員,我怎么能在這個時候就紅棗枸杞茶喝起來呢?我設(shè)想如果再有一種新的播放器的話代碼會怎么樣猬错。窗看。。
- 假設(shè)新增優(yōu)酷播放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
的上層業(yè)務(wù)代碼沒有做出任何改變显沈,這種中間層的方案也還是可以解決我剛才設(shè)想的問題,可是作為一個有強迫癥的程序員逢唤,if
else
過多也覺得會增加錯誤機率拉讯,如果接下來有第四、第五個播放器呢鳖藕?
方案三
播放器功能
- 播放
- 暫停
- 停止
- 重置
每個播放器都不外乎有以上幾個功能魔慷,那么是否可以通過一種新的方法來改變之前的策略呢?
創(chuàng)建PlayProtocol
協(xié)議著恩,協(xié)議有play
院尔、pause
、stop
喉誊、resume
等通用method
@protocol PlayProtocol <NSObject>
- (void)play;
- (void)pause;
- (void)stop;
- (void)resume;
創(chuàng)建TencentPlayObject
類召边,遵守PlayProtocol
協(xié)議,在TencentPlayObject
初始化TencentSDK
,并實現(xiàn)PlayProtocol
協(xié)議制定的方法裹驰。
- (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];
}
創(chuàng)建SinaPlayObject
和YouKuObject
隧熙,遵守PlayProtocol
,初始化相對應(yīng)的SDK,并實現(xiàn)協(xié)議方法幻林,方法同TencentPlayObject
一樣贞盯。
創(chuàng)建PlayerFactory
類,根據(jù)type
類型沪饺,用來生產(chǎn)對應(yīng)的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
的代碼進行調(diào)整
@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];
}
相比較之前的中間層方案躏敢,協(xié)議的方法更直接明了,從PlayerFactory
類中可以看出整葡,只有在生產(chǎn)player
的時候用到了if
else
判斷件余,其它只要各自遵守協(xié)議即可,代碼上更簡單流程遭居,出錯機率大大降低啼器。ViewController
業(yè)務(wù)層的代碼也保持了不變的原則。如果需要刪除哪個播放器俱萍,只要對應(yīng)刪除SDK
和Object
類即可端壳,不用像在中間層一樣在代碼中刪除,出錯機率也大大降低枪蘑。
如果在使用協(xié)議方案的同時再加一層中間層會怎樣损谦?
可以把協(xié)議比作一套算法岖免,上層業(yè)務(wù)直接與中間層關(guān)聯(lián),在中間層可以去替換不同協(xié)議照捡,就做到了算法上的改變颅湘,這種方案使程序結(jié)構(gòu)更加嚴謹,替換方案更多栗精,維護更加方便簡潔栅炒。