您现在的位置是:首页 > 正文

设计模式之适配器模式(C++实现)

2024-01-30 22:48:47阅读 0

更多设计模式参看: 设计模式之模式概述(模式汇总)(C++实现)

介绍

意图:

适配器模式(Adapter Pattern): 将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

解决问题:

主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

实现概述:

适配器模式有两种实现方式:类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现

两种实现如何选择。以下可做参考:

判断的标准主要有两个,一个是 Adaptee 接口的个数,另一个是 Adaptee 和 ITarget 的契合程度。

如果 Adaptee 接口并不多,那两种实现方式都可以。

如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都相同,那推荐使用类适配器,因为 Adaptor 复用父类 Adaptee 的接口,比起对象适配器的实现方式,Adaptor 的代码量要少一些。

如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都不相同,那推荐使用对象适配器,因为组合结构相对于继承更加灵活。

要点:

适配器继承或依赖已有的对象,实现想要的目标接口。

应用场景:

  • 封装有缺陷的接口设计

    外部系统在接口设计方面有缺陷,引入之后会影响到自身代码,为了隔离设计上的缺陷可以使用适配器模式抽象出更好的接口设计

  • 统一多个类的接口设计

    某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。比如统一多款第三方敏感词过滤系统的接口。

  • 替换依赖的外部系统

    当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。

  • 兼容老版本接口

    在做版本升级的时候,对于一些要废弃的接口,不直接将其删除,而是暂时保留,并且标注为 deprecated,并在内部委托为新的接口实现。

  • 适配不同格式的数据

    还可以用在不同格式的数据之间的适配。比如,把收集的不同存储格式的数据,统一为相同的格式,以方便存储和使用。

生活中场景

读卡器、电源适配器等各种转接头

软件中场景

JAVA 中的 jdbc

优点:

