适配器模式(Adapter)

定义

有一天,你要用到一个功能,而恰好你已经有一个实现了该功能的类,但是该类的接口和你所需要使用的接口不谦容,怎么做?

把原来的类拿过来修改还是怎么的?

修改的话,要改变原有的接口,也许修改起来比较难,也许你已经忘了该类是怎么实现的,也许以后的某一天你又要在其他程序里面使用该类,而那个时候要使用的接口又是另外的一种方式。所以,直接修改的话,未免太傻太天真,而且难免会修改得千真万确。在编程经验方面,一般代码的层次越多,其兼容性也就会更好,能满足使得代码的复用率更高,所以不妨试试在我们使用代码的地方和已经有的类之间不妨增加一层,这样也许就能做到很好的兼容了。

由此我们引出了适配器设计模式,所谓适配器模式就是将一个类的接口转换成客户希望的所希望另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。适配器会实现客户端所期望的接口,继而把客户端和另外一个满足功能要求但是接口不同的类连接起来,适配器是客户端所知道的目标接口对象和已有类的中间人。适配器的原定义如下:


在适配器模式中,有三个角色,目标接口(Target)、适配器(Adaptee)、需要适配的类(Adapter),在上述的场景中,你已经有了的类为Adaptee,你所需要的接口为Target,而增加的那一层则是Adapter。实现适配器模式主要有两种方式:类适配器和对象适配器,其主要区别在于"is a"和"has a"。


类适配器

在最开始的设计模式一书中,类适配器模式是通过多继承方式实现的。C++这门编程语言不像Java、Objective-C具备协议(protocol)这一特性,因此定义客户端所需要的接口是通过定义抽象类实现的,因此配器是通过继承实现的,同时继承抽象接口类以及Adaptee类(目测也可以通过继承抽象接口类和包含目标类实现)。Objective-C因为具备协议这一特性,所以目标接口可通过定义接口实现,然后我们所需要的类适配器实现该协议并且继承Adaptee类,因此类适配器既是Target类型也是Adaptee类型。类适配器的主要原理图如下所示:


适配器重写了Target的request方法,没有重写Adaptee的sepcificReques接口,实际上,适配器实现的request会直接或者间接地调用Adaptee的sepcificReques接口,向父类发送sepcificReques消息


对象适配器

对象适配器不像类适配器,它不是通过继承的方式来实现的,而是通过组合引用的方式来实现,其实现原理图如下所示:


对象适配器的实现和类适配器的实现很相似,唯一的不同是对象适配和Adaptee的关系不再是"is a",而变成了"has a"。对象适配器需要保持一个对Adaptee对象的引用,这样的话,在request方法中就可以通过向该引用发送specialRequest消息了,而不像是类适配器通过super向父类发送消息那样。


类适配器VS对象适配器

类适配器对象适配器
只针对单一具体的Adaptee类,把Adaptee适配到Target能适配多个Adaptee类及其子类
易于重载Adaptee的行为难于重载Adaptee的方法,需要借助它的子类对象而不是Adaptee本身
只有一个Adapter对象,无需额外的指针间接访问Adaptee需要额外的指针间接地访问Adaptee并适配其方法

适配器使用场景

参考自设计模式学习笔记-适配器模式

  • 程序需要重用现有的类,而这些类的接口不符合当前代码所定义的接口。
  • 想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
  • 重构两个所做的事情相同或相似,但是具有不同接口的类。
  • 旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候。

代码示例

Target

本例是根据上图中的适配器原理图来简示适配器的,比较简单。假设客户端已经知道的接口定义,即协议如下所示:
//
//  Target.h
//  AdapterDemo
//
//  Created by God Lin on 15/1/17.
//  Copyright (c) 2015年 arbboter. All rights reserved.
//

#import <Foundation/Foundation.h>

// 客户端所需要的接口定义
@protocol Target <NSObject>

@required
- (void) request;

@end

Adaptee

客户端需要通过 request接口执行自己的程序,而我们现有的实现程序功能的类Adaptee不具有 request接口,定义如下:
#import <Foundation/Foundation.h>

@interface Adaptee : NSObject

// 原有的接口,和需要的接口不兼容
- (void) specialRequest;

@end
#import "Adaptee.h"

@implementation Adaptee

- (void) specialRequest
{
    NSLog(@"Adaptee interface");
}

@end
可以发现,Adaptee不具有 request接口,但是有接口 specialRequest接口实现了客户端所需要的功能,如果不使用适配器模式的话,客户端需要通过调用 specialRequest,但是现在我们的程序要求客户端必须调用 request方法,所以需要做适配了。

类适配器

如果采用类适配器,其该类适配器的定义如下:
#import "Adaptee.h"
#import "Target.h"

@interface ClassAdapter : Adaptee <Target>

@end
#import "ClassAdapter.h"

@implementation ClassAdapter
- (void) request
{
    // 类适配器因为继承自Adaptee
    // 故调用向父类发送消息
    [super specialRequest];
}
@end
上述代码中,类适配器通过继承要适配的对象Adaptee,同时实现了客户端所需要的接口协议Target,然后再实现中重写了Target中的方法 request,使用父类 super调用Adaptee对象的 sepecialRequest

对象适配器

如果使用对象适配器模式,其定义如下所示:
#import "Adaptee.h"
#import "Target.h"

@interface ObjectAdapter : NSObject <Target>

// 组合引用Adaptee对象,为了简化这里直接在此处声明一个成员
// 变量,实际上该对象可以在其他地方创建,这里引用就好了的
@property (nonatomic, strong) Adaptee* adaptee;

@end
#import "ObjectAdapter.h"

@implementation ObjectAdapter

- (id) init
{
    if(self = [super init])
    {
        _adaptee = [[Adaptee alloc] init];
    }
    return self;
}

- (void) request
{
    // 对象适配器模式通过向Adaptee对象
    // 发送消息
    [self.adaptee specialRequest];
}

@end
上述代码中,对象适配器使用引用的对象调用Adaptee对象的 sepecialRequest

Client

因为使用了适配器模式,所以客户端可直接使用适配器来调用接口request执行所需要的功能,如下所示:
#import "Client.h"
#import "ClassAdapter.h"
#import "ObjectAdapter.h"

@implementation Client

- (void)testAdapter
{
    id<Target> adapter = nil;
    
#if 1
    // 类适配器
    ClassAdapter* classAdapter =  [[ClassAdapter alloc] init];
    adapter = classAdapter;
#else
    // 对象适配器
    ObjectAdapter* objectAdapter = [[ObjectAdapter alloc] init];
    adapter = objectAdapter;
#endif
    
    // 因为创建了适配器,所以客户端可直接通过
    // 适配器去调用Adaptee的方法,适配器的方法是客户端知道的
    // 使用客户端知道的接口调用
    [adapter request];
}

@end
这样,即使需要适配另外一个Adaptee类,客户端代码也是不需要修改调用的,所以简单多了。

总结

使用适配器模式优点多多,如下所示:
  • 通过适配器,增强代码的复用率,兼容新旧接口。客户端可以调用同一接口,因而对客户端来说是透明的。
  • 复用了现存的类,解决了现存类和复用环境要求不一致的问题。
  • 一个对象适配器可以把多个不同的适配者类适配到同一个目标
通过适配器模式,可以兼容新旧代码,复用各种代码,减少代码的维护和编写成本,通过适配器这个插头转换器,再也不用担心接口不兼容的问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值