在生活中,想用苹果充电线给安卓的手机充电时,因为两者的接口不一样,会导致充电口无法进行匹配, 这时候,就需要适配器,将安卓的充电口转化为苹果的接口,这样就可以充电啦.已有的类与新的接口不兼容问题是很普遍的, 人们在日常的生产中,为找到了一个解决方案,以上面的例子引出今天要讲的设计模式--适配器模式.

定义与结构

1.1 定义

适配器模式将一个类的接口变成客户端所期待的另种接口, 从而使原本的接口不匹配无法在一起工作的两个类能够一起工作.

在适配器模式,存在两种不同的模式结构: 类的适配器模式和对象的适配器模式.

1.2 对象的适配器模式

1.2.1 适配器模式设计三个角色

  1. Target(目标抽象类): 目标抽象类定义为客户所需接口, 是个抽象类或者接口, 也可以是具体类. 例如对于上面的例子,客户端的目标是给只接受安卓充电口的安卓手机充电, 所以目标抽象类就是安卓充电线的接口.
  2. Adaptee(适配者类): 适配者类是被适配的角色, 定义了一个已经存在的接口, 接口需要适配, 适配者类一般是一个具体类, 包含了客户希望使用的业务方法. 例如苹果充电线就是适配者类.
  3. Adapter(适配器类): 通过需要包装一个需要适配的对象,将原接口转化成目标接口. 例如为了充电, 需要一个适配器, 使之一边可以连接安卓充电的接口, 一边可以连接苹果充电线的接口.

1.2.2 UML图

1.2.3 代码示例

(1) Target类

public class Android {
public void isAndroid(){
System.out.println("这是一个只接受安卓充电线的插口");
}
}

(2)Adaptee类

public class Iphone {
public void isIphone(){
System.out.println("这是一个适配苹果充电线的插口");
}
}

(3)Adapter类: 把两者进行适配

/**
* 适配器,作为中间件,把他们进行适配
*/
public class Adapter extends Android{
private Iphone iphone; public Adapter(Iphone iphone){
this.iphone = iphone;
} @Override
public void isAndroid() {
iphone.isIphone();
}
}

(4) 测试

public class Demo {
public static void main(String[] args){
Android android = new Adapter(new Iphone());
//调用的是安卓的接口,但实际上
//确实一个可以接受苹果充电器的接口
android.isAndroid();
}
}

对于这种对象的适配器模式, 实际上就是通过一个适配器类,把目标类和需要被适配的类进行组合。所以适配器类Adapter一般需要继承或实现Targert,并且还得持有Adaptee的实例引用。

1.3 类的适配器模式

1.3.1 定义

除了上面的对象适配器模式之外, 还有另外一种类适配器模式. 在这种模式中, Adapter不持有Adaptee的实例引用,而是直接继承了Adaptee类, 然后再实现Target接口. 或者直接继承Adaptee类和Target类, 但由于Java不支持多重继承, 所以只能实现Target的方式.

1.3.2 代码

(1) Target接口类

interface Android {
void isAndroid();
}

(2) Adaptee类

public class Iphone {
public void isIphone(){
System.out.println("这是一个适配苹果充电线的接口");
}
}

(3) Adapter类:继承Adaptee,实现Target

/**
* 适配器,把安卓手机的插口转化为可以用苹果充电线充电的接口
*/
public class Adapter extends Iphone implements Android{
@Override
public void isAndroid() {
//直接调用
isIphone();
}
}

(4) 测试类

public class Demo {
public static void main(String[] args){
Android android = new Adapter();
android.isAndroid();
}
}

这两种最重要的区别是:

对象适配器模式通过组合来实现适配器功能;

类适配器模式通过多继承来实现Target来实现适配器功能.

实例

2.1  适用型

  1. 你想使用一个已经存在的类,而它的接口不符合你的需求,即已有类的接口与需求不匹配
  2. 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
  3. (仅适用于对象Adapter)你想使用一些已存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口,对象适配器可以适配它的父类接口

2.2 委托

