IOS 设计模式-适配器

1 适配器模式的概念
2 数据直接适配带来的困境
3 适配器模式实践
4 适配器模式的优缺点

我们大家都知道,平时我们的生活用电,不同的国家,电压是不同的,中国是220V,日本是100v,等等。那么我们使用的苹果手机充电,不管在哪个国家,同一个充电器,都可以给手机来充电,那么这个充电器可以适配不同的电压,那么这个充电器就相当于一个适配器。
1 适配器模式:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。

2 数据直接适配带来的困境
a)直接赋值的弊端
b)用对象赋值的灵活性
c)如何降低数据层与视图层的耦合度
实例讲解:
1 直接赋值的弊端
我们假设在视图页面显示一个明信片,明信片上面显示 名字 一个分割线,一个日期,我们知道,不同的明信片 它的样式是不一样的,下面是一个明信片类

#import <UIKit/UIKit.h>
#define  BUSINESS_FRAME  CGRectMake(0, 0, 200, 130)

@interface BusinessCardView : UIView

/**
 *  名字
 */
@property (nonatomic, strong) NSString *name;

/**
 *  线条颜色
 */
@property (nonatomic, strong) UIColor  *lineColor;

/**
 *  电话号码
 */
@property (nonatomic, strong) NSString *phoneNumber;
BusinessCardView.m
#import "BusinessCardView.h"

@interface BusinessCardView ()

@property (nonatomic, strong) UILabel  *nameLabel;
@property (nonatomic, strong) UIView   *lineView;
@property (nonatomic, strong) UILabel  *phoneNumberLabel;

@end
@implementation BusinessCardView

#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {

        [self setup];
    }
    
    return self;
}

- (void)setup {

    self.backgroundColor     = [UIColor whiteColor];
    self.layer.borderWidth   = 0.5f;
    self.layer.shadowOpacity = 0.5f;
    self.layer.shadowOffset  = CGSizeMake(5, 5);
    self.layer.shadowRadius  = 1.f;
    self.layer.shadowColor   = [UIColor grayColor].CGColor;
    
    
    self.nameLabel      = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, 150, 25)];
    self.nameLabel.font = [UIFont fontWithName:@"Avenir-Light" size:20.f];
    [self addSubview:self.nameLabel];
    
    
    self.lineView                 = [[UIView alloc] initWithFrame:CGRectMake(0, 45, 200, 5)];
    [self addSubview:self.lineView];
    
    
    self.phoneNumberLabel               = [[UILabel alloc] initWithFrame:CGRectMake(41, 105, 150, 20)];
    self.phoneNumberLabel.textAlignment = NSTextAlignmentRight;
    self.phoneNumberLabel.font          = [UIFont fontWithName:@"AvenirNext-UltraLightItalic" size:16.f];
    [self addSubview:self.phoneNumberLabel];
}
#pragma mark - 重写setter,getter方法
@synthesize name        = _name;
@synthesize lineColor   = _lineColor;
@synthesize phoneNumber = _phoneNumber;

- (void)setName:(NSString *)name {
    
    _name           = name;
    _nameLabel.text = name;
}

- (NSString *)name {

    return _name;
}

- (void)setLineColor:(UIColor *)lineColor {

    _lineColor                = lineColor;
    _lineView.backgroundColor = _lineColor;
}

- (UIColor *)lineColor {

    return _lineColor;
}

- (void)setPhoneNumber:(NSString *)phoneNumber {

    _phoneNumber           = phoneNumber;
    _phoneNumberLabel.text = phoneNumber;
}

- (NSString *)phoneNumber {

    return _phoneNumber;
}

那么我们将这个明信片显示在当前视图上:

ViewController.m

#import "ViewController.h"
#import "BusinessCardView.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];

    BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
    cardView.center            = self.view.center;
    
    //直接赋值
    cardView.name        = @"JiKeXueYuan";
    cardView.lineColor   = [UIColor redColor];
    cardView.phoneNumber = @"101 - 5687 - 000";
    [self.view addSubview:cardView];
}

