Шаблон адаптера - Adapter pattern

В программная инженерия, то шаблон адаптера это шаблон разработки программного обеспечения (также известный как оболочка, альтернативное именование, общее с декоратор шаблон ), что позволяет интерфейс существующего класс для использования в качестве другого интерфейса.[1] Он часто используется для того, чтобы существующие классы работали с другими без изменения их исходный код.

Примером может служить адаптер, преобразующий интерфейс Объектная модель документа из XML документ в древовидной структуре, которая может отображаться.

Обзор

Адаптер[2] шаблон дизайна - один из двадцати трех хорошо известных Шаблоны проектирования GoF которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать.

Шаблон проектирования адаптера решает такие проблемы, как:[3]

  • Как можно повторно использовать класс, не имеющий интерфейса, который требуется клиенту?
  • Как могут работать вместе классы с несовместимыми интерфейсами?
  • Как можно предоставить альтернативный интерфейс для класса?

Часто (уже существующий) класс нельзя повторно использовать только потому, что его интерфейс не соответствует интерфейсу, который требуется клиентам.

Шаблон проектирования адаптера описывает, как решать такие проблемы:

  • Определить отдельный адаптер класс, который преобразует (несовместимый) интерфейс класса (приспособленный) в другой интерфейс (цель) клиенты требуют.
  • Работать через адаптер для работы (повторного использования) классов, не имеющих необходимого интерфейса.

Ключевой идеей этого паттерна является проработка отдельного адаптер который адаптирует интерфейс (уже существующего) класса, не изменяя его.

Клиенты не знают, работают ли они с цель класс напрямую или через адаптер с классом, не имеющим цель интерфейс.

См. Также диаграмму классов UML ниже.

Определение

Адаптер позволяет двум несовместимым интерфейсам работать вместе. Это реальное определение адаптера. Интерфейсы могут быть несовместимы, но внутренняя функциональность должна соответствовать потребностям. Шаблон проектирования адаптера позволяет несовместимым классам работать вместе, преобразовывая интерфейс одного класса в интерфейс, ожидаемый клиентами.

Применение

Адаптер можно использовать, когда оболочка должна учитывать определенный интерфейс и поддерживать полиморфный поведение. В качестве альтернативы декоратор позволяет добавлять или изменять поведение интерфейса во время выполнения, а фасад используется, когда требуется более простой или более простой интерфейс для базового объекта.[4]

ШаблонНамерение
Адаптер или оберткаПреобразует один интерфейс в другой, чтобы он соответствовал ожиданиям клиента.
ДекораторДинамически добавляет ответственности к интерфейсу, упаковывая исходный код
ДелегацияПоддержка «композиции важнее наследования»
ФасадПредоставляет упрощенный интерфейс

Структура

Диаграмма классов UML

Пример диаграммы классов UML для шаблона проектирования адаптера.[5]

В приведенном выше UML диаграмма классов, то клиент класс, который требует цель интерфейс не может повторно использовать приспособленный class напрямую, потому что его интерфейс не соответствует цель интерфейс. клиент работает через адаптер класс, реализующий цель интерфейс с точки зрения приспособленный:

  • В объектный адаптер способ реализует цель интерфейс, делегируя приспособленный объект во время выполнения (Adaptee.specificOperation ()).
  • В адаптер класса способ реализует цель интерфейс путем наследования от приспособленный класс во время компиляции (specificOperation ()).

Шаблон адаптера объекта

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

Шаблон адаптера объекта, выраженный в UML
Шаблон адаптера объекта, выраженный в LePUS3

Шаблон адаптера класса

Этот шаблон адаптера использует несколько полиморфные интерфейсы реализация или наследование как ожидаемого интерфейса, так и уже существующего интерфейса. Обычно ожидаемый интерфейс создается как чистый интерфейс класс, особенно в языки такие как Ява (до JDK 1.8), которые не поддерживают множественное наследование классов.[1]

Шаблон адаптера класса, выраженный в UML.
Шаблон адаптера класса, выраженный в LePUS3

Еще одна форма шаблона адаптера среды выполнения

Мотивация от решения во время компиляции

Это желательно для classA обеспечить classB с некоторыми данными, допустим, некоторые Строка данные. Решение для времени компиляции:

classB.setStringData(classA.getStringData());

Однако предположим, что формат строковых данных должен быть изменен. Решение во время компиляции - использовать наследование:

общественный класс Format1ClassA расширяет ClassA {    @Override    общественный Строка getStringData() {        вернуть формат(нанизывать());    }}

и, возможно, создать правильно "форматирующий" объект во время выполнения с помощью заводской образец.

Решение для адаптера времени выполнения

Решение с использованием «переходников» происходит следующим образом:

(i) Определите промежуточный интерфейс "поставщика" и напишите реализацию этого интерфейса поставщика, которая обертывает источник данных, ClassA в этом примере и выводит данные в соответствующем формате:

