设计模式之适配器模式(Adapter模式)

前言

日常生活中,经常会遇到这样的情况:我们碰到了一个问题需要解决,手里现有的工具呢不能够直接解决问题,需要通过变通,比如说,水管需要延长才能达到我们的距离需求,但是呢现有的水管直径不同,这个时候就需要一个转接头能够将两个不同直径的水管拼接起来。诸如此类的问题,比比皆是,如电脑屏幕连接的数据线又各种格式,DVI、VGA、HDMI等,也经常遇到要用转接头连接一端是VGA一端是HDMI接口情况,再比如家庭用电电压是电器所需电压,并不一致,这个时候也需要转换。

那么在程序世界里,自然也会碰到类似的问题。现有的程序可以解决大部分需求问题,但是又不能直接使用!所以就需要一个转接头能够将现有的程序所需的程序连接起来。我们将这种模式成为适配器模式,也叫Adapter模式。

模式介绍

类设计

类比现实
实际情况 大直径水管 Banner类
适配器 转接头 PrintBanner类
需求情况 小直径水管 Print接口

类UML关系图

UML类图

代码实现

本次示例中,使用一个简单的打印字符串的类,我们目前有一个现有类Banner,功能是将所给的字符串加上括号或者*号,打印到控制台。假设我们现在有一个新需求,需要对所给字符串进行弱显示和强显示,分别对应加括号和加*。而且我们不能直接使用Banner类方法,因为这两个方法属于protect级别,不对外开放。所以我们需要写一个类,继承自Banner,并将该方法开放出去。

Banner类

"""
@Time: 2023/3/18 22:54
@Auth: CivilDog
@File: Banner.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""


class Banner:
    """
    打印字符串的类,类比现有程序
    """
    def __init__(self, str_to_print: str):
        self.__str_to_print = str_to_print

    def _show_with_paren(self):
        print("("+self.__str_to_print+")")

    def _show_with_aster(self):
        print("*"+self.__str_to_print+"*")

PrintInterface类

"""
@Time: 2023/3/18 22:57
@Auth: CivilDog
@File: PrintInterface.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
import abc
from abc import ABCMeta


class PrintInterface(metaclass=ABCMeta):
	"""
	打印类的接口类,抽象出需求所需要的方法,并由实际使用类所继承和实现
	"""
    @abc.abstractmethod
    def print_weak(self):
        pass

    @abc.abstractmethod
    def print_strong(self):
        pass

PrintBanner类

"""
@Time: 2023/3/18 22:58
@Auth: CivilDog
@File: PrintBanner.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
from DesignMode.AdapterMode.PrintInterface import PrintInterface
from DesignMode.AdapterMode.Banner import Banner


class PrintBanner(PrintInterface, Banner):
	"""
	多重继承PrintInterface和Banner,实现父类定义的抽象方法,和开放protected级别的方法
	"""
    def __init__(self, str_to_print: str):
        super(PrintBanner, self).__init__(str_to_print)

    def print_weak(self):
        self._show_with_paren()

    def print_strong(self):
        self._show_with_aster()

AdapterClient类

"""
@Auth: CivilDog
@File: AdapterClient.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
from DesignMode.AdapterMode.PrintBanner import PrintBanner


class AdapterClient:
    """
    Adapter模式的测试入口
    """

    @staticmethod
    def run(*args, **kwargs):
        str_to_print = "Hello World!"
        p_b = PrintBanner(str_to_print)
        p_b.print_weak()
        p_b.print_strong()


if __name__ == '__main__':
    AdapterClient.run()

输出结果

(Hello World!)
*Hello World!*

Process finished with exit code 0

利用委托,避免多继承

因为多继承可能引入新的问题,而且往往难以排查。所以有时候项目里会要求尽量避免多重继承!
这个时候可以换用委托的方式进行,也就是将PrintBanner类的工作委托给其他类,也就是Banner类。这种方式也可以在不暴露Banner类本身的情况下,开放我们所需要的接口。

委托方式也是程序开发中常见的一种编程方法。

Banner类

"""
@Time: 2023/3/18 22:54
@Auth: CivilDog
@File: Banner.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""


class Banner:
    """
    打印字符串的类,类比现有程序
    """
    def __init__(self, str_to_print: str):
        self.__str_to_print = str_to_print

    def show_with_paren(self):
        print("("+self.__str_to_print+")")

    def show_with_aster(self):
        print("*"+self.__str_to_print+"*")

PrintBanner类

class PrintBanner(PrintInterface):

    def __init__(self, str_to_print: str):
        super(PrintBanner, self).__init__()
        self.__banner = Banner(str_to_print)

    def print_weak(self):
        self.__banner.show_with_paren()

    def print_strong(self):
        self.__banner.show_with_aster()

总结

为什么要用适配器模式

以上举了一个简单的小例子,其实这里如果实现这种需求,可以直接修改Banner类,将方法暴露出来不就行了。为什么还要这么麻烦折腾出多余的两个类。

避免影响原来代码,降低成本

举例是为了让大家理解适配器模式的使用方法。实际开发情况和开发需求比这个要复杂的多。我们现有的程序一般都是经过严格测试才上线的。如果因为来了新需求,而直接在源代码基础上进行改动,那就需要重新进行测试,成本比较高,而且很容易出现问题,影响范围也比较大。

采用适配器模式,可以避免这种情况,原来的代码经过测试已经比较稳定,而且都是在线上运行的。通过适配,将现有代码使用到新项目,新需求中,即便是出现问题,也很容易判断是适配代码的问题。

只需要接口,即可实现新的类

python语言都是源码开放的,但是很多编译型语言往往都不会开源,而是提供一些API供外部调用。这个时候,在项目中,我们为了避免暴露第三方的东西,一般都会自行封装一个类。就是适配器模式。

版本升级中往往出现它的身影

软件生命周期总是伴随升级。极少情况下能够做到完全抛弃旧版本,所以新旧版本的兼容问题,往往令人头疼。适配器模式,可以帮助我们轻松的应对这种情况!

什么情况下不能使用适配器模式

现有程序和需求之间完全不匹配的时候,自然无法使用,要自行实现新的类。

《图解设计模式》结城浩

猜你喜欢

转载自blog.csdn.net/weixin_43500200/article/details/129644393