实际开发过程中,我们从服务器端接收的数据往往都是对象,或则列表数据,那么我们就不能直接给cardView.name = @"JiKeXueYuan"; 而可能会是cardView.name = someModel.name; 如果使用明信片的地方很多,那么我们这种直接赋值的地放都需要修改,大大增加了工作量。那么针对于这种问题,我们可以把数据封装成一个对象,这样或许能够更方便管理一些。我们新建一个model类:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface Model : NSObject

/**
 *  名字
 */
@property (nonatomic, strong) NSString *name;

/**
 *  线条颜色
 */
@property (nonatomic, strong) UIColor  *lineColor;

/**
 *  电话号码
 */
@property (nonatomic, strong) NSString *phoneNumber;

@end
Model.m
#import "Model.h"

@implementation Model

@end

BusinessCardView.m 添加一个方法

-(void)loadData:(Model *)data
{
  self.name = data.name;
  self.phoneNumbr = data.phoneNumber;
  self.lineColor = data.lineColor;
}
ViewController.m

#import "ViewController.h"
#import "BusinessCardView.h"

#import "Model.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];

    BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
    cardView.center            = self.view.center;
    
    // 以对象赋值
    Model *model      = [[Model alloc] init];
    model.name        = @"JiKeXueYuan";
    model.lineColor   = [UIColor redColor];
    model.phoneNumber = @"101 - 5687 - 000";
    [cardView loadData:model];
    [self.view addSubview:cardView];
}

使用model减少了cardView 于当前要显示的视图的耦合度,但是又会出现一些灵活性的问题,cardView只能加载model类型的数据,现在假如我又有一个新的model NewModel, 那么cardView要显示newModel的数据,那么只能新加一个方法,假如有还有第三个model 第四个model,那么工作量就大多了。这是直接赋值,和对象赋值的一些弊端,那么下面我们将采用适配器模式来解决这种问题。

3 使用适配器
a)创建抽象适配器对象
b)适配器与视图层建立输出联系
c)适配器与数据层建立输入联系
适配器可以分为两种,一种是类适配器,一种是对象适配器。
1 创建抽象适配器,OC中抽象类一般使用协议来实现。

BusinessCardAdapterProtocol.h
#import <Foundation/Foundation.h>

@protocol BusinessCardAdapterProtocol <NSObject>

- (NSString *)name;

- (UIColor *)lineColor;

- (NSString *)phoneNumber;

@end

接下来我们让BusinessCardView 遵循这个协议

BusinessCardView.h
#import <UIKit/UIKit.h>
#import "BusinessCardAdapterProtocol.h"

#define  BUSINESS_FRAME  CGRectMake(0, 0, 200, 130)

@interface BusinessCardView : UIView

/**
 *  名字
 */
@property (nonatomic, strong) NSString *name;

/**
 *  线条颜色
 */
@property (nonatomic, strong) UIColor  *lineColor;

/**
 *  电话号码
 */
@property (nonatomic, strong) NSString *phoneNumber;

/**
 *  加载数据(实现了BusinessCardAdapterProtocol协议的数据)
 *
 *  @param data 实现了BusinessCardAdapterProtocol协议的数据
 */
- (void)loadData:(id <BusinessCardAdapterProtocol>)data;

@end
BusinessCardView.m

#import "BusinessCardView.h"

@interface BusinessCardView ()

@property (nonatomic, strong) UILabel  *nameLabel;
@property (nonatomic, strong) UIView   *lineView;
@property (nonatomic, strong) UILabel  *phoneNumberLabel;

@end
@implementation BusinessCardView

#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {

        [self setup];
    }
    
    return self;
}

