Python 中的适配器设计模式

介绍

适配器设计模式 是受欢迎的 结构设计模式 用于软件工程。 本指南着眼于我们如何在 Python 中实现适配器设计模式。

设计模式 是类似模板的解决方案——实际上是解决软件开发中反复出现的常见问题的秘诀。 适配器模式基于现实世界适配器的概念! 例如,笔记本电脑的充电器末端可能有一个 3 针插头,但墙壁插座可能只有一个 2 针插座。 要将 3 针充电器插入此插座,我们需要一个可以接受 3 针插头的适配器,并且 适应 接口进入 2针 插座。

一个 2 针充电器和一个 3 针充电器有 相同的基本功能 (从插座向笔记本电脑导电),但有 不同的形式, 并且可以很容易地 适应 进入另一个。 每当您拥有基本功能相同但形式不同的软件组件时,您都可以应用适配器设计模式。

适配器模式遵循这个确切的原则。 它允许两个不兼容的接口一起工作,而无需修改每个组件的内部结构。 这是通过在外部使一个接口适应另一个接口来实现的。

在深入研究适配器模式之前,让我们先看一些基本术语:

  • 客户端接口:指定客户端应实现的功能的接口。
  • 客户:实现客户端接口的类。
  • 适配者/服务:需要与客户端接口协作的不兼容类。
  • 适配器:使服务和客户端之间的协作成为可能的类。

不同类型的适配器模式

适配器设计模式可以通过两种不同的方式实现:

对象适配器

使用此方法,适配器类实现来自客户端接口的方法。 因此,客户端对象和适配器对象相互兼容。 服务对象形成一个 has-a 与适配器对象的关系,即服务对象属于适配器对象。

我们知道服务类与客户端不兼容。 适配器类通过用该对象实例化自身来包装服务对象。 现在,可以通过适配器对象访问服务对象,允许客户端与之交互。

我们可以在所有现代编程语言中实现对象适配器。

类适配器

使用这种方法,适配器有一个 is-a 与服务类的关系。 在这种情况下,适配器实现了客户端所需的方法,但它继承自多个适配器,使其能够直接调用它们不兼容的函数。 这种变体的最大缺点是我们只能在支持类的多重继承的编程语言中使用它。

适配器设计模式在 Python 中的实现

在下面的部分中,我们将在 Python 中实现适配器设计模式,具体使用 对象适配器变体. 本节分为两部分。 首先,我们将创建应该使用适配器模式的环境。 清楚地了解这种模式如何解决一些软件问题很重要。 第二部分将使用适配器来解决问题。

类之间的不兼容问题

让我们看看客户端和服务类实现不同功能时的兼容性问题。 使用以下方法创建一个客户端类并将其保存在文件夹中 car.py:

import random

class Car:
    def __init__(self):
        self.generator = random.Random()

    def accelerate(self):
        random_num = self.generator.randint(50, 100)
        speed = random_num
        print(f"The speed of the car is {speed} mph")

    def apply_brakes(self):
        random_num = self.generator.randint(20, 40)
        speed = random_num
        print(f"The speed of the car is {speed} mph after applying the brakes")

    def assign_driver(self, driver_name):
        print(f"{driver_name} is driving the car")

在这里,我们创建了一个 Car 具有三种方法的类 accelerate(), apply_brakes()assign_driver(). 我们导入了 random 模块并使用它来生成数字,这些数字在加速和刹车后设定汽车的速度。 这 assign_driver() 方法显示汽车司机的姓名。

接下来,我们必须创建一个希望与客户端类协作的服务或适配者类 Car. 像这样创建一个摩托车类并将其保存在您的文件夹中 motorcycle.py:

import random

class Motorcycle:
    def __init__(self):
        self.generator = random.Random()

    def rev_throttle(self):
        random_num = self.generator.randint(50, 100)
        speed = random_num
        print(f"The speed of the motorcycle is {speed} mph")

    def pull_brake_lever(self):
        random_num = self.generator.randint(20, 40)
        speed = random_num
        print(
            f"The speed of the motorcycle is {speed} mph after applying the brakes")

    def assign_rider(self, rider_name):
        print(f"{rider_name} is riding the motorcycle")  

一个服务等级, Motorcycle 上面用三种方法创建 rev_throttle(), pull_brake_lever()assign_rider(). 请注意服务和客户端类的方法之间的区别,尽管它们的功能相似。 这 accelerator() 方法增加了汽车的速度,而 rev_throttle() 方法增加摩托车的速度。 同样地, apply_brakes()pull_brake_lever() 在相应的车辆中应用刹车。 最后, assign_driver()assign_rider() 方法分配车辆操作员。

接下来,让我们创建一个类来访问这些不同的方法。 首先,添加一个 __init.py__ 在您创建的同一文件夹中 car.pymotorcycle.py:

touch __init__.py

现在在新文件中添加以下代码 drive.py:

from car import Car
from motorcycle import Motorcycle
import traceback

