ソフトウェア工学において、アダプタパターンは、既存のクラスのインターフェースを別のインターフェースとして使用できるようにするソフトウェア設計パターン(ラッパーとも呼ばれ、デコレータパターンと共有される別名)です。 [1]これは、ソースコードを変更せずに既存のクラスを他のクラスと連携させるためによく使用されます。
一例として、 XMLドキュメントのドキュメント オブジェクト モデルのインターフェイスを、表示可能なツリー構造に変換するアダプタがあります。
概要
アダプタ[2]設計パターンは、柔軟で再利用可能なオブジェクト指向ソフトウェア、つまり実装、変更、テスト、再利用が容易なオブジェクトを設計するために、繰り返し発生する設計上の問題を解決する方法を記述した、よく知られている23のGang of Four設計パターンの1つです。
アダプタ設計パターンは次のような問題を解決します: [3]
- クライアントが必要とするインターフェースを持たないクラスを再利用するにはどうすればよいでしょうか?
- 互換性のないインターフェースを持つクラスはどのように連携できますか?
- クラスに代替インターフェースを提供するにはどうすればよいでしょうか?
多くの場合、(既存の)クラスは、そのインターフェースがクライアントが要求するインターフェースに準拠していないという理由だけで再利用できません。
アダプタ設計パターンは、このような問題を解決する方法を説明します。
adapterクラス ( ) の (互換性のない) インターフェースを、クライアントが必要とするadaptee別のインターフェース ( ) に変換する別のクラスを定義しますtarget。adapter必要なインターフェースを持たないクラスを操作 (再利用) するには、を実行します。
このパターンの重要な考え方は、adapter(既存の)クラスのインターフェースを変更せずに適応させる個別の処理を実行することです。
targetクライアントは、クラスを直接操作しているのか、それともインターフェースadapterを持たないクラスを介して操作しているのかを知りませんtarget。
下記の UML クラス図も参照してください。
意味
アダプタを使用すると、互換性のない 2 つのインターフェイスを連携させることができます。これがアダプタの実際の定義です。インターフェイスは互換性がない可能性がありますが、内部の機能はニーズに適合している必要があります。アダプタ設計パターンでは、1 つのクラスのインターフェイスをクライアントが期待するインターフェイスに変換することで、互換性のないクラスを連携させることができます。
使用法
アダプタは、ラッパーが特定のインターフェースを尊重し、ポリモーフィックな動作をサポートする必要がある場合に使用できます。また、デコレータを使用すると、実行時にインターフェースの動作を追加または変更することができ、ファサードは、基礎となるオブジェクトへのより簡単なインターフェースが必要な場合に使用されます。[4]
構造
UML クラス図

上記のUML クラス図では、インターフェイスをclient必要とするクラスは、そのインターフェイスがインターフェイスに準拠していないため、クラスを直接target再利用できません。代わりに、は、次の観点からインターフェイスを実装するクラスを通じて機能します。
adapteetargetclientadaptertargetadaptee
- 方法は、実行時にオブジェクトに委任することによってインターフェース
object adapterを実装します( )。targetadapteeadaptee.specificOperation() - 方法は、コンパイル時にクラスから継承することによってインターフェース
class adapterを実装します( )。targetadapteespecificOperation()
オブジェクトアダプタパターン
このアダプタ パターンでは、アダプタにはラップするクラスのインスタンスが含まれます。 この場合、アダプタはラップされたオブジェクトのインスタンスを呼び出します。

_pattern_in_LePUS3.png/500px-Adapter(Object)_pattern_in_LePUS3.png)
クラスアダプタパターン
このアダプタパターンは、期待されるインターフェースと既存のインターフェースの両方を実装または継承する複数のポリモーフィックインターフェースを使用します。特にクラスの多重継承をサポートしていないJava (JDK 1.8以前)などの言語では、期待されるインターフェースが純粋なインターフェースクラスとして作成されるのが一般的です。 [1]