- (void)setup {

    self.backgroundColor     = [UIColor whiteColor];
    self.layer.borderWidth   = 0.5f;
    self.layer.shadowOpacity = 0.5f;
    self.layer.shadowOffset  = CGSizeMake(5, 5);
    self.layer.shadowRadius  = 1.f;
    self.layer.shadowColor   = [UIColor grayColor].CGColor;
    
    
    self.nameLabel      = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, 150, 25)];
    self.nameLabel.font = [UIFont fontWithName:@"Avenir-Light" size:20.f];
    [self addSubview:self.nameLabel];
    
    
    self.lineView                 = [[UIView alloc] initWithFrame:CGRectMake(0, 45, 200, 5)];
    [self addSubview:self.lineView];
    
    
    self.phoneNumberLabel               = [[UILabel alloc] initWithFrame:CGRectMake(41, 105, 150, 20)];
    self.phoneNumberLabel.textAlignment = NSTextAlignmentRight;
    self.phoneNumberLabel.font          = [UIFont fontWithName:@"AvenirNext-UltraLightItalic" size:16.f];
    [self addSubview:self.phoneNumberLabel];
}

- (void)loadData:(id <BusinessCardAdapterProtocol>)data {

    self.name        = [data name];
    self.lineColor   = [data lineColor];
    self.phoneNumber = [data phoneNumber];
}

#pragma mark - 重写setter,getter方法
@synthesize name        = _name;
@synthesize lineColor   = _lineColor;
@synthesize phoneNumber = _phoneNumber;

- (void)setName:(NSString *)name {
    
    _name           = name;
    _nameLabel.text = name;
}

- (NSString *)name {

    return _name;
}

- (void)setLineColor:(UIColor *)lineColor {

    _lineColor                = lineColor;
    _lineView.backgroundColor = _lineColor;
}

- (UIColor *)lineColor {

    return _lineColor;
}

- (void)setPhoneNumber:(NSString *)phoneNumber {

    _phoneNumber           = phoneNumber;
    _phoneNumberLabel.text = phoneNumber;
}

- (NSString *)phoneNumber {

    return _phoneNumber;
}

@end

创建一个抽象的适配器对象

BusinessCardAdapter.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "BusinessCardAdapterProtocol.h"

@interface BusinessCardAdapter : NSObject <BusinessCardAdapterProtocol>

/**
 *  输入对象
 */
@property (nonatomic, weak) id data;

/**
 *  与输入对象建立联系
 *
 *  @param data 输入的对象
 *
 *  @return 实例对象
 */
- (instancetype)initWithData:(id)data;

@end
BusinessCardAdapter.m
#import "BusinessCardAdapter.h"

@implementation BusinessCardAdapter

- (instancetype)initWithData:(id)data {
    
    self = [super init];
    if (self) {
        
        self.data = data;
    }
    
    return self;
}

- (NSString *)name {

    return nil;
}

- (UIColor *)lineColor {

    return nil;
}

- (NSString *)phoneNumber {

    return nil;
}

@end

创建一个model的适配器

ModelAdapter.h
#import "BusinessCardAdapter.h"

/**
 *  类适配器
 */
@interface ModelAdapter : BusinessCardAdapter

@end
ModelAdapter.m
#import "ModelAdapter.h"
#import "Model.h"

@implementation ModelAdapter

- (instancetype)initWithData:(id)data {
    
    self = [super init];
    if (self) {
        
        self.data = data;
    }
    
    return self;
}

- (NSString *)name {
    
    Model *data = self.data;
    
    return data.name;
}

- (UIColor *)lineColor {
    
    Model *data = self.data;
    
    return data.lineColor;
}

- (NSString *)phoneNumber {
    
    Model *data = self.data;
    
    return data.phoneNumber;
}

@end

让我们再次展示这个cardView

ViewController.h
#import "ViewController.h"
#import "BusinessCardView.h"

#import "Model.h"
#import "ModelAdapter.h"
@interface ViewController ()

