어댑터 패턴 - Adapter pattern

에서는 소프트웨어 엔지니어링 상기 어댑터 패턴 A는 디자인 패턴 (도 랩퍼는 공유 다른 이름으로 알려져 장식 패턴 허용) 인터페이스를 기존의 클래스가 다른 인터페이스로 사용이. [1] 종종 기존 클래스가 소스 코드 를 수정하지 않고 다른 클래스와 함께 작동하도록 만드는 데 사용됩니다 .

XML 문서 문서 객체 모델 인터페이스를 표시 할 수있는 트리 구조 로 변환하는 어댑터를 예로 들 수 있습니다.

개요

어댑터 [2] 디자인 패턴은 유연하고 재사용 가능한 객체 지향 소프트웨어를 디자인하기 위해 반복되는 디자인 문제를 해결하는 방법을 설명하는 23 개의 잘 알려진 GoF 디자인 패턴 중 하나입니다. 테스트 및 재사용.

어댑터 디자인 패턴은 다음과 같은 문제를 해결합니다. [3]

  • 클라이언트에 필요한 인터페이스가없는 클래스를 어떻게 재사용 할 수 있습니까?
  • 호환되지 않는 인터페이스가있는 클래스는 어떻게 함께 작동 할 수 있습니까?
  • 수업에 대체 인터페이스를 어떻게 제공 할 수 있습니까?

종종 (이미 존재하는) 클래스는 인터페이스가 클라이언트가 요구하는 인터페이스를 따르지 않기 때문에 재사용 할 수 없습니다.

어댑터 디자인 패턴은 이러한 문제를 해결하는 방법을 설명합니다.

  • adapter클래스 ( adaptee) 의 (호환되지 않는) 인터페이스를 target클라이언트가 요구하는 다른 인터페이스 ( ) 로 변환 하는 별도의 클래스를 정의하십시오 .
  • adapter필요한 인터페이스가없는 클래스로 작업 (재사용) 하려면를 통해 작업하십시오.

이 패턴의 핵심 아이디어 adapter는 (이미 존재하는) 클래스의 인터페이스를 변경하지 않고 조정 하는 별도의 작업을 수행 하는 것입니다.

클라이언트는 target클래스에서 직접 작업하는지 아니면 인터페이스 adapter가없는 클래스를 통해 작업하는지 알지 못합니다 target.

아래의 UML 클래스 다이어그램을 참조하십시오.

정의

어댑터를 사용하면 호환되지 않는 두 인터페이스가 함께 작동 할 수 있습니다. 이것은 어댑터에 대한 실제 정의입니다. 인터페이스가 호환되지 않을 수 있지만 내부 기능이 필요에 맞아야합니다. 어댑터 디자인 패턴을 사용하면 한 클래스의 인터페이스를 클라이언트가 예상하는 인터페이스로 변환하여 호환되지 않는 클래스가 함께 작동 할 수 있습니다.

용법

랩퍼가 특정 인터페이스를 존중하고 다형성 동작을 지원해야하는 경우 어댑터를 사용할 수 있습니다 . 또는 데코레이터를 사용하면 런타임에 인터페이스의 동작을 추가하거나 변경할 수 있으며, 기본 객체에 대한 더 쉽고 간단한 인터페이스가 필요할 때 파사드 가 사용됩니다. [4]

무늬 의지
어댑터 또는 래퍼 클라이언트가 기대하는 것과 일치하도록 한 인터페이스를 다른 인터페이스로 변환합니다.
데코레이터 원래 코드를 래핑하여 인터페이스에 책임을 동적으로 추가
대표단 "상속보다 구성"지원
정면 단순화 된 인터페이스 제공

구조

UML 클래스 다이어그램

어댑터 디자인 패턴에 대한 샘플 UML 클래스 다이어그램입니다. [5]

