设计模式 (10)——适配器模式(Adapter Pattern,结构型)

1.概述

使用设计模式可以提高代码的可复用性、可扩充性和可维护性。适配器模式(Adapter Pattern)属结构性模式,将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

何谓适配器模式?适配器模式就是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

在适配器模式中,我们可以定义一个包装类,包装不兼容的函数接口和提供该函数接口的对象,这个包装类就是适配器,它所包装的对象就是适配者。

适配器提供给客户需要的函数接口,适配器的实现就是将客户的请求转换成对适配者相应函数接口的引用。也就是说,当客户调用适配器的方法时,适配器方法内部将调用适配者的方法,客户并不是直接访问适配者,而是通过调用适配器方法访问适配者。因为适配器可以使互不兼容的类能够“合作愉快”。

适配器模式的类图:

Client:客户类。 Target:目标抽象类; Adapter:适配器类; Adaptee:适配者类;

2.适配器模式的简单应用

下面使用《Head First 设计模式》中用火鸡冒充鸭子的例子来讲解。

假如,我们拥有一个抽象的鸭子类,鸭子会飞,也会叫。绿头鸭实现鸭子的抽象类。

//抽象鸭类
class Duck {
public:
    virtual void quack() = 0;
    virtual void fly() = 0;
};

//绿头鸭
class MallardDuck {
public:
    virtual void quack() {
        cout << "绿头鸭呱呱叫"<< endl;
    }
    virtual void fly() {
        cout << "绿头鸭开始飞"<< endl;
    }
};

同时,我们拥有火鸡类,火鸡同样可以叫和飞,但是叫声是咯咯叫,飞行也没有鸭子飞的远。

class Turkey {
public:
    void gobble() {
        cout << "火鸡咯咯叫" << endl;
    }
    void fly() {
        cout << "火鸡短距离飞" << endl;
    }
};

现在,假设缺少鸭子对象,使用一些火鸡来冒充。因为火鸡的函数接口不同,所以我们不能直接拿来用。那么,可以使用适配器可以来包装火鸡对象和函数接口。

class TurkeyAdapter:public Duck {
    Turkey turkey;
public:
    TurkeyAdapter(const Turkey& turkey):turkey(turkey){}

    virtual void quack() {
        turkey.gobble();
    }

    //火鸡飞行5次才能达到鸭子的飞行距离
    virtual void fly() {
        for(int i=0;i<5;++i)
            turkey.fly();
    }
};

下面是客户端代码使用火鸡适配器。

#include <iostream>
using namespace std;

int main() {
    MallardDuck mallardDuck;
    //绿头鸭开始叫和飞
    mallardDuck.quack();
    mallardDuck.fly();

    //火鸡充当鸭子开始叫和飞
    TurkeyAdapter* fakeDuck=new TurkeyAdapter( Turkey() );
    fakeDuck->quack();
    fakeDuck->fly();
}

程序输出结果:

绿头鸭呱呱叫
绿头鸭开始飞
火鸡咯咯叫
火鸡短距离飞
火鸡短距离飞
火鸡短距离飞
火鸡短距离飞
火鸡短距离飞

3.适配器模式应用场景与优缺点

应用场景: (1)系统需要使用现有的类,而这些类的接口不符合系统的需要。 (2)想要建立一个可以重复使用的类(适配器),用于与一些彼此之间没有太大关联的类建立联系。

优点: (1)松散耦合。将客户端代码和适配者类解耦,通过使用适配器让不兼容的接口变得兼容。 (2)隐藏和复用。增加了适配者类(如火鸡类)的复用性,将适配者类的实现封装在适配器中,隐藏了适配者类的具体实现,而且提高了适配者的复用性。 (3)良好的灵活性和扩展性。在不修改原有代码的基础上可以增加新的适配器类,符合“开放关闭原则”。

缺点: 采用组合的方式将适配者封装在适配器类中,不能够重写(覆盖)适配者的行为。

4.类适配器

适配器模式分为对象适配器和类适配器。前面所概述的是对象适配器。类适配器使用有限,因为类适配器需要使用多重继承,这个在java、C#中没有办法实现,但在C++中可以实现。

类适配器的类图如下:

类适配器优点: 适配器类继承适配者类,在必要的时候,可以重写适配者的行为。

类适配器缺点: 对于Java、C#等不支持多重继承的语言,一次最多只能适配一个目标类。其使用存在一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。

5.小结

(1)当我们需要使用一个现有的类,但是它的函数接口并不符合我们的需求时,我们可以使用适配器模式。 (2)适配器模式分为类适配器和对象适配器,其中类适配器需要用到多重继承。 (3)适配器模式(对象适配器)将一个对象包装起来改变其函数接口,来适应现有系统的需要;装饰者模式讲一个对象包装起来以增加新的行为和责任;而外观模式将一群对象包装起来,以简化其函数接口。


参考文献

[1]设计模式读书笔记—–适配器模式 [2]Head First 设计模式(中文版):235-270

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏刘君君

Builder模式

22850
来自专栏JAVA高级架构

适配器模式(Adapter)

10430
来自专栏Android开发指南

13.缓存、三级缓存、内存溢出、AsyncTask

449120
来自专栏向治洪

picasso图片缓存框架

picasso是Square公司开源的一个Android图形缓存库,地址http://square.github.io/picasso/,可以实现图片下载和缓...

32480
来自专栏QQ空间开发团队的专栏

Android 内存泄漏分析心得

本文通过 QQ 和 Qzone 中内存泄漏实例来讲 android 中内存泄漏分析解法和编写代码应注意的事项。

66810
来自专栏何俊林

Android Multimedia框架总结(二十一)MediaCodec中创建到start过程(到jni部分)

最近我正在参加CSDN 2016年度博客之星评选,如果我的文章对你曾有过帮助,不妨点击文末【阅读原文】给我投上一票, 时间:11月28日到12月18日每天都可以...

27160
来自专栏向治洪

Android数据库Realm实践

Android开发中常用的数据库有5个: 1. OrmLite OrmLite 不是 Android 平台专用的ORM框架,它是Java ORM。支持JDBC连...

26090
来自专栏码匠的流水账

聊聊spring cloud gateway的ForwardedHeadersFilter

本文主要研究一下spring cloud gateway的ForwardedHeadersFilter

10320
来自专栏developerHaoz 的安卓之旅

Android 关于内存泄露,你必须了解的东西

内存管理的目的就是让我们在开发过程中有效避免我们的应用程序出现内存泄露的问题。内存泄露相信大家都不陌生,我们可以这样理解:「没有用的对象无法回收的现象就是内存泄...

7410
来自专栏一“技”之长

Android中SharedPreferences使用小记

        Android中使用SharedPreferences来进行简单数据的持久化处理,从名字就可以了解,其设计目的是为了保存应用程序的一些偏好设置,...

9240

扫码关注云+社区

领取腾讯云代金券