В программной инженерии шаблон адаптера — это шаблон проектирования программного обеспечения (также известный как обертка , альтернативное наименование, общее с шаблоном декоратора ), который позволяет использовать интерфейс существующего класса в качестве другого интерфейса. [1] Он часто используется для того, чтобы существующие классы работали с другими без изменения их исходного кода .
Примером является адаптер, который преобразует интерфейс объектной модели документа XML - документа в древовидную структуру, которую можно отобразить.
Шаблон проектирования адаптера [2] является одним из двадцати трех известных шаблонов проектирования «Банды четырех» , которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и повторно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые проще внедрять, изменять, тестировать и повторно использовать.
Шаблон проектирования адаптера решает такие проблемы, как: [3]
Часто (уже существующий) класс не может быть использован повторно только потому, что его интерфейс не соответствует интерфейсу, требуемому клиентами.
Шаблон проектирования адаптера описывает, как решать такие проблемы:
adapter
класс, который преобразует (несовместимый) интерфейс класса ( adaptee
) в другой интерфейс ( target
), требуемый клиентами.adapter
возможность работы с (повторного использования) классами, не имеющими необходимого интерфейса.Основная идея этого шаблона заключается в работе с отдельным классом adapter
, который адаптирует интерфейс (уже существующего) класса, не изменяя его.
Клиенты не знают, работают ли они с target
классом напрямую или через adapter
класс, не имеющий target
интерфейса.
См. также диаграмму классов UML ниже.
Адаптер позволяет двум несовместимым интерфейсам работать вместе. Это реальное определение адаптера. Интерфейсы могут быть несовместимыми, но внутренняя функциональность должна соответствовать потребностям. Шаблон проектирования адаптера позволяет несовместимым классам работать вместе, преобразуя интерфейс одного класса в интерфейс, ожидаемый клиентами.
Адаптер может использоваться, когда оболочка должна уважать определенный интерфейс и должна поддерживать полиморфное поведение. В качестве альтернативы декоратор позволяет добавлять или изменять поведение интерфейса во время выполнения, а фасад используется, когда требуется более простой или легкий интерфейс к базовому объекту. [4]
В приведенной выше диаграмме классов UML класс , которому требуется интерфейс, не может повторно использовать класс напрямую, поскольку его интерфейс не соответствует интерфейсу . Вместо этого он работает через класс, который реализует интерфейс в терминах :client
target
adaptee
target
client
adapter
target
adaptee
object adapter
способ реализует target
интерфейс путем делегирования объекту adaptee
во время выполнения ( adaptee.specificOperation()
).class adapter
способ реализует target
интерфейс путем наследования от adaptee
класса во время компиляции ( specificOperation()
).В этом шаблоне адаптера адаптер содержит экземпляр класса, который он оборачивает. В этой ситуации адаптер делает вызовы экземпляра оборачивающего объекта .
Этот шаблон адаптера использует несколько полиморфных интерфейсов, реализующих или наследующих как ожидаемый интерфейс, так и существующий ранее интерфейс. Обычно ожидаемый интерфейс создается как чистый класс интерфейса , особенно в таких языках, как Java (до JDK 1.8), которые не поддерживают множественное наследование классов. [1]
Желательно classA
предоставить classB
некоторые данные, предположим, некоторые String
данные. Решение времени компиляции:
классБ . setStringData ( classA . getStringData ());
Однако предположим, что формат строковых данных должен быть изменен. Решением времени компиляции является использование наследования:
открытый класс Format1ClassA расширяет ClassA { @Override public String getStringData () { return format ( toString ()); } }
и, возможно, создать правильно «форматирующий» объект во время выполнения с помощью шаблона фабрики .
Решение с использованием «адаптеров» осуществляется следующим образом:
ClassA
и выводит данные, отформатированные соответствующим образом:открытый интерфейс StringProvider { public String getStringData (); } публичный класс ClassAFormat1 реализует StringProvider { частный ClassA classA = null ; общественный ClassAFormat1 ( окончательный ClassA a ) { classA = a ; } public String getStringData () { return format ( classA . getStringData ()); } private String format ( final String sourceValue ) { // Преобразуем исходную строку в формат, требуемый // объектом, которому нужны данные исходного объекта return sourceValue . trim (); } }
открытый класс ClassAFormat1Adapter расширяет адаптер { открытый объект адаптировать ( final Object anObject ) { return new ClassAFormat1 (( ClassA ) anObject ); } }
adapter
в глобальном реестре, чтобы его adapter
можно было найти во время выполнения:AdapterFactory.getInstance ( ) . registerAdapter ( ClassA.class , ClassAFormat1Adapter.class , " format1 " ) ;
ClassA
в ClassB
, напишите:Адаптер адаптер = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format1 " ) ; StringProvider поставщик = ( StringProvider ) адаптер.adapte ( classA ) ; Строка строка = поставщик.getStringData ( ) ; classB.setStringData ( string ) ;
или более кратко:
classB.setStringData ( ( ( StringProvider ) AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format1 " ) . adapt ( classA ) ) . getStringData ( ) ) ;
Адаптер адаптер = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format2 " ) ;
ClassA
, скажем, в виде изображения :Class C
Адаптер адаптер = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , ImageProvider.class , " format2 " ) ; Поставщик ImageProvider = ( ImageProvider ) адаптер.адаптировать ( classA ) ; classC.setImage ( поставщик.getImage ( ) ) ;
ClassB
несколько "представлений" без необходимости изменять иерархию классов. В общем, это позволяет использовать механизм для произвольных потоков данных между объектами, которые могут быть модифицированы для существующей иерархии объектов.ClassC
ClassA
При реализации шаблона адаптера для ясности можно применить имя класса к реализации поставщика; например, . Он должен иметь метод-конструктор с переменной класса адаптируемого объекта в качестве параметра. Этот параметр будет передан члену экземпляра . При вызове clientMethod он будет иметь доступ к экземпляру адаптируемого объекта, что позволяет получить доступ к требуемым данным адаптируемого объекта и выполнять операции с этими данными, которые генерируют желаемый вывод.[ClassName]To[Interface]Adapter
DAOToProviderAdapter
[ClassName]To[Interface]Adapter
интерфейс ILightningPhone { void recharge (); void useLightning (); } интерфейс IMicroUsbPhone { void recharge (); void useMicroUsb (); } класс Iphone реализует ILightningPhone { private boolean connector ; @Override public void useLightning () { connector = true ; System.out.println ( " Lightning подключен " ) ; } @Override public void recharge () { if ( connector ) { System . out . println ( "Перезарядка началась" ); System . out . println ( "Перезарядка завершена" ); } else { System . out . println ( "Сначала подключите Lightning" ); } } } класс Android реализует IMicroUsbPhone { private boolean connector ; @Override public void useMicroUsb () { connector = true ; System.out.println ( " MicroUsb подключен " ) ; } @Override public void recharge () { if ( connector ) { System . out . println ( "Перезарядка начата" ); System . out . println ( "Перезарядка завершена" ); } else { System . out . println ( "Сначала подключите MicroUsb" ); } } } /* предоставление целевого интерфейса при упаковке исходного объекта */ class LightningToMicroUsbAdapter implements IMicroUsbPhone { private final ILightningPhone lightningPhone ; public LightningToMicroUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; } @Override public void useMicroUsb () { System.out.println ( " MicroUsb подключен " ) ; lightningPhone.useLightning ( ) ; } @Override public void recharge () { lightningPhone.recharge ( ) ; } } public class AdapterDemo { static void rechargeMicroUsbPhone ( IMicroUsbPhone телефон ) { phone.useMicroUsb ( ) ; phone.recharge ( ) ; } static void rechargeLightningPhone ( телефон ILightningPhone ) { телефон . useLightning (); телефон . recharge (); } public static void main ( String [] args ) { Android android = new Android (); Iphone iPhone = new Iphone (); System.out.println ( " Подзарядка Android с помощью MicroUsb " ) ; rechargeMicroUsbPhone ( android ) ; System.out.println ( " Подзарядка iPhone с помощью Lightning " ) ; rechargeLightningPhone ( iPhone ) ; System.out.println ( " Подзарядка iPhone с помощью MicroUsb" ); rechargeMicroUsbPhone ( new LightningToMicroUsbAdapter ( iPhone ) ) ; } }
Выход
Подзарядка андроида через MicroUsbMicroUSB подключенПополнение началосьПополнение завершеноЗарядка iPhone с помощью LightningМолния подключенаПополнение началосьПополнение завершеноПодзарядка iPhone через MicroUsbMicroUSB подключенМолния подключенаПополнение началосьПополнение завершено
""" Пример шаблона адаптера. """ from abc import ABCMeta , abstractmethodNOT_IMPLEMENTED = "Вам следует это реализовать."RECHARGE = [ "Пополнение начато." , "Пополнение завершено." ]POWER_ADAPTERS = { "Android" : "MicroUSB" , "iPhone" : "Lightning" }CONNECTED = " {} подключено." CONNECT_FIRST = " Сначала подключитесь {} ."класс RechargeTemplate ( метакласс = ABCMeta ): @abstractmethod def recharge ( self ): вызвать NotImplementedError ( NOT_IMPLEMENTED )класс FormatIPhone ( RechargeTemplate ): @abstractmethod def use_lightning ( self ): вызвать NotImplementedError ( NOT_IMPLEMENTED )класс FormatAndroid ( RechargeTemplate ): @abstractmethod def use_micro_usb ( self ): вызвать NotImplementedError ( NOT_IMPLEMENTED )класс IPhone ( FormatIPhone ): __name__ = "iPhone" def __init__ ( self ): self . connector = False def use_lightning ( self ): self . connector = True print ( CONNECTED . format ( POWER_ADAPTERS [ self . __name__ ])) def recharge ( self ): if self . connector : for state in RECHARGE : print ( state ) else : print ( CONNECT_FIRST . format ( POWER_ADAPTERS [ self . __name__ ]))класс Android ( FormatAndroid ): __name__ = "Android" def __init__ ( self ): self . connector = False def use_micro_usb ( self ): self . connector = True print ( CONNECTED . format ( POWER_ADAPTERS [ self . __name__ ])) def recharge ( self ): if self . connector : for state in RECHARGE : print ( state ) else : print ( CONNECT_FIRST . format ( POWER_ADAPTERS [ self . __name__ ]))класс IPhoneAdapter ( FormatAndroid ): def __init__ ( self , mobile ): self . mobile = mobile def recharge ( self ): self . mobile . recharge () def use_micro_usb ( self ): print ( CONNECTED . format ( POWER_ADAPTERS [ "Android" ])) self . mobile . use_lightning ()класс AndroidRecharger : def __init__ ( self ) : self.phone = Android ( ) self.phone.use_micro_usb ( ) self.phone.recharge ( ) класс IPhoneMicroUSBRecharger : def __init __ ( self ) : self.phone = IPhone ( ) self.phone_adapter = IPhoneAdapter ( self.phone ) self.phone_adapter.use_micro_usb ( ) self.phone_adapter.recharge ( ) класс IPhoneRecharger : def __init__ ( self ) : self.phone = IPhone ( ) self.phone.use_lightning ( ) self.phone.recharge ( ) печать ( "Зарядка Android с помощью зарядного устройства MicroUSB." ) AndroidRecharger () печать ()печать ( "Зарядка iPhone с помощью MicroUSB с использованием шаблона адаптера." ) IPhoneMicroUSBRecharger () печать ()печать ( "Зарядка iPhone с помощью зарядного устройства для iPhone." ) IPhoneRecharger ()
открытый интерфейс ILightningPhone { void ConnectLightning (); void Recharge (); } открытый интерфейс IUsbPhone { void ConnectUsb (); void Recharge (); } public sealed class AndroidPhone : IUsbPhone { private bool isConnected ; public void ConnectUsb () { this . isConnected = true ; Console . WriteLine ( "Телефон Android подключен." ); } public void Recharge () { if ( this . isConnected ) { Console . WriteLine ( "Android-телефон заряжается." ); } else { Console . WriteLine ( "Сначала подключите USB-кабель." ); } } } public sealed class ApplePhone : ILightningPhone { private bool isConnected ; public void ConnectLightning () { this . isConnected = true ; Console . WriteLine ( "Телефон Apple подключен." ); } public void Recharge () { if ( this . isConnected ) { Console . WriteLine ( "Зарядка телефона Apple." ); } else { Console . WriteLine ( "Сначала подключите кабель Lightning." ); } } } public sealed class LightningToUsbAdapter : IUsbPhone { private readonly ILightningPhone lightningPhone ; private bool isConnected ; public LightningToUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; } public void ConnectUsb () { this . lightningPhone . ConnectLightning (); } public void Recharge ( ) { this.lightningPhone.Recharge ( ) ; } } public void Main () { ILightningPhone applePhone = new ApplePhone (); IUsbPhone адаптерКабель = new LightningToUsbAdapter ( applePhone ) ; адаптерКабель.ПодключитьUsb ( ); адаптерКабель.Перезарядка ( ) ; }
Выход:
Телефон Apple подключен. Кабель-адаптер подключен. Телефон Apple заряжается.