适配器模式是把类的接口变换为客户端要求的另一种接口,这里的客户端它们是什么?它们是Cocoa Touch框架中的类,那么此处什么是Target呢?是一个委托协议,实现协议的具体类会是个适配器,那么什么是与框架不匹配而需要适配的类呢?应用程序中的其他类,所以为什么委托模式其实是适配器模式。我们在Cocoa Touch框架中见过的许多框架类,是用协议中定义的某种形式的委托来实现的,我们可以把自己的委托实现为适配器。

2.3 简单实用

//协议 角色中的Target
protocol Target{
func userExpectInterface()
} //类 角色中的Adaptee
class Adaptee: NSObject {
func doSomething(){
print("adaptee doing something!")
}
} //类适配器 经常父类,遵守协议
class ClassAdapter: Adaptee,Target {
func userExpectInterface() {
super.doSomething()
}
} //对象适配器 遵守协议,拥有其它类对象,间接引用
class ObjectAdapter:Target{
let adaptee = Adaptee()
func userExpectInterface() {
adaptee.doSomething()
}
}

2.4 Demo讲解

我们经常需要使用第三方的服务,可以使用适配器模式对第三方的接口进行适配,比如:使用谷歌登录

1. 声明协议

// target
public protocol AuthenticationService {
func login(email: String, password: String, success: @escaping (User, Token) -> Void, failure: @escaping (Error?) -> Void)
}

2. 原有的登录方法

// adaptee
public class GoogleAuthenticator {
public func login(email: String, password: String, completion: @escaping (GoogleUser?, Error?) -> Void) {
let token = "special-token-value"
let user = GoogleUser(email: email, password: password, token: token)
completion(user, nil)
}
}

3、相关数据结构

public struct GoogleUser {
public var email: String
public var password: String
public var token: String
} public struct User {
public let email: String
public let password: String
} public struct Token {
public let value: String
}

4、适配器类

// adapter
public class GoogleAuthenticatorAdapter: AuthenticationService {
private var authenticator = GoogleAuthenticator()
public func login(email: String, password: String, success: @escaping (User, Token) -> Void, failure: @escaping (Error?) -> Void) {
authenticator.login(email: email, password: password) { (googleUser, error) in
guard let googleUser = googleUser else {
failure(error)
return
}
let user = User(email: email, password: password)
let token = Token(value: googleUser.token)
success(user, token)
}
}
}

5、简单实用

var authService: AuthenticationService = GoogleAuthenticatorAdapter()

authService.login(email: "user@example.com", password: "password", success: { (user, token) in
print("auth succeeded: \(user.email), \(token.value)")
}) { (error) in
print("auth failed")
}

优缺点

3.1 优点

  1. 更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
  2. 更好的扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
  3. 灵活性非常好:不想要适配器时,删掉这个适配器就好了,其他代码不用改。

3.2 缺点

  • 过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

总结

适配器模式有点如上,其缺点也存在,很多东西本来可以直接了当,用了适配器后就多了一大坨代码。当然如果把上面的实现在拆分下文件,那么显然如果不知道适配器原理的人 就很难理解代码why了。但是对于项目的长远来看,如果可以写出可变性好的代码,偶尔降低代码的可读性 也是可以接受的,毕竟这些模式都很金典,看不懂只能说明学得还不够多。

