Шаблон проектирования адаптера в 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.py и motorcycle.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()

Этот скрипт создает наш клиент и сервисные объекты. Сначала мы импортируем Car и Motorcycle классы и создавать объекты с ними. Затем мы вызываем методы из bike объект (Motorcycle учебный класс). После этого мы вызываем методы car объект (Car учебный класс). При выполнении весь код, упомянутый до сих пор, будет работать.

Однако возникает исключение, когда мы пытаемся вызвать методы класса Car класс с bike объект. Когда мы запускаем этот скрипт:

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 script для использования правильных методов. Однако во многих случаях у нас может не быть доступа к исходному коду клиента или класса обслуживания. Кроме того, это простой пример. С более крупными клиентами и сервисами рефакторинг любого из них может оказаться нецелесообразным, если мы нарушим совместимость с другими системами.

Вместо этого мы можем использовать адаптер для преодоления разрыва совместимости между нашим клиентским кодом и нашим сервисным объектом.

Использование адаптеров для решения проблемы несовместимости

В новом файле 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 объект с помощью адаптера, чтобы интерфейс каждого класса не менялся.

Отметка времени:

Больше от Стекабьюс