@end
@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];

    BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
    cardView.center            = self.view.center;
    
   
    Model *cardModel  = [[Model alloc] init];
    cardModel.name           = @"JiKeXueYuan";
    cardModel.lineColor = @"black";
    cardModel.phoneNumber    = @"101 - 5687 - 000";
    
    // 与输入建立联系
    BusinessCardAdapter *modelAdapter = [[ModelAdapter alloc] initWithData:model];
    
    // 与输出建立联系
    [cardView loadData:modelAdapter];
    
    [self.view addSubview:cardView];
}
@end

这样我们整个适配器模式就已经完成了,下面假如我们又有一个新的model newModel要适配,那么我们就可以这样做

NewCardModel.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface NewCardModel : NSObject

/**
 *  名字
 */
@property (nonatomic, strong) NSString *name;

/**
 *  线条颜色
 */
@property (nonatomic, strong) NSString *colorHexString;

/**
 *  电话号码
 */
@property (nonatomic, strong) NSString *phoneNumber;

@end
NewCardModel.m
#import "NewCardModel.h"

@implementation NewCardModel

@end
NewCardModelAdapter.h
#import "BusinessCardAdapter.h"

/**
 *  类适配器
 */
@interface NewCardModelApater : BusinessCardAdapter

@end
NewCardModelAdapter.m

#import "NewCardModelApater.h"
#import "NewCardModel.h"

@implementation NewCardModelApater

- (instancetype)initWithData:(id)data {
    
    self = [super init];
    if (self) {
        
        self.data = data;
    }
    
    return self;
}

- (NSString *)name {
    
    NewCardModel *data = self.data;
    
    return data.name;
}

- (UIColor *)lineColor {
    
    NewCardModel *data = self.data;
    
    // todo
    
    if ([data.colorHexString isEqualToString:@"black"]) {
        
        return [UIColor blackColor];
        
    } else {
        
        return [UIColor redColor];
        
    }
}

- (NSString *)phoneNumber {
    
    NewCardModel *data = self.data;
    
    return data.phoneNumber;
}

@end
ViewController.m
#import "ViewController.h"
#import "BusinessCardView.h"

#import "NewCardModel.h"
#import "NewCardModelApater.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];

    BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
    cardView.center            = self.view.center;
    NewCardModel *newCardModel  = [[NewCardModel alloc] init];
    newCardModel.name           = @"JiKeXueYuan";
    newCardModel.colorHexString = @"black";
    newCardModel.phoneNumber    = @"101 - 5687 - 000";
    
    // 与输入建立联系
    BusinessCardAdapter *modelAdapter = [[NewCardModelApater alloc] initWithData:model];
    
    // 与输出建立联系
    [cardView loadData:modelAdapter];
    
    [self.view addSubview:cardView];
}

@end

这样我们就适配了一个新的model类,在此我们要介绍一下,类适配器,对象适配器。
像上面这样,我们对于每个model类,都建立了一个相对应的adaper类,那么这种方式就是类类适配器,这样也许每个model,我们都要建立也个adapter,有可能我们觉得会很麻烦,那么我们可不可以只使用一个适配器来适配所有的model呢,答案是可以的,下面我们建立一个CardAdapter 适配器来适配所有的model

CardAdapter.h
#import "BusinessCardAdapter.h"

/**
 *  对象适配器
 */
@interface CardAdapter : BusinessCardAdapter

@end
CardAdapter.m
#import "CardAdapter.h"
#import "Model.h"
#import "NewCardModel.h"

@implementation CardAdapter

- (instancetype)initWithData:(id)data {
    
    self = [super init];
    if (self) {
        
        self.data = data;
    }
    
    return self;
}

- (NSString *)name {
    
    NSString *name = nil;
    
    if ([self.data isMemberOfClass:[Model class]]) {
        
        Model *model = self.data;
        name         = model.name;
        
    } else if ([self.data isMemberOfClass:[NewCardModel class]]) {
        
        NewCardModel *model = self.data;
        name                = model.name;
        
    }
    
    return name;
}

