Wzór adaptera

W inżynierii oprogramowania wzorzec adaptera to wzorzec projektowy oprogramowania (znany również jako opakowanie , alternatywne nazewnictwo współdzielone ze wzorcem dekoratora ), który umożliwia użycie interfejsu istniejącej klasy jako innego interfejsu. Jest często używany, aby istniejące klasy działały z innymi bez modyfikowania ich kodu źródłowego .

Przykładem jest adapter, który konwertuje interfejs Document Object Model dokumentu XML na strukturę drzewa, która może być wyświetlana.

Przegląd

Wzorzec projektowy adaptera jest jednym z dwudziestu trzech dobrze znanych wzorców projektowych Gang of Four , które opisują, jak rozwiązywać powtarzające się problemy projektowe w celu zaprojektowania elastycznego i wielokrotnego użytku oprogramowania zorientowanego obiektowo, czyli obiektów łatwiejszych do wdrożenia, zmiany, przetestowania i ponownie użyć.

Wzorzec projektowy adaptera rozwiązuje takie problemy jak:

  • Jak można ponownie wykorzystać klasę, która nie ma interfejsu wymaganego przez klienta?
  • W jaki sposób klasy, które mają niekompatybilne interfejsy, mogą ze sobą współpracować?
  • W jaki sposób można zapewnić alternatywny interfejs dla klasy?

Często (już istniejącej) klasy nie można ponownie użyć tylko dlatego, że jej interfejs nie jest zgodny z wymaganiami klientów.

Wzorzec projektowy adaptera opisuje sposób rozwiązywania takich problemów:

  • Zdefiniuj oddzielną klasę adaptera , która konwertuje (niekompatybilny) interfejs klasy ( adaptee ) na inny interfejs ( target ) wymagany przez klientów.
  • Pracuj z adapterem , aby pracować z (ponownie używanymi) klasami, które nie mają wymaganego interfejsu.

Kluczową ideą tego wzorca jest praca z oddzielnym adapterem , który dostosowuje interfejs (już istniejącej) klasy bez jej zmiany.

Klienci nie wiedzą, czy pracują z klasą docelową bezpośrednio, czy za pośrednictwem adaptera z klasą, która nie ma interfejsu docelowego .

Zobacz także diagram klas UML poniżej.

Definicja

Adapter umożliwia współpracę dwóch niekompatybilnych interfejsów. To jest rzeczywista definicja adaptera. Interfejsy mogą być niekompatybilne, ale wewnętrzna funkcjonalność powinna odpowiadać potrzebom. Wzorzec projektowy adaptera umożliwia współpracę niekompatybilnych klas poprzez konwersję interfejsu jednej klasy na interfejs oczekiwany przez klientów.

Stosowanie

Adaptera można użyć, gdy opakowanie musi przestrzegać określonego interfejsu i musi obsługiwać zachowanie polimorficzne . Alternatywnie, dekorator umożliwia dodawanie lub zmianę zachowania interfejsu w czasie wykonywania, a fasada jest używana, gdy pożądany jest łatwiejszy lub prostszy interfejs do obiektu leżącego poniżej.

Wzór Zamiar
Adapter lub opakowanie Konwertuje jeden interfejs na inny, tak aby odpowiadał oczekiwaniom klienta
Dekorator Dynamicznie dodaje odpowiedzialność do interfejsu, zawijając oryginalny kod
Delegacja Wspieraj „kompozycję zamiast dziedziczenia”
Fasada Zapewnia uproszczony interfejs

Struktura

Diagram klas UML

Przykładowy diagram klas UML dla wzorca projektowego adaptera.

Na powyższym diagramie klas UML klasa klienta, która wymaga interfejsu docelowego , nie może bezpośrednio ponownie użyć klasy adaptee , ponieważ jej interfejs nie jest zgodny z interfejsem docelowym . Zamiast tego klient działa poprzez klasę adaptera , która implementuje interfejs docelowy w kategoriach adaptee :

  • Sposób adaptera obiektów implementuje interfejs docelowy poprzez delegowanie do obiektu adaptee w czasie wykonywania ( adaptee.specificOperation() ).
  • adaptera klas implementuje docelowy interfejs poprzez dziedziczenie z klasy adaptee w czasie kompilacji ( specificOperation() ).

Wzorzec adaptera obiektu

W tym wzorcu adaptera adapter zawiera instancję klasy, którą opakowuje. W tej sytuacji adapter wykonuje wywołania instancji opakowanego obiektu .