위의 UML 클래스 다이어그램 에서 인터페이스 client가 필요한 클래스는 인터페이스가 인터페이스를 따르지 않기 때문에 클래스를 직접 target재사용 할 수 없습니다 . 대신 다음과 같은 측면 에서 인터페이스 를 구현 하는 클래스를 통해 작동합니다 .adapteetargetclientadaptertargetadaptee

  • object adapter방법은 구현 target에 위임하여 인터페이스를 adaptee실행시 객체 ( adaptee.specificOperation()).
  • class adapter방법 은 컴파일 타임 ( ) 에서 클래스에서 target상속 하여 인터페이스를 구현합니다 .adapteespecificOperation()

개체 어댑터 패턴

이 어댑터 패턴에서 어댑터는 래핑하는 클래스의 인스턴스를 포함합니다. 이 상황에서 어댑터는 래핑 된 개체 의 인스턴스를 호출 합니다 .

UML로 표현 된 객체 어댑터 패턴
LePUS3로 표현 된 객체 어댑터 패턴

클래스 어댑터 패턴

이 어댑터 패턴 은 예상되는 인터페이스와 기존 인터페이스를 모두 구현하거나 상속 하는 다중 다형성 인터페이스를 사용합니다 . 특히 클래스의 다중 상속지원하지 않는 Java (JDK 1.8 이전) 와 같은 언어 에서는 예상되는 인터페이스가 순수 인터페이스 클래스 로 생성되는 것이 일반적입니다 . [1]

UML로 표현 된 클래스 어댑터 패턴 입니다.
LePUS3로 표현 된 클래스 어댑터 패턴

추가 형태의 런타임 어댑터 패턴

컴파일 타임 솔루션의 동기

일부 데이터 classA를 제공 classB하는 것이 바람직 String합니다. 일부 데이터를 가정 해 보겠습니다 . 컴파일 타임 솔루션은 다음과 같습니다.

classB . setStringData ( classA . getStringData ()); 

그러나 문자열 데이터의 형식이 변경되어야한다고 가정하십시오. 컴파일 타임 솔루션은 상속을 사용하는 것입니다.

공용  클래스  Format1ClassA는  ClassA를 확장합니다  { @Override public String getStringData () { return format ( toString ()); } }           

그리고 아마도 팩토리 패턴 을 사용하여 런타임에 올바른 "포맷팅"객체를 생성 할 수 있습니다 .

런타임 어댑터 솔루션

"어댑터"를 사용하는 솔루션은 다음과 같이 진행됩니다.

(i) 중간 "제공자"인터페이스를 정의 ClassA하고이 예에서 데이터 소스를 래핑하고 적절한 형식의 데이터를 출력하는 해당 공급자 인터페이스의 구현을 작성합니다 .

