設(shè)計模式系列文章
《iOS設(shè)計模式(1)簡單工廠模式》
《iOS設(shè)計模式(2)工廠模式》
《iOS設(shè)計模式(4)抽象工廠模式》
《iOS設(shè)計模式(5)策略模式》
《iOS設(shè)計模式(6)模板模式》
《iOS設(shè)計模式(7)建造者模式》
1.概念描述
在計算機編程中德崭,適配器模式(有時候也稱包裝樣式或者包裝)將一個類的接口適配成用戶所期待的进苍。一個適配允許通常因為接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中宇色。---百度百科
2.實例場景
最近視頻和直播非橙辶担火蟀俊,由于公司業(yè)務(wù)需要迅栅,小猿也有幸參與到了視頻和直播的開發(fā)中时呀。由于我們的項目是之前人留下來的舊代碼张漂,原項目中的人都走沒了,但是項目龐大谨娜,重新來做也不現(xiàn)實航攒,只能在原有的代碼基礎(chǔ)上修改和擴展新的需求。所有就遇到了一系列的坑瞧预。今天就講一個非常簡單的場景屎债,這個場景中就用到了適配器模式。
具體需求是這樣的:原有項目中使用的播放器(假如為AVPlayer)由于種種原因現(xiàn)在需要用其他的開源播放器(假如為ijkplayer)替換掉垢油。但是由于工期非常緊張盆驹,項目又龐大,之前的代碼通用性又不好滩愁,牽一發(fā)而動全身躯喇,重新來寫播放器這塊肯定不現(xiàn)實,各方面硝枉、各部門也不同意廉丽。怎么辦?只有接口不動妻味,內(nèi)部換芯正压,這樣改動幾乎不會影響到業(yè)務(wù)邏輯層和UI層。所有自然而然的就想到了適配器模式责球,做一個適配器焦履,用適配器來把新播放器適配舊接口。下面看具體實現(xiàn)雏逾。
3.代碼實現(xiàn)
原有的AVPlayer代理
@protocol AVPlayerProtocol <NSObject>
/**
* AVPlayer開啟視頻
*
* @return 描述(只為舉例需要)
*/
- (NSString *)a_play;
/**
* AVPlayer暫停視頻
*
* @return 描述(只為舉例需要)
*/
- (NSString *)a_pause; // 開啟視頻
/**
* AVPlayer停止播放
*
* @return 描述(只為舉例需要)
*/
- (NSString *)a_stop; // 開啟視頻
@end
原有的AVPlayer類
#import <Foundation/Foundation.h>
#import "AVPlayerProtocol.h"
@interface AVPlayer : NSObject<AVPlayerProtocol>
@end
#import "AVPlayer.h"
@implementation AVPlayer
// 開啟視頻
- (NSString *)a_play
{
return @"AVPlayer 播放視頻";
}
// 暫停視頻
- (NSString *)a_pause
{
return @"AVPlayer 暫停視頻";
}
// 停止視頻
- (NSString *)a_stop
{
return @"AVPlayer 停止視頻";
}
@end
原有的客戶端調(diào)用代碼
// 選擇AVPlayer
- (IBAction)btnAVPlayerEvent:(UIButton *)sender {
if (_player) {
_player = nil;
}
_player = [[AVPlayer alloc] init];
}
// 播放器播放視頻
- (IBAction)btnPlayerEvent:(UIButton *)sender {
_lbState.text = _player ? [_player a_play] : @"播放器為空";
}
// 播放器暫停視頻
- (IBAction)btnPauseEvent:(UIButton *)sender {
_lbState.text = _player ? [_player a_pause] : @"播放器為空";
}
// 播放器停止視頻
- (IBAction)btnStopEvent:(UIButton *)sender {
_lbState.text = _player ? [_player a_stop] : @"播放器為空";
}
下面是要適配的新播放器代理
@protocol IjkplayerProtocol <NSObject>
/**
* ijkplayer播放視頻
*
* @return 描述(只為舉例需要)
*/
- (NSString *)i_play;
/**
* ijkplayer暫停視頻
*
* @return 描述(只為舉例需要)
*/
- (NSString *)i_pause; // 開啟視頻
/**
* ijkplayer停止播放
*
* @return 描述(只為舉例需要)
*/
- (NSString *)i_stop; // 開啟視頻
@end
新播放器類
#import <Foundation/Foundation.h>
#import "IjkplayerProtocol.h"
@interface Ijkplayer : NSObject<IjkplayerProtocol>
@end
#import "Ijkplayer.h"
@implementation Ijkplayer
// 開啟視頻
- (NSString *)i_play
{
return @"Ijkplayer 播放視頻";
}
// 暫停視頻
- (NSString *)i_pause
{
return @"Ijkplayer 暫停視頻";
}
// 停止視頻
- (NSString *)i_stop
{
return @"Ijkplayer 停止視頻";
}
@end
大家可以清晰的看到嘉裤,每家播放器的大體功能基本上是一致的,這是作為一個播放器基本的屬性栖博,但是每家的播放器相同功能的接口名稱和類的實現(xiàn)方法則不盡相同屑宠。而且項目中業(yè)務(wù)層和UI層基本上是基于舊播放器(AVPlayer)的接口完成的(當然當初設(shè)計的時候沒有考慮到后面業(yè)務(wù)的擴展和變更)。所以這時候就要考慮建一個適配器類來對接新播放器和舊接口業(yè)務(wù)邏輯仇让。
適配器有兩個要求:
- 要實現(xiàn)舊播放器的代理接口典奉。保證上層調(diào)用者不受影響躺翻。
- 對接新播放器,完成舊接口和新播放器接口的轉(zhuǎn)換秋柄。
#import <Foundation/Foundation.h>
#import "AVPlayerProtocol.h"
#import "Ijkplayer.h"
@interface PlayerAdapter : NSObject<AVPlayerProtocol>
- (instancetype)initWithIjkplayer:(Ijkplayer *)ijkplayer;
@end
#import "PlayerAdapter.h"
@interface PlayerAdapter ()
{
Ijkplayer *_ijkplayer;
}
@end
@implementation PlayerAdapter
- (instancetype)initWithIjkplayer:(Ijkplayer *)ijkplayer
{
self = [super init];
if (self) {
_ijkplayer = ijkplayer;
}
return self;
}
// 開啟視頻
- (NSString *)a_play
{
return _ijkplayer.i_play;
}
// 暫停視頻
- (NSString *)a_pause
{
return _ijkplayer.i_pause;
}
// 停止視頻
- (NSString *)a_stop
{
return _ijkplayer.i_stop;
}
@end
這樣获枝,我們的適配器類的功能就完成了。這時候骇笔,客戶端的調(diào)用僅僅需要修改播放器的初始化就行了省店,其他的全不用動。
// 選擇AVPlayer
- (IBAction)btnAVPlayerEvent:(UIButton *)sender {
sender.selected = YES;
_btnIjkplayer.selected = NO;
if (_player) {
_player = nil;
}
_player = [[AVPlayer alloc] init]; // 之前的舊代碼
}
// 選擇Ijkplayer
- (IBAction)btnIjkplayerEvent:(UIButton *)sender {
sender.selected = YES;
_btnAVPlayer.selected = NO;
if (_player) {
_player = nil;
}
Ijkplayer *ijkplayer = [[Ijkplayer alloc] init]; //新代碼
_player = [[PlayerAdapter alloc] initWithIjkplayer:ijkplayer];
}
輸出結(jié)果
無縫切換笨触。