Adapter (アダプタ) パターン
クラス間の求めるインターフェースが異なる時に、その差異を効率的に双方のインターフェースに適合させるデザインパターンです。
「前に作ったクラスを再利用したいけど、そのままだと今回の案件には使えないんだよなぁ」とか
「他のチームが作ったクラスがあるけど、そのままではこの画面から使うのに使いにくい。だけど
他のチームが作ったクラスなので、自分は直接触っちゃいけないって言われているんだよなぁ」
といった時に、「おっ!こうすればいいじゃん」と思うのがこのパターンです。
サンプルでは、再利用したいクラスを SomethingCheck クラスとし、
Adapter(適合者)は、ICheck インターフェースを実装している、SomethingCheckWrapper クラス または、SomethingCheckDelegation クラスとしています。
再利用したいクラスは、窓口が Integer 型なのに対し、今回は画面のテキストボックスに入力された値( String 型)
を渡したいので、ICheck インターフェースを通して SomethingCheck クラスのメソッドを呼び出しています。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

チェックボタンをクリックすると、テキストボックスに入力された文字が、
数値の 7 より大きい値かどうかのチェックを行います。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント StartUpForm です。
Adapter パターンには、委譲によるもの(サンプルでは SomethingCheckDelegation )と
継承によるもの(サンプルでは SomethingCheckWrapper )の 2 種類存在しますが、
ここでは、継承によるものを用いて実装しています。
どちらを用いても、結果は同じです。
Public Class StartUpForm Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler End Sub ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使って変更してください。 ' コード エディタを使って変更しないでください。 Friend WithEvents TextBox1 As System.Windows.Forms.TextBox Friend WithEvents Button1 As System.Windows.Forms.Button <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.TextBox1 = New System.Windows.Forms.TextBox Me.Button1 = New System.Windows.Forms.Button Me.SuspendLayout() ' 'TextBox1 ' Me.TextBox1.Location = New System.Drawing.Point(48, 96) Me.TextBox1.Name = "TextBox1" Me.TextBox1.TabIndex = 0 Me.TextBox1.Text = "TextBox1" ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(168, 96) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 1 Me.Button1.Text = "Button1" ' 'Form1 ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.TextBox1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.ResumeLayout(False) End Sub #End Region '画面がロードされた時のイベント Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Adapter" Me.TextBox1.Text = "" Me.Button1.Text = "チェック" End Sub 'チェックボタンをクリックした時のイベント Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click If Me.TextBox1.Text = "" Then MessageBox.Show("入力されていません", _ Me.Text, _ MessageBoxButtons.OK, _ MessageBoxIcon.Exclamation) Me.TextBox1.Focus() Exit Sub End If 'ICheckインターフェースを実装しているクラスのインスタンスを生成する Dim myCheckObject As ICheck myCheckObject = New SomethingCheckWrapper(Me.TextBox1.Text) 'チェック実行 If myCheckObject.IsOverSeven() Then MessageBox.Show("7以上です", _ Me.Text, _ MessageBoxButtons.OK, _ MessageBoxIcon.Information) Else MessageBox.Show("7より小さいです", _ Me.Text, _ MessageBoxButtons.OK, _ MessageBoxIcon.Exclamation) End If End Sub End Class
再利用したいクラスの SomethingCheck クラスです。
'一皮被せられるクラス Public Class SomethingCheck 'チェックする数字 Protected m_checkNumber As Integer 'デフォルトコンストラクタ Public Sub New() End Sub 'コンストラクタ Public Sub New(ByVal checkInteger As Integer) Me.m_checkNumber = checkInteger End Sub 'チェック実行 Public Overridable Function IsOverSeven() As Boolean '7以上かどうかのチェック If Me.m_checkNumber >= 7 Then Return True Else Return False End If End Function End Class
Adapter(適合者)のインターフェースです。
'Adapterのインターフェース Public Interface ICheck '7以上かどうかチェックするメソッド定義 Function IsOverSeven() As Boolean End Interface
継承によるアダプタのクラスです。ICheck インターフェースを実装しています。
'継承によるアダプタ Public Class SomethingCheckWrapper Inherits SomethingCheck Implements ICheck 'チェックする数字 Private m_checkString As String 'コンストラクタ Public Sub New(ByVal checkString As String) Me.m_checkString = checkString End Sub '7以上かどうかチェックするメソッド実装 Public Overrides Function IsOverSeven() As Boolean Implements ICheck.IsOverSeven If Not System.Text.RegularExpressions.Regex.Match(Me.m_checkString, "^-?[0-9]+$").Success Then '数字でないときは例外をThrow Throw New CommonExceptionAndHandler.MyException("入力文字が不正です") End If '親クラスのIsOverSevenメソッドを呼ぶ MyBase.m_checkNumber = Integer.Parse(Me.m_checkString) Return MyBase.IsOverSeven() End Function End Class
委譲によるアダプタのクラスです。ICheck インターフェースを実装しています。
サンプルの中では使用されていません。
'委譲によるアダプタ Public Class SomethingCheckDelegation Implements ICheck 'チェックする文字列 Private m_checkString As String 'コンストラクタ Public Sub New(ByVal checkString As String) Me.m_checkString = checkString End Sub '7以上かどうかチェックするメソッド実装 Public Function IsOverSeven() As Boolean Implements ICheck.IsOverSeven If Not System.Text.RegularExpressions.Regex.Match(Me.m_checkString, "^-?[0-9]+$").Success Then '数字でないときは例外をThrow Throw New CommonExceptionAndHandler.MyException("入力文字が不正です") End If 'SomethingCheckのインスタンスを生成し、SomethingCheckのIsOverSevenを実行する Dim myCheckObject As SomethingCheck = New SomethingCheck(Integer.Parse(Me.m_checkString)) Return myCheckObject.IsOverSeven() End Function End Class
■ひとこと
このパターンに関しても、デザインパターンの入り口です。
SomethingCheckWrapper クラスと SomethingCheckDelegation クラスの IsOverSeven メソッドの中身を見たら、
継承と委譲の違いがよく解っていただけるかしら...と思います。
また、そのままでは使えないものでも、Adapter パターンを適用する事により、
1 から作らなくても、済んでしまう事が解っていただけたでしょうか。
サンプルの例では、再利用したいクラスのメソッドが簡単なメソッドであった為、利点が解りにくいかもしれませんが、
複雑なロジックであった場合、このパターンの威力が発揮されると思います。
再利用できる部分は再利用して、楽しちゃいましょう。