공용  인터페이스  StringProvider  {  공용  문자열  getStringData ();  }  공용  클래스  ClassAFormat1의  구현  StringProvider  {  개인  를 ClassA  를 ClassA  =  널 (null) ;  공개  ClassAFormat1 ( 최종  를 ClassA의  ) { 를 ClassA = ; } public String getStringData () { 반환 형식 ( classA . getStringData ()); }             private  String  format ( final  String  sourceValue )  {  // 소스 문자열을  소스 객체의 데이터를 필요로하는 객체에 의해 //  필요한 형식으로 조작합니다 . return  sourceValue . 트림 ();  }  } 

(ii) 공급자의 특정 구현을 반환하는 어댑터 클래스를 작성합니다.

public  class  ClassAFormat1Adapter  extends  Adapter  {  public  Object  adapt ( final  Object  anObject )  {  return  new  ClassAFormat1 (( ClassA )  anObject );  }  } 

(iii)를 adapter전역 레지스트리에 등록하여 adapter런타임에를 조회 할 수 있습니다.

AdapterFactory . getInstance (). registerAdapter ( ClassA . class ,  ClassAFormat1Adapter . class ,  "format1" ); 

(iv) 코드에서에서로 데이터를 전송 ClassA하려면 다음 ClassB을 작성하십시오.

어댑터  어댑터  =  AdapterFactory . getInstance ()  . getAdapterFromTo ( ClassA . class ,  StringProvider . class ,  "format1" );  StringProvider  provider  =  ( StringProvider )  어댑터 . 적응 ( classA );  문자열  문자열  =  공급자 . getStringData ();  classB . setStringData ( 문자열 ); 

또는 더 간결하게 :

classB . setStringData (  (( StringProvider )  AdapterFactory . 의 getInstance ()  . getAdapterFromTo ( 를 ClassA . 클래스 ,  StringProvider . 클래스 ,  "포맷 1" )  . 적응 ( 를 ClassA ))  . getStringData ()); 

(v) 장점은 데이터를 두 번째 형식으로 전송하려는 경우 다른 어댑터 / 공급자를 검색한다는 점에서 볼 수 있습니다.

어댑터  어댑터  =  AdapterFactory . getInstance ()  . getAdapterFromTo ( ClassA . class ,  StringProvider . class ,  "format2" ); 

(vi) 그리고 다음과 ClassA같은 이미지 데이터에서 데이터를 출력하려는 ​​경우 :Class C

어댑터  어댑터  =  AdapterFactory . getInstance ()  . getAdapterFromTo ( ClassA . class ,  ImageProvider . class ,  "format2" );  ImageProvider  공급자  =  ( ImageProvider )  어댑터 . 적응 ( classA );  classC . setImage ( provider . getImage ()); 

이런 식으로 (7 세), 어댑터 및 공급 업체의 사용에 의해 다수의 "보기"수 ClassBClassC로를 ClassA클래스 계층 구조를 변경하지 않고. 일반적으로 기존 개체 계층 구조로 개조 할 수있는 개체 간의 임의 데이터 흐름을위한 메커니즘을 허용합니다.

어댑터 패턴 구현

어댑터 패턴을 구현할 때 명확성 을 위해 공급자 구현에 클래스 이름 적용 할 수 있습니다 . 예 : . 매개 변수로 adaptee 클래스 변수가있는 생성자 메소드가 있어야합니다. 이 매개 변수는의 인스턴스 멤버로 전달됩니다 . clientMethod가 호출되면 어댑터의 필수 데이터에 액세스하고 원하는 출력을 생성하는 해당 데이터에 대한 작업을 수행 할 수 있도록 허용하는 어댑터 인스턴스에 액세스 할 수 있습니다.[ClassName]To[Interface]AdapterDAOToProviderAdapter[ClassName]To[Interface]Adapter

자바

interface  LightningPhone  {  void  recharge ();  무효  useLightning ();  }  interface  MicroUsbPhone  {  void  recharge ();  무효  useMicroUsb ();  }  class  Iphone   LightningPhone을  구현합니다. {  private  boolean  connector ;  @Override  public  void  useLightning ()  {  커넥터  =  true ;  시스템 . 아웃 . println ("Lightning connected"); } @Override public void recharge() { if (connector) { System.out.println("Recharge started"); System.out.println("Recharge finished"); } else { System.out.println("Connect Lightning first"); } } } class Android implements MicroUsbPhone { private boolean connector; @Override public void useMicroUsb() { connector = true; System.out.println("MicroUsb connected"); } @Override public void recharge() { if (connector) { System.out.println("Recharge started"); System.out.println("Recharge finished"); } else { System.out.println("Connect MicroUsb first"); } } } /* exposing the target interface while wrapping source object */ class LightningToMicroUsbAdapter implements MicroUsbPhone { private final LightningPhone lightningPhone; public LightningToMicroUsbAdapter(LightningPhone lightningPhone) { this.lightningPhone = lightningPhone; } @Override public void useMicroUsb() { System.out.println("MicroUsb connected"); lightningPhone.useLightning(); } @Override public void recharge() { lightningPhone.recharge(); } } public class AdapterDemo { static void rechargeMicroUsbPhone(MicroUsbPhone phone) { phone.useMicroUsb(); phone.recharge(); } static void rechargeLightningPhone(LightningPhone phone) { phone.useLightning(); phone.recharge(); } public static void main(String[] args) { Android android = new Android(); Iphone iPhone = new Iphone(); 시스템 . 아웃 . println ( "MicroUsb로 안드로이드 충전" );  rechargeMicroUsbPhone ( android );  시스템 . 아웃 . println ( "Lightning으로 iPhone 충전하기" );  rechargeLightningPhone ( iPhone );  시스템 . 아웃 . println ( "MicroUsb로 iPhone 충전" );  rechargeMicroUsbPhone ( 새로운  LightningToMicroUsbAdapter  ( iPhone ));  }  } 

산출

MicroUsb MicroUsb가 연결된 상태에서 Android 충전하기 충전 시작됨 충전 완료 Lightning Lightning이 연결된 상태에서 iPhone 충전하기 충전 시작됨 충전 완료 MicroUsb MicroUsb가 연결된 상태에서 iPhone 충전하기 Lightning 연결됨 충전 시작됨 충전 완료 

파이썬

"" "  어댑터 패턴 예제.  " ""  from  abc  import  ABCMeta ,  abstractmethod  NOT_IMPLEMENTED  =  "이것을 구현해야합니다."  RECHARGE  =  [ "충전이 시작되었습니다." ,  "충전 완료." ]  POWER_ADAPTERS  =  { "Android" :  "MicroUSB" ,  "iPhone" :  "Lightning" }  CONNECTED  =  " {} 연결됨."  CONNECT_FIRST  =  " 먼저 {}를 연결하십시오 .   __metaclass__ = ABCMeta @abstractmethod def recharge(self): raise NotImplementedError(NOT_IMPLEMENTED) class FormatIPhone(RechargeTemplate): @abstractmethod def use_lightning(self): raise NotImplementedError(NOT_IMPLEMENTED) class FormatAndroid(RechargeTemplate): @abstractmethod def use_micro_usb(self): raise NotImplementedError(NOT_IMPLEMENTED) class 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__])) class 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__])) class 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() class AndroidRecharger: def __init__(self): self.phone = Android() self.phone.use_micro_usb() self.phone.recharge() class IPhoneMicroUSBRecharger: def __init__(self): self.phone = IPhone() self.phone_adapter = IPhoneAdapter(self.phone) self.phone_adapter.use_micro_usb() self.phone_adapter.recharge() class IPhoneRecharger: def __init__(self): self.phone = IPhone() self.phone.use_lightning() self.phone.recharge() print("Recharging Android with MicroUSB recharger.") AndroidRecharger()  print ()  print ( "어댑터 패턴을 사용하여 MicroUSB로 iPhone 충전" )  IPhoneMicroUSBRecharger ()  print ()  print ( "iPhone 충전기로 iPhone 충전" )  IPhoneRecharger () 

또한보십시오

참고 문헌

  1. ^ a b Freeman, Eric; 프리먼, 엘리자베스; 시에라, 캐시 ; Bates, Bert (2004). 머리 우선 디자인 패턴 . 한빛 . 피. (244) ISBN 978-0-596-00712-6. OCLC 809772256 . 2013 년 5 월 4 일 에 원본 (페이퍼 백) 에서 보관되었습니다 . 만회 2013년 4월 30일을 .
  2. ^ 감마, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). 디자인 패턴 : 재사용 가능한 객체 지향 소프트웨어의 요소 . 애디슨 웨슬리 . 쪽. 139ff . ISBN 0-201-63361-2.
  3. ^ "어댑터 디자인 패턴-문제, 솔루션 및 적용 가능성" . w3sDesign.com . 2017 년 812 일에 확인 함 .
  4. Freeman, Eric; 프리먼, 엘리자베스; 시에라, 캐시; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (eds.). Head First Design Patterns (페이퍼 백) . 1 . 한빛 . 243, 252, 258, 260 쪽. ISBN 978-0-596-00712-6. 2012 년 7 월 2 일에 확인 함 .
  5. ^ "어댑터 디자인 패턴-구조 및 협업" . w3sDesign.com . 2017 년 812 일에 확인 함 .