_pattern_in_LePUS3.png/500px-Adapter(Class)_pattern_in_LePUS3.png)
ランタイムアダプタパターンのさらなる形態
コンパイル時のソリューションからの動機
classA何らかのデータを供給することが望まれます。classB何らかのデータがあると仮定しますString。コンパイル時のソリューションは次のとおりです。
クラスB 。setStringData ( classA . getStringData ());
しかし、文字列データの形式を変更する必要がある場合、コンパイル時の解決策は継承を使用することです。
パブリッククラスFormat1ClassA はClassAを拡張します{ @OverrideパブリックString getStringData () { return format ( toString ()); } }
そして、おそらくファクトリパターンを使用して、実行時に正しく「フォーマットされた」オブジェクトを作成します。
ランタイムアダプタソリューション
「アダプタ」を使用したソリューションは次のように進行します。
- 中間の「プロバイダー」インターフェースを定義し、この例ではデータのソースをラップし
ClassA、適切な形式でデータを出力するプロバイダー インターフェースの実装を記述します。パブリックインターフェイスStringProvider {パブリックString getStringData (); } パブリッククラスClassAFormat1はStringProviderを実装します{プライベートクラスAクラスA = null ; public ClassAFormat1 ( final ClassA a ) { classA = a ; } パブリック文字列getStringData () { return format ( classA . getStringData ()); } private String format ( final String sourceValue ) { // ソース文字列を、ソースオブジェクトのデータを必要とするオブジェクトに必要な形式に操作します。 return sourceValue . trim (); } }
- プロバイダーの特定の実装を返すアダプター クラスを記述します。
パブリッククラスClassAFormat1Adapter はAdapterを拡張します{ public Object adapt ( 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 )アダプター.adapt ( classA ) ;文字列文字列=プロバイダー.getStringData ( ) ; classB.setStringData ( string ) ;
あるいはもっと簡潔に言うと:
classB.setStringData ( ( ( StringProvider ) AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format1 " ) . adapt ( classA ) ) . getStringData ( ) ) ;
- 2 番目の形式でデータを転送する必要がある場合は、別のアダプタ/プロバイダーを検索すると、次のような利点があります。
アダプターアダプター= AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , StringProvider.class , " format2 " ) ;
ClassAそして、からのデータを、例えば画像データとして出力したい場合:Class Cアダプターアダプター= AdapterFactory.getInstance ( ) . getAdapterFromTo ( ClassA.class , ImageProvider.class , " format2 " ) ; ImageProviderプロバイダー= ( ImageProvider )アダプター.adapt ( classA ) ; classC.setImage (プロバイダー.getImage ( ) ) ;
- このように、アダプタとプロバイダを使用すると、クラス階層を変更することなく、複数の「ビュー」が可能になります
ClassB。ClassC一般ClassA的に、既存のオブジェクト階層に後付けできるオブジェクト間の任意のデータ フローのメカニズムが可能になります。
アダプタパターンの実装
アダプタ パターンを実装する場合、わかりやすくするために、クラス名をプロバイダ実装に適用できます。たとえば、 です。アダプタ クラス変数をパラメータとして持つコンストラクタ メソッドが必要です。このパラメータは のインスタンス メンバーに渡されます。 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 ;
LightningToMicroUsbAdapterは、ILightningPhoneとLightningPhoneの両方をサポートします。
@Override
public void useMicroUsb () { System.out.println ( " MicroUsbが接続されました" ); lightningPhone.useLightning ( ) ; }
@Override
public void recharge () { lightningPhone . recharge (); } }
パブリッククラスAdapterDemo { static void rechargeMicroUsbPhone ( IMicroUsbPhone phone ) { phone . useMicroUsb (); phone . recharge (); }
static void rechargeLightningPhone ( ILightningPhone電話) {電話.useLightning ( );電話.recharge () ; }
パブリック静的void main ( String [] args ) { Android android = new Android (); Iphone iPhone = new Iphone ();
System . out . println ( "MicroUsb で Android を充電" );
rechargeMicroUsbPhone ( android );
System . out . println ( "Lightning で iPhone を充電する" );
rechargeLightningPhone ( iPhone );
System . out . println ( "MicroUsb で iPhone を充電する" );
rechargeMicroUsbPhone ( new LightningToMicroUsbAdapter ( iPhone ) ); } }
出力
MicroUSBでAndroidを充電する MicroUSB接続 充電を開始しました 充電完了 LightningでiPhoneを充電する Lightning 接続 充電を開始しました 充電完了 MicroUSBでiPhoneを充電する MicroUSB接続 Lightning 接続 充電を開始しました 充電完了
パイソン
"""
アダプタ パターンの例。
"""
from abc import ABCMeta , abstractmethod
NOT_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 ( )
print ( "MicroUSB 充電器で Android を充電しています。" )
AndroidRecharger ()
print ()
print ( "アダプタパターンを使用して MicroUSB で iPhone を充電します。" )
IPhoneMicroUSBRecharger ()
print ()
print ( "iPhone 充電器で iPhone を充電しています。" )
IPhoneRecharger ()
C#
パブリックインターフェイスILightningPhone { void ConnectLightning (); void Recharge (); }
パブリックインターフェイスIUsbPhone { void ConnectUsb (); void Recharge (); }
public shielded 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 shielded 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 ケーブルを接続してください。" ); } } }
パブリックシールクラスLightningToUsbAdapter : IUsbPhone {プライベート読み取り専用ILightningPhone lightningPhone ;プライベートbool isConnected ;パブリックLightningToUsbAdapter ( ILightningPhone lightningPhone ) { this.lightningPhone = lightningPhone ; }パブリックvoid ConnectUsb ( ) { this.lightningPhone.ConnectLightning ( ) ; }
パブリックvoid Recharge () { this . lightningPhone . Recharge (); } }
public void Main () { ILightningPhone applePhone = new ApplePhone ( ); IUsbPhone adaptorCable = new LightningToUsbAdapter ( applePhone ) ; adaptorCable.ConnectUsb ( ); adaptorCable.Recharge () ; }
出力:
Apple の電話が接続されています。
アダプタ ケーブルが接続されています。Apple
の電話が充電中です。
参照
- アダプタ Java デザイン パターン - アダプタ
- 委任は、オブジェクト アダプタ パターンに強く関連しています。
- 依存性逆転の原則。これは、高レベルクラスが低レベルモジュール(アダプタクラスによって実装される)への独自の(アダプタ)インターフェースを定義する場合に、アダプタパターンを適用するものと考えることができます。
- ポートとアダプタのアーキテクチャ
- シム
- ラッパー関数
- ラッパーライブラリ
参考文献
- ^ ab フリーマン、エリック; フリーマン、エリザベス;シエラ、キャシー; ベイツ、バート (2004)。Head First デザインパターン。オライリーメディア。p. 244。ISBN 978-0-596-00712-6. OCLC 809772256. 2013年5月4日に オリジナル(ペーパーバック)からアーカイブ。2013年4月30日閲覧。
- ^ ガンマ、エリック; ヘルム、リチャード; ジョンソン、ラルフ; ブリサイドス、ジョン (1994)。デザインパターン: 再利用可能なオブジェクト指向ソフトウェアの要素。アディソン・ウェズリー。pp. 139ff。ISBN 0-201-63361-2。
- ^ 「アダプタ設計パターン - 問題、解決策、適用性」。w3sDesign.com。2017年8月28日時点のオリジナルよりアーカイブ。2017年8月12日閲覧。
- ^ フリーマン、エリック; フリーマン、エリザベス; シエラ、キャシー; ベイツ、バート (2004)。 ヘンドリクソン、マイク; ルーキデス、マイク (編)。 Head First Design Patterns (ペーパーバック)。 第 1 巻。O'Reilly Media。 pp. 243、252、258、260。ISBN 978-0-596-00712-6. 2012年7月2日閲覧。
- ^ 「アダプタ設計パターン - 構造とコラボレーション」w3sDesign.com。 2017 年 8 月 28 日時点のオリジナルよりアーカイブ。2017 年 8 月 12 日閲覧。