Wzorzec adaptera obiektu wyrażony w języku UML
Wzorzec adaptera obiektu wyrażony w LePUS3

Wzorzec adaptera klasy

Ten wzorzec adaptera wykorzystuje wiele interfejsów polimorficznych , które implementują lub dziedziczą zarówno oczekiwany interfejs, jak i istniejący wcześniej interfejs. Typowe jest, że oczekiwany interfejs jest tworzony jako czysta interfejsu , szczególnie w językach takich jak Java (przed JDK 1.8), które nie obsługują wielokrotnego dziedziczenia klas.

Wzorzec adaptera klas wyrażony w języku UML .
Wzorzec adaptera klasy wyrażony w LePUS3

Dalsza forma wzorca adaptera środowiska wykonawczego

Motywacja z rozwiązania czasu kompilacji

Pożądane jest, aby klasa A dostarczała klasie B pewne dane, załóżmy, że niektóre dane typu String . Rozwiązaniem czasu kompilacji jest:

 klasa  B.  setStringData  (  klasa A.  getStringData  (  )); 

Załóżmy jednak, że format danych ciągu musi być zróżnicowany. Rozwiązaniem czasu kompilacji jest użycie dziedziczenia:

     
    
       
         
    
 klasa  publiczna  Format1ClassA  rozszerza  ClassA  {  @Override  public  String  getStringData  ()  {  format  powrotu  (  toString  ());  }  } 

i być może utwórz poprawnie „formatujący” obiekt w czasie wykonywania za pomocą wzorca fabrycznego .

Rozwiązanie adaptera czasu pracy