适配器模式(Adapter Pattern)--设计模式的更多相关文章

  1. 乐在其中设计模式(C#) - 适配器模式(Adapter Pattern)

    原文:乐在其中设计模式(C#) - 适配器模式(Adapter Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 适配器模式(Adapter Pattern) 作者:webabc ...

  2. 怎样让孩子爱上设计模式 —— 7.适配器模式(Adapter Pattern)

    怎样让孩子爱上设计模式 -- 7.适配器模式(Adapter Pattern) 标签: 设计模式初涉 概念相关 定义: 适配器模式把一个类的接口变换成client所期待的还有一种接口,从而 使原本因接 ...

  3. 设计模式系列之适配器模式(Adapter Pattern)——不兼容结构的协调

    模式概述 模式定义 模式结构图 模式伪代码 类适配器,双向适配器,缺省适配器 类适配器 双向适配器 缺省适配器 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 主要缺点 适 ...

  4. 二十四种设计模式:适配器模式(Adapter Pattern)

    适配器模式(Adapter Pattern) 介绍将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.示例有一个Message实体类 ...

  5. 设计模式 - 适配器模式(adapter pattern) 具体解释

    适配器模式(adapter pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 适配器模式(adapter pattern): 将一个类的接 ...

  6. 【设计模式】适配器模式 Adapter Pattern

    适配器模式在软件开发界使用及其广泛,在工业界,现实中也是屡见不鲜.比如手机充电器,笔记本充电器,广播接收器,电视接收器等等.都是适配器. 适配器主要作用是让本来不兼容的两个事物兼容和谐的一起工作.比如 ...

  7. 设计模式 - 适配器模式(adapter pattern) 枚举器和迭代器 具体解释

    适配器模式(adapter pattern) 枚举器和迭代器 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考适配器模式(adapter patter ...

  8. 设计模式(七): 通过转接头来观察"适配器模式"(Adapter Pattern)

    在前面一篇博客中介绍了“命令模式”(Command Pattern),今天博客的主题是“适配器模式”(Adapter Pattern).适配器模式用处还是比较多的,如果你对“适配器模式”理解呢,那么自 ...

  9. Java设计模式之适配器模式(Adapter Pattern)

    Adapter Pattern的作用是在不改变功能的前提下转换接口.Adapter分为两类,一类是Object Adapter, 还有一类是Class Adapter.因为Class Adapter的 ...

随机推荐

  1. 基于Proxy的小程序状态管理

    摘要: 小程序状态管理. 作者:wwayne 原文:基于Proxy的小程序状态管理 Fundebug经授权转载,版权归原作者所有. 微信小程序的市场在进一步的扩大,而背后的技术社区仍在摸索着最好的实践 ...

  2. 原生 JavaScript 代替 jQuery【转】

    目录 用原生JavaScript代替jQuery Query Selector CSS & Style DOM Manipulation Ajax Events Utilities Promi ...

  3. OL7.7安装Oracle 11.2.0.4

    安装环境准备工具 yum –y install oracle-rdbms-server-11gR2-preinstall 创建目录 mkdir -p /u01/app/oracle/product/1 ...

  4. iTerm2 + Oh My Zsh 打造舒适终端体验[mac os系统]

    当使用Mac OS系统登陆服务器时,发现tab键不能提示系统默认的命令,于是参照各种网络文章,网友提供一种软件oh my zsh [网址:https://ohmyz.sh/] 其实最重要一个命令足矣 ...

  5. HBuilder 云打包生成 .apk 文件所需的安卓证书如何获取以及文件打包前必须的设置

    在 HBuilder 云打包功能中,生在 .apk 文件虽然平台提供了免费的 安卓证书,但如果有其它需求,比如想发布,那么就需要自己去申请各种类型的证书了,这里介绍几个工具,方便在线生成证书并配置到打 ...

  6. 5. [mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)

    零.说明(重要,需要先搞清楚概念有助于后面的理解) 1.mmc core card相关模块为对应card实现相应的操作,包括初始化操作.以及对应的总线操作集合.负责和对应card协议层相关的东西. 这 ...

  7. [PHP] 项目实践中的自动加载实现

    1.使用spl_autoload_register函数注册一个自己定义的自动加载函数2.当在代码中new一个不存在的类的时候,上面的函数会被调用,不存在的类名会被作为参数传入该函数中3.兼容了两种方式 ...

  8. JAVAWEB复习笔记-day02

    1.CSS样式优先级 优先级:由上到下,由外到内.优先级越来越高 2.css选择器 html标签选择器 class选择器(.) id选择器(#) 3.优先级 style属性>id选择器>c ...

  9. SQLAlchemy 多对多

    创建多对多表 from sqlalchemy.ext.declarative import declarative_base Base=declarative_base() from sqlalche ...

  10. StarUML 系列,静态图与动态图,用例图,类图

    大部分:   静态图部分,即静态不动的图 1.用例图, Use case diagram 1.展示系统核心功能及与其交互的用户ACTOR 表示:椭圆