if __name__ == '__main__':
    car = Car()
    bike = Motorcycle()

    print("The Motorcyclen")
    bike.assign_rider("Subodh")
    bike.rev_throttle()
    bike.pull_brake_lever()
    print("n")

    print("The Carn")
    car.assign_driver("Sushant")
    car.accelerate()
    car.apply_brakes()
    print("n")

    print("Attempting to call client methods with the service objectn")

    try:
        bike.assign_driver("Robert")
        bike.accelerate()
        bike.apply_brakes()
    except AttributeError:
        print("Oops! bike object cannot access car methods")
        traceback.print_exc()

这个脚本创建我们的客户端和服务对象。 我们首先导入 CarMotorcycle 类并用它们创建对象。 然后我们从 bike 对象(Motorcycle 班级)。 之后,我们调用 car 对象(Car 班级)。 执行时,到目前为止提到的所有代码都将起作用。

但是,当我们尝试调用 Carbike 目的。 当我们运行这个脚本时:

The Motorcycle

Subodh is riding the motorcycle
The speed of the motorcycle is 91 mph
The speed of the motorcycle is 37 mph after applying the brakes


The Car

Sushant is driving the car
The speed of the car is 59 mph
The speed of the car is 33 mph after applying the brakes


Attempting to call client methods with the service object

Oops! bike object cannot access car methods
Traceback (most recent call last):
  File "drive.py", line 24, in 
    bike.assign_driver("Robert")
AttributeError: 'Motorcycle' object has no attribute 'assign_driver'

在这种情况下,我们可以修改 Motorcycle 班级或 drive.py 脚本使用正确的方法。 但是,在很多情况下,我们可能无法访问客户端或服务类的源代码。 另外,这是一个简单的例子。 对于较大的客户端和服务,如果我们破坏与其他系统的兼容性,重构它们中的任何一个可能都不可行。

相反,我们可以使用适配器来弥合客户端代码和服务对象之间的兼容性差距。

使用适配器解决不兼容问题

在一个新文件中, motorcycle_adapter.py,添加以下类:

class MotorcycleAdapter:

    def __init__(self, motorcycle):
        self.motorcycle = motorcycle

    def accelerate(self):
        self.motorcycle.rev_throttle()

    def apply_brakes(self):
        self.motorcycle.pull_brake_lever()

    def assign_driver(self, name):
        self.motorcycle.assign_rider(name)

我们创建了一个 MotorcycleAdapter 类,它用服务对象实例化自己(motorcycle)。 适配器实现了客户端方法,这些方法是 accelerate(), apply_brakes()assign_driver(). 体内的 accelerate() 方法,我们使用了 motorcycle 要调用的服务对象的实例 rev_throttle() 服务方式。 同样,其他方法使用相应的方法 Motorcycle 类。

现在,让我们更新 drive.py 所以我们可以在 try/except 块:

from car import Car
from motorcycle import Motorcycle
from motorcycle_adapter import MotorcycleAdapter 
import traceback

if __name__ == '__main__':
    car = Car()
    bike = Motorcycle()
    bike_adapter = MotorcycleAdapter(bike) 

    ...

    try:
        print("Attempting to call client methods with the service object using an adaptern")
        bike_adapter.assign_driver("Robert")
        bike_adapter.accelerate()
        bike_adapter.apply_brakes()
    except AttributeError:
        print("Oops! bike object cannot access car methods")
        traceback.print_exc()

在这里,bike_adapter 是一个对象 MotorcycleAdapter 班级。 我们提供了 bike 反对 MotorcycleAdapter 类的构造函数。 执行这个脚本会给我们以下输出:

查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!

The Motorcycle

Subodh is riding the motorcycle
The speed of the motorcycle is 88 mph
The speed of the motorcycle is 35 mph after applying the brakes


The Car

Sushant is driving the car
The speed of the car is 91 mph
The speed of the car is 24 mph after applying the brakes


Attempting to call client methods with the service object

Attempting to call client methods with the service object using an adapter

Robert is riding the motorcyle
The speed of the motorcycle is 67 mph
The speed of the motorcycle is 25 mph after applying the brakes

无需调整底层 Motorcycle 类,我们可以让它像一个 Car 使用适配器!

适配器设计模式的优缺点

适配器模式的优点是:

  • 我们可以实现适配器类和客户端类之间的低耦合。
  • 我们可以重用适配器类以在应用程序中合并大量服务类。
  • 我们可以通过引入多个适配器来增加程序的灵活性而不干扰客户端代码

适配器模式的缺点是:

  • 程序的复杂性随着适配器类和服务类的增加而增加。
  • 当请求从一个类转发到另一个类时,程序的开销会增加。
  • 适配器模式(类适配器)使用多重继承,所有编程语言可能都不支持。

结论

在本文中,我们了解了适配器设计模式、它的类型以及它们解决的问题。 我们在 Python 中实现了适配器模式,以便我们可以与 Motorcycle 对象,像一个 Car 对象通过使用适配器,因此每个类的接口不会改变。

时间戳记:

更多来自 堆栈滥用