Rozwiązanie z wykorzystaniem „adapterów” przebiega w następujący sposób:

  1. Zdefiniuj pośredni interfejs „dostawcy” i napisz implementację tego interfejsu dostawcy, która opakowuje źródło danych, w tym przykładzie ClassA , i wyprowadza dane w odpowiednim formacie:
       
          
    
    
         
            
    
            
              
        
    
           
             
        
    
             
            
            
             
        
     publiczny  interfejs  StringProvider  {  publiczny  ciąg  getStringData  ();  }  public  class  ClassAFormat1  implementuje  StringProvider  {  private  ClassA  classA  =  null  ;  public  ClassAFormat1  (  final  ClassA  a  )  {  classA  =  a  ;  }  public  String  getStringData  ()  {  zwracany  format  (  classA  .  getStringData  ());  }  private  String  format  (  final  String  sourceValue  )  {  // Zmodyfikuj łańcuch źródłowy do formatu wymaganego // przez obiekt wymagający   zwrotu  danych obiektu źródłowego  sourceValue  .  przycinanie  ();  }  } 
    
  2. Napisz klasę adaptera, która zwraca konkretną implementację dostawcy:
         
             
               
        
     klasa  publiczna  ClassAFormat1Adapter  rozszerza  adapter  {  public  Object  adapt  (  final  Object  anObject  )  {  return  new  ClassAFormat1  ((  ClassA  )  anObject  );  }  } 
    
  3. Zarejestruj adapter w rejestrze globalnym, aby adapter mógł być wyszukiwany w czasie wykonywania:
       Fabryka adapterów  .  pobierzInstancję  ().  registerAdapter  (  ClassA  .  klasa  ,  ClassAFormat1Adapter  .  klasa  ,  "format1"  ); 
    
  4. W kodzie, chcąc przenieść dane z ClassA do ClassB , napisz:
      
        
              
        
       
     Adapter  adaptera  =  Fabryka adapterów  .  getInstance  ()  .  getAdapterFromTo  (  ClassA  .  klasa  ,  StringProvider  .  klasa  ,  "format1"  );  Dostawca  StringProvider  =  adapter  (  StringProvider  )  .  dostosować  (  klasa A  );  Ciąg  znaków  =  dostawca  .  getStringData  ();  klasa  B.  setStringData  (  string  ); 
    

    lub bardziej zwięźle:

    
        
                
                      
                    
             klasa  B.  setStringData  (  ((  StringProvider  )  AdapterFactory  .  getInstance  ()  .  getAdapterFromTo  (  ClassA  .  class  ,  StringProvider  .  class  ,  "format1"  )  .  adapt  (  class A  ))  .  getStringData  ()); 
    
  5. Zaletą jest to, że jeśli chcesz przesłać dane w drugim formacie, poszukaj innego adaptera/dostawcy:
      
        
               Adapter  adaptera  =  Fabryka adapterów  .  getInstance  ()  .  getAdapterFromTo  (  ClassA  .  klasa  ,  StringProvider  .  klasa  ,  "format2"  ); 
    
  6. A jeśli pożądane jest wyprowadzenie danych z klasy A jako, powiedzmy, danych obrazu w klasie C :
      
        
              
        
     Adapter  adaptera  =  Fabryka adapterów  .  getInstance  ()  .  getAdapterFromTo  (  Klasa  A.  klasa  ,  ImageProvider  .  klasa  ,  "format2"  );  Dostawca  ImageProvider  =  adapter  (  ImageProvider  )  .  dostosować  (  klasa A  );  klasa C  .  setImage  (  dostawca  .  getImage  ()); 
    
  7. W ten sposób użycie adapterów i dostawców umożliwia wiele „widoków” z klasy B i klasy C do klasy A bez konieczności zmiany hierarchii klas. Ogólnie rzecz biorąc, umożliwia mechanizm dowolnego przepływu danych między obiektami, który można zmodernizować do istniejącej hierarchii obiektów.

Implementacja wzorca adaptera

Podczas implementacji wzorca adaptera, dla jasności, można zastosować nazwę klasy [ ClassName ] To [ Interface ] Adapter do implementacji dostawcy; na przykład DAOToProviderAdapter . Powinien mieć metodę konstruktora ze zmienną klasy adaptee jako parametr. Ten parametr zostanie przekazany do elementu członkowskiego instancji adaptera [ nazwa klasy ] do [ interfejsu ] . Po wywołaniu metody clientMethod będzie ona miała dostęp do instancji osoby adaptującej, która umożliwia dostęp do wymaganych danych osoby adaptowanej i wykonywanie operacji na tych danych, które generują pożądane dane wyjściowe.

Jawa

  
     
     


  
     
     


    
      

    
       
          
        
    

    
       
          
            
            
          
            
        
    


    
      

    
       
          
        
    

    
       
          
            
            
          
            
        
    


    
       

       
          
    

    
       
        
        
    

    
       
        
    


   
        
        
        
    

        
        
        
    

         
            
            

        
        

        
        

        
          
    
 interfejs  ILightningPhone  {  unieważnić  ładowanie  ();  unieważnić  użycie Błyskawicy  ();  }  interfejs  IMicroUsbPhone  {  unieważnić  ładowanie  ();  nieważne  użycieMicroUsb  ();  }  class  Iphone  implementuje  ILightningPhone  {  private  boolean  connector  ;  @Override  public  void  useLightning  ()  {  złącze  =  true  ;  systemu  .  na  zewnątrz  println  (  "Błyskawica połączona"  );  }  @Override  public  void  doładowanie  ()  {  if  (  łącznik  )  {  System  .  na  zewnątrz  println  (  "Doładowanie rozpoczęte"  );  systemu  .  na  zewnątrz  println  (  "Ładowanie zakończone"  );  }  else  {  System  .  na  zewnątrz  println  (  "Najpierw podłącz Lightning"  );  }  }  }  klasa  Android  implementuje  IMicroUsbPhone  {  private  boolean  connector  ;  @Override  public  void  useMicroUsb  ()  {  złącze  =  true  ;  systemu  .  na  zewnątrz  println  (  "Połączono z MicroUSB"  );  }  @Override  public  void  doładowanie  ()  {  if  (  łącznik  )  {  System  .  na  zewnątrz  println  (  "Doładowanie rozpoczęte"  );  systemu  .  na  zewnątrz  println  (  "Ładowanie zakończone"  );  }  else  {  System  .  na  zewnątrz  println  (  "Najpierw podłącz MicroUsb"  );  } }  }  /  * eksponowanie interfejsu docelowego podczas zawijania obiektu źródłowego */  class  LightningToMicroUsbAdapter  implements  IMicroUsbPhone  {  private  final  ILightningPhone  lightningPhone  ;  public  LightningToMicroUsbAdapter  (  ILightningPhone  lightningPhone  )  {  this  .  błyskawicaTelefon  =  błyskawicaTelefon  ;  }  @Override  public  void  useMicroUsb  ()  {  System  .  na  zewnątrz  println  (  "Połączono z MicroUSB"  );  BłyskawicaTelefon  .  użyj Błyskawicy  ();  }  @Override  public  void  doładowanie  ()  {  lightningPhone  .  naładować  ();  }  }  Public  Class  AdapterDemo  {  static  void  rechargeMicroUsbPhone  (  telefon  IMicroUsbPhone  )  {  telefon  .  użyjMicroUsb  ();  telefon  .  naładować  ();  }  static  void  doładowanie LightningPhone  (  ILightningPhone  phone  )  {  phone  .  użyj Błyskawicy  ();  telefon  .  naładować  ();  }  public  static  void  main  (  String  []  args  )  {  Android  android  =  nowy  Android  ();  iPhone  iPhone  =  nowy  iPhone  ();  systemu  .  na  zewnątrz  println  (  "Ładowanie Androida przez MicroUsb"  );  naładuj MicroUsbPhone  (  Android  );  systemu  .  na  zewnątrz  println  (  "Ładowanie iPhone'a za pomocą Lightning"  );  naładujLightningPhone  (  iPhone  );  systemu  .  na  zewnątrz  println  (  "Ładowanie iPhone'a przez MicroUsb"  );  naładuj MicroUsbPhone  (  nowy  LightningToMicroUsbAdapter  (  iPhone  ));  }  } 

Wyjście

Ładowanie Androida z MicroUsb MicroUsb podłączone Ładowanie rozpoczęte Ładowanie zakończone Ładowanie iPhone'a z Lightning Lightning połączone Ładowanie rozpoczęte Ładowanie zakończone Ładowanie iPhone'a z MicroUsb MicroUsb połączone Lightning połączone Ładowanie rozpoczęte Ładowanie zakończone

Pyton




    

  

   

     

  
  

 

    
     
         

 
    
     
         

 
    
     
         

 
      

     
          

     
          
        

     
         
               
                
        
            

 
      

     
          

     
          
        

     
         
               
                
        
            

 
      
          

     
        

     
        
        

 
     
          
        
        

 
     
          
          
        
        

 
     
          
        
        










 """  Przykład wzorca adaptera.  """  from  abc  import  ABCMeta  ,  abstractmethod  NOT_IMPLEMENTED  =  "Powinieneś to zaimplementować."  RECHARGE  =  [  "Rozpoczęło się ładowanie."  ,  „Ładowanie zakończone”.  ]  POWER_ADAPTERS  =  {  "Android"  :  "MicroUSB"  ,  "iPhone"  :  "Lightning"  }  CONNECTED  =  "  {}  połączono."  CONNECT_FIRST  =  "Najpierw połącz  {}  ."  klasa  recargetemplate  (  metaclass  =  abcmeta  ):  @abstractmethod  def  recarge  (  self  )  :  podnieś  notImplementedError  (  not_implemented  )  classatiphone  (  rechargetEmplate  ):  @abstractmethod  def  użyte_lightning  (  self  )  :  Raise  NotImplementEdror  (  nie_implementedAdandRoid  (  rechargeMplate )   : @AbStamplate   )  :  @AbStamPlate  )  _Micro_USB  (  self  ):  podnieś  NotImplementedError  (  NOT_IMPLEMENTED  )  klasa  IPhone  (  FormatIPhone  ):  __name__  =  "iPhone"  def  __init__  (  self  ):  self  .  złącze  =  False  def  use_lightning  (  self  ):  self  .  złącze  =  True  print  (  CONNECTED  .  format  (  POWER_ADAPTERS  [  self  .  __name__  ]))  def  recharge  (  self  ):  if  self  .  złącze  :  dla  stanu  w  RECHARGE  :  print  (  stan  )  else  :  print  (  CONNECT_FIRST  .  format  (  PIERWSZY_PRZEŁĄCZNIKI  [  self  .  __name__  ]))  klasa  Android  (  FormatAndroid  ):  __name__  =  "Android"  def  __init__  (  self  ):  self  .  złącze  =  False  def  use_micro_usb  (  self  ):  self  .  złącze  =  True  print  (  CONNECTED  .  format  (  POWER_ADAPTERS  [  self  .  __name__  ]))  def  recharge  (  self  ):  if  self  .  złącze  :  dla  stanu  w  RECHARGE  :  print  (  stan  )  else  :  print  (  CONNECT_FIRST  .  format  (  POWER_ADAPTERS  [  self  .  __name__  ]))  class  IPhoneAdapter  (  FormatAndroid  ):  def  __init__  (  self  ,  mobile  ):  self  .  mobile  =  mobile  def  doładowanie  (  self  ):  self  .  mobilny  .  recharge  ()  def  use_micro_usb  (  self  ):  print  (  CONNECTED  .  format  (  POWER_ADAPTERS  [  "Android"  ]))  self  .  mobilny  .  use_lightning  ()  klasa  AndroidRecharger  :  def  __init__  (  self  ):  self  .  telefon  =  sam  Android  ()  .  telefon  .  use_micro_usb  ()  sam  .  telefon  .  ładowanie  ()  klasa  IPhoneMicroUSBRecharger  :  def  __init__  (  self  ):  self  .  telefon  =  IPhone  ()  sam  .  phone_adapter  =  IPhoneAdapter  (  self  .  phone  )  self  .  telefon_adapter  .  use_micro_usb  ()  sam  .  telefon_adapter  .  ładowanie  ()  klasa  IPhoneRecharger  :  def  __init__  (  self  ):  self  .  telefon  =  IPhone  ()  sam  .  telefon  .  use_lightning  ()  siebie  .  telefon  .  recharge  ()  print  (  „Ładowanie Androida za pomocą ładowarki MicroUSB.”  )  AndroidRecharger  ()  print  ()  print  (  „Ładowanie iPhone'a za pomocą ładowarki MicroUSB przy użyciu wzoru adaptera.”  )  IPhoneMicroUSBRecharger  ()  print  ()  print  (  „Ładowanie iPhone'a za pomocą ładowarki iPhone'a.”  )  Ładowarka iPhone'a  () 

C#

  

	 
	 


  

	 
	 


     

	  
	
	  
	
		  
		
	

	  
	
		 
		
			
		
		
		
			
		
	


     

	  
	
	  
	
		  
		
	

	  
	
		 
		
			
		
		
		
			
		
	


     

	   
	
	  
	
	  
	
		  
		
	
	
	  
	
		  
		
	

	  
	
		 
		
			
		
		
		
			
		
	


  

	    
	    
	
	
 interfejs  publiczny  ILightningPhone  {  unieważnić  ConnectLightning  ();  anuluj  ładowanie  ();  }  publiczny  interfejs  IUsbPhone  {  nieważne  ConnectUsb  ();  anuluj  ładowanie  ();  }  publiczna  zapieczętowana  klasa  AndroidPhone  :  IUsbPhone  {  private  bool  isConnected  ;  public  void  ConnectUsb  ()  {  to  .  isConnected  =  true  ;  Konsola  .  WriteLine  (  "Połączono telefon z Androidem."  );  }  public  void  Recharge  ()  {  if  (  this  .  isConnected  )  {  Konsola  .  WriteLine  (  "Ładowanie telefonu z Androidem."  );  }  else  {  Konsola  .  WriteLine  (  "Najpierw podłącz kabel USB."  );  }  }  }  publiczna  zapieczętowana  klasa  ApplePhone  :  ILightningPhone  {  private  bool  isConnected  ;  public  void  ConnectLightning  ()  {  to  .  isConnected  =  true  ;  Konsola  .  WriteLine  (  "Połączono telefon Apple."  );  }  public  void  Recharge  ()  {  if  (  this  .  isConnected  )  {  Konsola  .  WriteLine  (  "Ładowanie telefonu Apple."  );  }  else  {  Konsola  .  WriteLine  (  "Najpierw podłącz kabel Lightning."  );  }  }  }  publiczna  zapieczętowana  klasa  LightningToUsbAdapter  :  IUsbPhone  {  private  tylko do odczytu  ILightningPhone  lightningPhone  ;  prywatny  bool  jest podłączony  ;  public  LightningToUsbAdapter  (  ILightningPhone  lightningPhone  )  {  this  .  błyskawicaTelefon  =  błyskawicaTelefon  ;  to  .  BłyskawicaTelefon  .  Połącz Błyskawicę  ();  }  public  void  ConnectUsb  ()  {  to  .  isConnected  =  true  ;  Konsola  .  WriteLine  (  "Kabel adaptera podłączony."  );  }  public  void  Recharge  ()  {  if  (  this  .  isConnected  )  {  this  .  BłyskawicaTelefon  .  Naładuj  ();  }  else  {  Konsola  .  WriteLine  (  "Najpierw podłącz kabel USB."  );  }  }  }  public  void  Main  ()  {  ILightningPhone  applePhone  =  nowy  ApplePhone  ();  IUsbPhone  adapterCable  =  nowy  LightningToUsbAdapter  (  applePhone  );  adapter Kabel  .  PołączUsb  ();  adapter Kabel  .  Naładuj  ();  } 

Wyjście:



 Telefon Apple podłączony.  Kabel adaptera podłączony.  Ładowanie telefonu Apple. 

Zobacz też