设计模式系列文章
《iOS设计模式(1)简单工厂模式》
《iOS设计模式(2)工厂模式》
《iOS设计模式(4)抽象工厂模式》
《iOS设计模式(5)策略模式》
《iOS设计模式(6)模板模式》
《iOS设计模式(7)建造者模式》
1.概念描述
在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。---百度百科
2.实例场景
最近视频和直播非常火,由于公司业务需要,小猿也有幸参与到了视频和直播的开发中。由于我们的项目是之前人留下来的旧代码,原项目中的人都走没了,但是项目庞大,重新来做也不现实,只能在原有的代码基础上修改和扩展新的需求。所有就遇到了一系列的坑。今天就讲一个非常简单的场景,这个场景中就用到了适配器模式。
具体需求是这样的:原有项目中使用的播放器(假如为AVPlayer)由于种种原因现在需要用其他的开源播放器(假如为ijkplayer)替换掉。但是由于工期非常紧张,项目又庞大,之前的代码通用性又不好,牵一发而动全身,重新来写播放器这块肯定不现实,各方面、各部门也不同意。怎么办?只有接口不动,内部换芯,这样改动几乎不会影响到业务逻辑层和UI层。所有自然而然的就想到了适配器模式,做一个适配器,用适配器来把新播放器适配旧接口。下面看具体实现。
3.代码实现
原有的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
原有的客户端调用代码
// 选择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
大家可以清晰的看到,每家播放器的大体功能基本上是一致的,这是作为一个播放器基本的属性,但是每家的播放器相同功能的接口名称和类的实现方法则不尽相同。而且项目中业务层和UI层基本上是基于旧播放器(AVPlayer)的接口完成的(当然当初设计的时候没有考虑到后面业务的扩展和变更)。所以这时候就要考虑建一个适配器类来对接新播放器和旧接口业务逻辑。
适配器有两个要求:
- 要实现旧播放器的代理接口。保证上层调用者不受影响。
- 对接新播放器,完成旧接口和新播放器接口的转换。
#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
这样,我们的适配器类的功能就完成了。这时候,客户端的调用仅仅需要修改播放器的初始化就行了,其他的全不用动。
// 选择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];
}
输出结果
无缝切换。