stringtranslate.com

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

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

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

Обзор

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

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

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

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

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

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

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

Определение

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

Использование

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

Структура

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

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

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

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

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

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

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

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

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

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

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

Желательно classAпредоставить classBнекоторые данные, предположим, некоторые Stringданные. Решение времени компиляции:

классБ . setStringData ( classA . getStringData ());

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

открытый класс Format1ClassA расширяет ClassA { @Override public String getStringData () { return format ( toString ()); } }             

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

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

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

  1. Определите промежуточный интерфейс «поставщика» и напишите реализацию этого интерфейса поставщика, который в этом примере оборачивает источник данных 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 (); } }          
  2. Напишите класс адаптера, который возвращает конкретную реализацию поставщика:
    открытый класс ClassAFormat1Adapter расширяет адаптер { открытый объект адаптировать ( final Object anObject ) { return new ClassAFormat1 (( ClassA ) anObject ); } }                
  3. Зарегистрируйте adapterв глобальном реестре, чтобы его adapterможно было найти во время выполнения:
    AdapterFactory.getInstance ( ) . registerAdapter ( ClassA.class , ClassAFormat1Adapter.class , " format1 " ) ;  
  4. В коде, если вы хотите передать данные из 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 ( ) ) ;       
  5. Преимущество можно увидеть в том, что если требуется передать данные во втором формате, то следует поискать другого адаптера/провайдера:
    Адаптер адаптер = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format2 " ) ;      
  6. А если требуется вывести данные ClassA, скажем, в виде изображения :Class C
    Адаптер адаптер = AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , ImageProvider.class , " format2 " ) ; Поставщик ImageProvider = ( ImageProvider ) адаптер.адаптировать ( classA ) ; classC.setImage ( поставщик.getImage ( ) ) ;          
  7. Таким образом, использование адаптеров и поставщиков позволяет использовать ClassBнесколько "представлений" без необходимости изменять иерархию классов. В общем, это позволяет использовать механизм для произвольных потоков данных между объектами, которые могут быть модифицированы для существующей иерархии объектов.ClassCClassA

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

При реализации шаблона адаптера для ясности можно применить имя класса к реализации поставщика; например, . Он должен иметь метод-конструктор с переменной класса адаптируемого объекта в качестве параметра. Этот параметр будет передан члену экземпляра . При вызове clientMethod он будет иметь доступ к экземпляру адаптируемого объекта, что позволяет получить доступ к требуемым данным адаптируемого объекта и выполнять операции с этими данными, которые генерируют желаемый вывод.[ClassName]To[Interface]AdapterDAOToProviderAdapter[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 заряжается.


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

Ссылки

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