общественный интерфейс StringProvider {    общественный Строка getStringData();}общественный класс ClassAFormat1 орудия StringProvider {    частный ClassA classA = значение NULL;    общественный ClassAFormat1(окончательный ClassA а) {        classA = а;    }    общественный Строка getStringData() {        вернуть формат(classA.getStringData());    }    частный Строка формат(окончательный Строка sourceValue) {        // Преобразование исходной строки в требуемый формат         // объектом, которому нужны данные исходного объекта        вернуть sourceValue.отделка();    }}

(ii) Напишите класс адаптера, который возвращает конкретную реализацию поставщика:

общественный класс ClassAFormat1Adapter расширяет Адаптер {    общественный Объект адаптироваться(окончательный Объект объект) {        вернуть новый ClassAFormat1((ClassA) объект);    }}

(iii) Зарегистрировать адаптер с глобальным реестром, так что адаптер можно посмотреть во время выполнения:

АдаптерФабрика.getInstance().registerAdapter(ClassA.класс, ClassAFormat1Adapter.класс, "формат1");

(iv) В коде, когда вы хотите передать данные из ClassA к КлассB, записывать:

Адаптер адаптер =    АдаптерФабрика.getInstance()        .getAdapterFromTo(ClassA.класс, StringProvider.класс, "формат1");StringProvider провайдер = (StringProvider) адаптер.адаптироваться(classA);Строка строка = провайдер.getStringData();classB.setStringData(строка);

или более кратко:

classB.setStringData(    ((StringProvider)            АдаптерФабрика.getInstance()                .getAdapterFromTo(ClassA.класс, StringProvider.класс, "формат1")                .адаптироваться(classA))        .getStringData());

(v) Преимущество можно увидеть в том, что, если требуется передать данные во втором формате, найдите другой адаптер / поставщик:

Адаптер адаптер =    АдаптерФабрика.getInstance()        .getAdapterFromTo(ClassA.класс, StringProvider.класс, "формат2");

(vi) И если желательно вывести данные из ClassA как, скажем, данные изображения в Класс C:

Адаптер адаптер =    АдаптерФабрика.getInstance()        .getAdapterFromTo(ClassA.класс, ImageProvider.класс, "формат2");ImageProvider провайдер = (ImageProvider) адаптер.адаптироваться(classA);classC.setImage(провайдер.getImage());

(vii) Таким образом, использование адаптеров и поставщиков позволяет использовать несколько "представлений" КлассB и Класс C в ClassA без изменения иерархии классов. В общем, он позволяет использовать механизм для произвольных потоков данных между объектами, который может быть модифицирован в существующую иерархию объектов.

Реализация шаблона адаптера

При реализации паттерна адаптера для наглядности можно применить имя класса [ClassName]Чтобы[Интерфейс]Адаптер реализации провайдера; Например, DAOToProviderAdapter. Он должен иметь метод конструктора с переменной адаптируемого класса в качестве параметра. Этот параметр будет передан члену экземпляра [ClassName]Чтобы[Интерфейс]Адаптер. Когда вызывается clientMethod, он будет иметь доступ к экземпляру адаптируемого объекта, который позволяет получить доступ к требуемым данным адаптируемого и выполнять операции с этими данными, которые генерируют желаемый результат.

Ява

интерфейс LightningPhone {    пустота перезарядка();    пустота useLightning();}интерфейс MicroUsbPhone {    пустота перезарядка();    пустота useMicroUsb();}класс Iphone орудия LightningPhone {    частный логический соединитель;    @Override    общественный пустота useLightning() {        соединитель = правда;        Система.вне.println("Молния подключена");    }    @Override    общественный пустота перезарядка() {        если (соединитель) {            Система.вне.println("Пополнение началось");            Система.вне.println(«Перезарядка завершена»);        } еще {            Система.вне.println("Сначала подключите Lightning");        }    }}класс Android орудия MicroUsbPhone {    частный логический соединитель;    @Override    общественный пустота useMicroUsb() {        соединитель = правда;        Система.вне.println("MicroUsb подключен");    }    @Override    общественный пустота перезарядка() {        если (соединитель) {            Система.вне.println("Пополнение началось");            Система.вне.println(«Перезарядка завершена»);        } еще {            Система.вне.println(«Сначала подключите MicroUsb»);        }    }}/ * отображение целевого интерфейса при переносе исходного объекта * /класс LightningToMicroUsbAdapter орудия MicroUsbPhone {    частный окончательный LightningPhone молния;    общественный LightningToMicroUsbAdapter(LightningPhone молния) {        этот.молния = молния;    }    @Override    общественный пустота useMicroUsb() {        Система.вне.println("MicroUsb подключен");        молния.useLightning();    }    @Override    общественный пустота перезарядка() {        молния.перезарядка();    }}общественный класс АдаптерДемо {    статический пустота перезарядкаMicroUsbPhone(MicroUsbPhone Телефон) {        Телефон.useMicroUsb();        Телефон.перезарядка();    }    статический пустота пополнить(LightningPhone Телефон) {        Телефон.useLightning();        Телефон.перезарядка();    }    общественный статический пустота основной(Строка[] аргументы) {        Android андроид = новый Android();        Iphone iPhone = новый Iphone();        Система.вне.println(«Зарядка андроида с помощью MicroUsb»);        перезарядкаMicroUsbPhone(андроид);        Система.вне.println(«Зарядка iPhone с помощью молнии»);        перезарядка(iPhone);        Система.вне.println(«Зарядка iPhone с помощью MicroUsb»);        перезарядкаMicroUsbPhone(новый LightningToMicroUsbAdapter (iPhone));    }}

Вывод

Зарядка Android с помощью MicroUsbMicroUsb подключенаПерезарядка началасьПодзарядка завершенаПерезарядка iPhone с помощью LightningМолния подключенаПерезарядка началасьЗарядка завершенаПерезарядка iPhone с помощью MicroUsbMicroUsb подключенаМолния подключена

Python

"""Пример шаблона адаптера."""от abc импорт ABCMeta, абстрактный методНЕ РЕАЛИЗОВАНА = «Вы должны реализовать это».ПЕРЕЗАРЯДИТЬ = [«Перезарядка началась»., «Перезарядка завершена».]POWER_ADAPTERS = {«Android»: «MicroUSB», «iPhone»: "Молния"}СВЯЗАННЫЙ = "{} связанный."CONNECT_FIRST = "Подключиться {} первый."класс RechargeTemplate:    __metaclass__ = ABCMeta    @abstractmethod    def перезарядка(я):        поднять NotImplementedError(НЕ РЕАЛИЗОВАНА)класс Формат IPhone(RechargeTemplate):    @abstractmethod    def use_lightning(я):        поднять NotImplementedError(НЕ РЕАЛИЗОВАНА)класс Формат: Android(RechargeTemplate):    @abstractmethod    def use_micro_usb(я):        поднять NotImplementedError(НЕ РЕАЛИЗОВАНА)класс Айфон(Формат IPhone):    __имя__ = «iPhone»    def __в этом__(я):        я.соединитель = Ложь    def use_lightning(я):        я.соединитель = Правда        Распечатать(СВЯЗАННЫЙ.формат(POWER_ADAPTERS[я.__имя__]))    def перезарядка(я):        если я.соединитель:            для штат в ПЕРЕЗАРЯДИТЬ:                Распечатать(штат)        еще:            Распечатать(CONNECT_FIRST.формат(POWER_ADAPTERS[я.__имя__]))класс Android(Формат: Android):    __имя__ = «Android»    def __в этом__(я):        я.соединитель = Ложь    def use_micro_usb(я):        я.соединитель = Правда        Распечатать(СВЯЗАННЫЙ.формат(POWER_ADAPTERS[я.__имя__]))    def перезарядка(я):        если я.соединитель:            для штат в ПЕРЕЗАРЯДИТЬ:                Распечатать(штат)        еще:            Распечатать(CONNECT_FIRST.формат(POWER_ADAPTERS[я.__имя__]))класс IPhoneAdapter(Формат: Android):    def __в этом__(я, мобильный):        я.мобильный = мобильный    def перезарядка(я):        я.мобильный.перезарядка()    def use_micro_usb(я):        Распечатать(СВЯЗАННЫЙ.формат(POWER_ADAPTERS[«Android»]))        я.мобильный.use_lightning()класс AndroidRecharger:    def __в этом__(я):        я.Телефон = Android()        я.Телефон.use_micro_usb()        я.Телефон.перезарядка()класс IPhoneMicroUSBЗарядное устройство:    def __в этом__(я):        я.Телефон = Айфон()        я.phone_adapter = IPhoneAdapter(я.Телефон)        я.phone_adapter.use_micro_usb()        я.phone_adapter.перезарядка()класс Зарядное устройство для iPhone:    def __в этом__(я):        я.Телефон = Айфон()        я.Телефон.use_lightning()        я.Телефон.перезарядка()Распечатать(«Зарядка Android с помощью зарядного устройства MicroUSB».)AndroidRecharger()Распечатать()Распечатать(«Зарядка iPhone от MicroUSB с помощью адаптера».)IPhoneMicroUSBЗарядное устройство()Распечатать()Распечатать(«Зарядка iPhone с помощью зарядного устройства iPhone».)Зарядное устройство для iPhone()

Смотрите также

использованная литература

  1. ^ а б Фриман, Эрик; Фриман, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Шаблоны проектирования Head First. O'Reilly Media. п. 244. ISBN  978-0-596-00712-6. OCLC  809772256. Архивировано из оригинал (мягкая обложка) на 2013-05-04. Получено 2013-04-30.
  2. ^ Гамма, Эрих; Хелм, Ричард; Джонсон, Ральф; Влиссидес, Джон (1994). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон Уэсли. стр.139ff. ISBN  0-201-63361-2.
  3. ^ «Шаблон проектирования адаптера - проблема, решение и применимость». w3sDesign.com. Получено 2017-08-12.
  4. ^ Фриман, Эрик; Фриман, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка). 1. O'Reilly Media. С. 243, 252, 258, 260. ISBN  978-0-596-00712-6. Получено 2012-07-02.
  5. ^ «Шаблон проектирования адаптера - структура и взаимодействие». w3sDesign.com. Получено 2017-08-12.