(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
(2) 增加了类的透明性复用性,对于客户端类而言是透明的,同一个适配者类可以在多个不同的系统中复用。
(3) 灵活性扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

缺点:

  • 对编程语言的限制,由于某些语言不支持多继承,一次最多只能适配一个适配者类,比如Java不支持多重继承,不能同时适配多个适配者类;
  • 过多地使用适配器,会让系统非常零乱,可读性变差不易整体进行把握,比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现

模式结构

角色

  • Target(目标抽象类): 目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
  • Adapter(适配器类): 适配器类是适配器模式的核心,可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。
  • Adaptee(适配者类): 适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码只有接口。

类图

类适配器:
在这里插入图片描述

对象适配器:
在这里插入图片描述

代码示例

敏感词过滤

GitHub

类适配器:

ClassAdapter

对象适配器:

Objectadapter

Target(目标抽象类)

/// Target(目标抽象类)
class FilterTarget {
public:
    virtual ~FilterTarget() = default;
    virtual void filter(std::string text) = 0;

protected:
    FilterTarget() = default;
};

Adaptee(适配者类)

/// Adaptee(适配者类)
class FilterAdaptee_A {
public:
    FilterAdaptee_A() {
        std::cout << "FilterAdaptee_A Hello" << std::endl;
        type = "FilterAdaptee_A";
    }
    ~FilterAdaptee_A() {
        std::cout << "FilterAdaptee_A Bye" << std::endl;
    }
    void adapteeFilter(const char *text) {
        std::cout <<"过滤器为:" << type << std::endl;
        std::cout <<"过滤内容为:" << text << std::endl;
    }

private:
    std::string type;
};

class FilterAdaptee_B {
public:
    FilterAdaptee_B() {
        std::cout << "FilterAdaptee_B Hello" << std::endl;
        type = "FilterAdaptee_B";
    }
    ~FilterAdaptee_B() {
        std::cout << "FilterAdaptee_B Bye" << std::endl;
    }
    void adapteeFilter(const std::string& text, const std::string& mask) {
        std::cout <<"过滤器为:" << type << std::endl;
        std::cout <<"过滤内容为:" << text << std::endl;
        std::cout <<"过滤mask为:" << mask << std::endl;

    }

private:
    std::string type;
};

Adapter(适配器类)

类适配器:


/// Adapter(适配器类)
class FilterAdapter_A: public FilterTarget, private FilterAdaptee_A {
public:
    FilterAdapter_A() {
        std::cout << "Class FilterAdapter_A Hello" << std::endl;
    }
    ~FilterAdapter_A() override {
        std::cout << "Class FilterAdapter_A Bye" << std::endl;
    }

    void filter(const std::string text) override {
        const char *temp = text.c_str();
        adapteeFilter(temp);
    }
};

class FilterAdapter_B: public FilterTarget, private FilterAdaptee_B {
public:
    FilterAdapter_B() {
        std::cout << "Class FilterAdapter_B Hello" << std::endl;
        mask = "MASK_B";
    }
    ~FilterAdapter_B() override {
        std::cout << "Class FilterAdapter_B Bye" << std::endl;
    }

    void filter(const std::string text) override {
        adapteeFilter(text,mask);
    }

private:
    std::string mask;
};

对象适配器:

/// Adapter(适配器类)
class FilterAdapter_A: public FilterTarget {
public:
    FilterAdapter_A() {
        std::cout << "Object FilterAdapter_A Hello" << std::endl;
    }
    ~FilterAdapter_A() override {
        std::cout << "Object FilterAdapter_A Bye" << std::endl;
    }

    void filter(const std::string text) override {
        const char *temp = text.c_str();
        filterAdapteeA.adapteeFilter(temp); // 委托
    }

private:
    FilterAdaptee_A filterAdapteeA;
};

class FilterAdapter_B: public FilterTarget {
public:
    FilterAdapter_B() {
        std::cout << "Object FilterAdapter_B Hello" << std::endl;
        mask = "MASK_B";
    }
    ~FilterAdapter_B() override {
        std::cout << "Object FilterAdapter_B Bye" << std::endl;
    }

    void filter(const std::string text) override {
        filterAdapteeB.adapteeFilter(text,mask);
    }

private:
    FilterAdaptee_B filterAdapteeB;
    std::string mask;
};

测试

int main() {
    std::list<FilterTarget *> filterList;
    filterList.push_back(new FilterAdapter_A());
    filterList.push_back(new FilterAdapter_B());

    std::list<FilterTarget *>::iterator iter; //声明一个迭代器
    iter = filterList.begin();
    while (iter!=filterList.end()) {
        FilterTarget * filter = *iter++;
        filter->filter("this is a Adapter Design Pattern Demo");
    }

    while(!filterList.empty()) {
        FilterTarget * filter = filterList.front();
        filterList.pop_front();
        delete filter;
    }

    return 0;
}

输出

类适配器:
在这里插入图片描述

对象适配器:
在这里插入图片描述

个人能力有限,如有错误之处或者其他建议,敬请告知欢迎探讨,谢谢!

网站文章

  • 安装kafka视图工具

    执行命令java -cp KafkaOffsetMonitor-assembly-0.2.0.jar com.quantifind.kafka.offsetapp.OffsetGetterWeb --zk 192.168.49.204:2181 --port 8888 --refresh 10.seconds --retain 2.days即可...

    2024-01-30 22:48:19
  • 【Spire.PDF】Spire.PDF破解 热门推荐

    【Spire.PDF】Spire.PDF破解 热门推荐

    用Spire.PDF生成报告时出现警告语 破解方法:删除第一个页 因为警告语只会在文档的第一页生成,所有删除文档第一页即可 PdfDocument pdfDocument = new PdfDocument(); pdfDocument.Pages.Add(); pdfDocument.Pages.RemoveAt(0); ...

    2024-01-30 22:48:12
  • 介绍下前端自动化工具grunt

    答:Grunt是一款基于Node.js的前端自动化构建工具,可以帮助开发者更轻松高效地完成日常的前端开发工作。它可以自动编译、合并、压缩和校验CSS和JavaScript,也可以在文件发生变化时自动重新加载页面,从而节省开发时间。...

    2024-01-30 22:48:06
  • 处理vue与后端的跨域问题

    处理vue与后端的跨域问题

    post和get请求该发送什么请求头

    2024-01-30 22:47:57
  • WSL安装Oracle Database 21c (RPM)

    wsl 安装 oracle database

    2024-01-30 22:47:28
  • 7-14 解一元一次方程 (17 分)

    请编写程序,解一元一次方程,ax+b=0。 一元一次方程求解公式为:x=−ab求解要求: ⋄a=0, 方程有唯一解,输出解; ⋄a=0,b=0, 方程无解,输出no solution ⋄a=0,b...

    2024-01-30 22:47:20
  • vue返回上一页

    累一天了 原本想写点深的东西,实在干不动了,水篇文章方法一this.$router.go(-1) //返回上一层方法二在@click直接写 click="$router.back(-1)" 返回上一页方法三history.go(-1) //返回上一层

    2024-01-30 22:47:14
  • 2019年最佳云桌面服务商

    2019年最佳云桌面服务商

    云计算已经彻底改变了IT和软件系统交付,许多应用程序现在都在云中运行。这使得用户在云中注册和设置解决方案非常容易,通常只需要几分钟。更好的是,云桌面解决方案是可扩展的,并且许多提供商提供的定价层仅针对...

    2024-01-30 22:46:42
  • 【Doxygen】Doxygen使用教程(个人总结)

    【Doxygen】Doxygen使用教程(个人总结)

    【Doxygen】Doxygen使用教程(个人总结) 简介Doxygen 引言.什么是Doxygen? Doxygen 是一个程序的文件产生工具,可将程序中的特定批注转换成为说明文件。通常我们在写程序...

    2024-01-30 22:46:35
  • Qt - Clion使用cmake运行QtCreator创建的qmake项目,无改动切换自如

    Qt - Clion使用cmake运行QtCreator创建的qmake项目,无改动切换自如

    Clion使用cmake运行QtCreator创建的qmake项目,这也正是我希望的。既可用qt的qmake在QtCreator运行、debug、发布等,同时也可以享受clion编码的便捷(对于jetbrains idea用户来说)。

    2024-01-30 22:46:28