• 【设计模式】适配器模式


    适配器模式

    简介

    logo

    在系统设计中,当需要组合使用的类不兼容时,也需要类似于变压器一样的适配器来协调这些不兼容者,这就是适配器模式。

    适配器模式:将一个类的接口转换成客户希望的另一个接口,适配器模式可以让那些接口不兼容的类一起工作。

    结构

    对象适配器:它使用了构成(关联)原则,适配器实现了其中一个对象的接口,并对另一个对象进行封装。所有流行的编程语言都可以实现适配器。

    uml1

    类适配器:它使用了继承机制,适配器同时继承两个对象的接口。值得注意的是,这种方式仅能在支持多重继承的编程语言中实现,例如C++

    uml2

    实现

    实现方式:

    • 确保至少有两个类的接口不兼容:
      • 一个无法修改(通常是第三方、遗留系统或者存在众多已有依赖的类)的功能性服务类。
      • 一个或多个将受益于使用服务类的客户端类。
    • 声明客户端接口,描述客户端如何与服务交互。
    • 创建遵循客户端接口的适配器类。所有方法暂时都为空。
    • 在适配器类中添加一个成员变量用于保存对于服务对象的引用。通常情况下会通过构造函数对该成员变量进行初始化,但有时在调用其方法时将该变量传递给适配器会更方便。
    • 依次实现适配器类客户端接口的所有方法。适配器会将实际工作委派给服务对象,自身只负责接口或数据格式的转换。
    • 客户端必须通过客户端接口使用适配器。这样以来,你就可以在不影响客户端代码的情况下修改或扩展适配器。
    #include <iostream>
    #include <string>
    #include <algorithm>
    
    // 目标抽象类:定义客户所需要的接口
    class Target {
    public:
        virtual ~Target() = default;
        virtual std::string Request() const {
            return "Target: The default target`s behavior.";
        }
    };
    
    // 适配者类
    class Adaptee {
    public:
        std::string SpecificRequest() const {
            return ".eetpadA eht fo roivaheb laicepS";
        }
    };
    
    // 适配器类
    class Adapter: public Target {
    private:
        Adaptee *adaptee_;
    
    public:
        Adapter(Adaptee *adaptee) : adaptee_(adaptee) {}
        std::string Request() const override {
            std::string to_reverse = this->adaptee_->SpecificRequest();
            std::reverse(to_reverse.begin(), to_reverse.end());
            return "Adapter: (TRANSLATED) " + to_reverse;
        }
    };
    
    // 客户端
    void ClientCode(const Target* target) {
        std::cout << target->Request();
    }
    
    int main(int argc, char *argv[]) {
        std::cout << "Client: 处理任何数据" << std::endl;
        Target* target = new Target;
        ClientCode(target);
        std::cout << std::endl << std::endl;
    
        std::cout << "Client: 我不能理解这个数据" << std::endl;
        Adaptee* adaptee = new Adaptee;
        std::cout << "Adaptee: " << adaptee->SpecificRequest();
        std::cout << std::endl << std::endl;
    
        std::cout << "Client: 结合适配器可以理解这个数据" << std::endl;
        Adapter *adapter = new Adapter(adaptee);
        ClientCode(adapter);
        std::cout << std::endl << std::endl;
    
        delete target;
        delete adaptee;
        delete adapter;
    
        return 0;
    }
    
    # -*- coding: utf-8 -*-
    
    
    class Target:
    
        def request(self):
            return "Target: The default target's behavior."
    
    
    class Adaptee:
    
        def specific_request(self):
            return ".eetpadA eht fo roivaheb laicepS"
    
    
    class Adapter(Target, Adaptee):
    
        def request(self):
            return "Adapter: (TRANSLATED) {0}".format(self.specific_request()[::-1])
    
    
    def client_code(target):
    
        print(target.request())
    
    
    if __name__ == "__main__":
        print("Client: 处理任何数据")
        target = Target()
        client_code(target)
        print("
    ")
    
        print("Client: 我不能理解这个数据")
        adaptee =  Adaptee()
        print("Adaptee: {0}".format(adaptee.specific_request()))
        print("
    ")
    
        print("Client: 结合适配器可以理解这个数据")
        adapter = Adapter()
        client_code(adapter)
    

    实例

    问题描述

    假设某公司A与某公司B需要合作,公司A需要访问公司B的人员信息,但公司A与公司B协议接口不同,该如何处理?

    问题解答

    # Example.py
    
    # -*- coding: utf-8 -*-
    
    
    class AStaff:
        name = ""
        id = ""
        phone = ""
    
        def __init__(self, id):
            self.id = id
    
        def getName(self):
            print "A protocol getName method...id:%s"%self.id
            return self.name
    
        def setName(self, name):
            print "A protocol setName method...id:%s"%self.id
            self.name = name
    
        def getPhone(self):
            print "A protocol getPhone method...id:%s"%self.id
            return self.phone
    
        def setPhone(self, phone):
            print "A protocol setPhone method...id:%s"%self.id
            self.phone=phone
    
    
    class BStaff:
        name = ""
        id = ""
        telephone = ""
    
        def __init__(self, id):
            self.id=id
    
        def get_name(self):
            print "B protocol get_name method...id:%s"%self.id
    
            return self.name
        def set_name(self, name):
            print "B protocol set_name method...id:%s"%self.id
            self.name = name
    
        def get_telephone(self):
            print "B protocol get_telephone method...id:%s"%self.id
            return self.telephone
    
        def set_telephone(self, telephone):
            print "B protocol get_name method...id:%s"%self.id
            self.telephone = telephone
    
    
    # 适配器
    class StaffAdapter:
        adapter = ""
    
        def __init__(self, id):
            self.adapter = BStaff(id)
    
        def getName(self):
            return self.adapter.get_name()
    
        def getPhone(self):
            return self.adapter.get_telephone()
    
        def setName(self, name):
            self.adapter.set_name(name)
    
        def setPhone(self, phone):
            self.adapter.set_telephone(phone)
    
    
    if __name__ == "__main__":
        a_staff= AStaff("123")
        a_staff.setName("AAA")
        a_staff.setPhone("0731-36262324")
        print("A Staff Name: ", a_staff.getName())
        print("A Staff Phone: ", a_staff.getPhone())
        print("
    ")
    
        b_staff = StaffAdapter("456")
        b_staff.setName("BBB")
        b_staff.setPhone("023-54323425")
        print("B Staff Name: ", b_staff.getName())
        print("B Staff Phone: ", b_staff.getPhone())
        print("
    ")
    
    

    总结

    优点

    • 将目标类和适配者类解耦,引入一个适配器类实现代码重用,无需修改原有结构。符合单一职责原则开闭原则
    • 增加类的透明和复用,对于客户端而言,适配者类是透明的。
    • 对象适配器可以把不同适配者适配到同一个目标

    缺点

    • 对编程语言的限制:例如Java不支持多重继承,一次最多只能适配一个适配者类,不能同时适配多个适配者类。
    • 代码整体复杂度增加,因为你需要新增一系列接口和类。有时直接更改服务类使其与其他代码兼容会更简单。

    场景

    • 系统需要使用一些现有的类,但这些类的接口不符合系统需要,或者没有这些类的源代码。
    • 想创建一个重复使用的类,用于和一些彼此没有太大关联的类一起工作

    与其他模式的关系

    • 桥接模式通常会在开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。另一方面,适配器模式通常在已有程序中使用,让相互不兼容的类能很好地合作。
    • 适配器模式可以对已有对象的接口进行修改,装饰模式则在不改变对象接口的前提下强化对象功能。此外,装饰模式还支持递归组合,适配器模式则无法实现。
    • 适配器模式能为封装对象提供不同的接口,代理模式能为对象提供相同的接口,装饰模式则能为对象提供加强的接口。
    • 外观模式为现有对象定义一个新接口,适配器模式则会试图运用已有的接口。适配器模式通常只是封装一个对象,外观模式通常会作用于整个对象子系统上。
    • 桥接模式、状态模式和策略模式(某种程序上包括适配器模式)的接口都非常相似,实际上,它们都基于组合模式,即将工作委派给其他对象,不过也都各自解决了不同的问题。
  • 相关阅读:
    20155338 2016-2017-2 《Java程序设计》第4周学习总结
    20155338 2016-2017-2 《Java程序设计》第3周学习总结
    20155338 2006-2007-2 《Java程序设计》第2周学习总结
    20155316 实验三《敏捷开发与XP实践》实验报告
    20155316 2016-2017-2 《Java程序设计》第10周学习总结
    20155316 2016-2017-2 《Java程序设计》第9周学习总结
    20155316 实验二《Java面向对象程序设计》实验报告
    20155316 2016-2017-2 《Java程序设计》第8周学习总结
    20155316 实验一《Java开发环境的熟悉》实验报告
    20155316 2016-2017-2 《Java程序设计》第7周学习总结
  • 原文地址:https://www.cnblogs.com/parzulpan/p/13552515.html
Copyright © 2020-2023  润新知