- (UIColor *)lineColor {
    
    UIColor *lineColor = nil;
    
    if ([self.data isMemberOfClass:[Model class]]) {
        
        Model *model = self.data;
        lineColor    = model.lineColor;
        
    } else if ([self.data isMemberOfClass:[NewCardModel class]]) {
        
        NewCardModel *model = self.data;
        
        if ([model.colorHexString isEqualToString:@"black"]) {
            
            lineColor = [UIColor blackColor];
            
        } else {
            
            lineColor = [UIColor redColor];
            
        }
    }
    
    return lineColor;
}

- (NSString *)phoneNumber {
    
    NSString *phoneNumber = nil;
    
    if ([self.data isMemberOfClass:[Model class]]) {
        
        Model *model = self.data;
        phoneNumber  = model.phoneNumber;
        
    } else if ([self.data isMemberOfClass:[NewCardModel class]]) {
        
        NewCardModel *model = self.data;
        phoneNumber         = model.phoneNumber;
        
    }
    
    return phoneNumber;
}

@end
ViewController.m
#import "ViewController.h"
#import "BusinessCardView.h"

#import "Model.h"
#import "ModelAdapter.h"

#import "NewCardModel.h"
#import "NewCardModelApater.h"

#import "CardAdapter.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];

    BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
    cardView.center            = self.view.center;
    
    // 直接赋值
//    cardView.name        = @"JiKeXueYuan";
//    cardView.lineColor   = [UIColor redColor];
//    cardView.phoneNumber = @"101 - 5687 - 000";
    
    // 以对象赋值
    Model *model      = [[Model alloc] init];
    model.name        = @"JiKeXueYuan";
    model.lineColor   = [UIColor redColor];
    model.phoneNumber = @"101 - 5687 - 000";
    
    NewCardModel *newCardModel  = [[NewCardModel alloc] init];
    newCardModel.name           = @"JiKeXueYuan";
    newCardModel.colorHexString = @"black";
    newCardModel.phoneNumber    = @"101 - 5687 - 000";
    
    // 与输入建立联系
    BusinessCardAdapter *modelAdapter = [[CardAdapter alloc] initWithData:model];
    
    // 与输出建立联系
    [cardView loadData:modelAdapter];
    
    [self.view addSubview:cardView];
}

@end

这种适配器就叫对象适配器,其实它跟类适配器差不多,只不过,它只是在一个适配器中,判断了一下model的类型,然后处理不同的数据,实际应用中,至于使用类适配器,还是对象适配器,视情况而定,如果适配情况比较少,那么我们可以使用对象适配器,这样可以减少适配器文件的数量,如果适配情况比较多,那么我们可以使用类适配器,这样逻辑显得比较清晰一下,如果还使用对象适配器的话,就会导致对象适配器判断逻辑很多,对象庞大臃肿,今后不容易维护修改。

4 适配器模式的优缺点
优点:降低模块耦合度,可以让不修改现有模块的情况下,从而实现新的功能
缺点:对于不懂适配器模式的人来说 可读性比较差,java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将一个适配者类和他的子类同时适配到目标接口。

总结:实际使用中,我们什么时候使用适配器模式呢,一个简单的视图赋值,我们使用适配器模式需要写那么多代码,有时候我们觉得可能太麻烦,个人认为,如果我们在写一些代码给很多人用的时候,比如自己写的开源框架,那么我们就要考虑使用适配器等这些设计模式,良好的模块划分,高度的可扩展性,别人用起来才会方便快捷。如果写的代码不给别人用,比如工作中的代码实现,当人员就一两个人的时候,那么为了能够按时完成工作任务,那么这种简单粗暴的赋值方式也未尝不可。但是平时工作中,能使用设计模式有条件的情况下还是应该使用的,毕竟我们一直做代码的搬运工,编程是门艺术,我们要去实现她的美。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,478评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,825评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,482评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,726评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,633评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,018评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,168评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,320评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,264评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,288评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,995评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,587评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,909评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,284评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,862评论 2 339

推荐阅读更多精彩内容