Adapter (アダプタ) パターン

クラス間の求めるインターフェースが異なる時に、その差異を効率的に双方のインターフェースに適合させるデザインパターンです。
「前に作ったクラスを再利用したいけど、そのままだと今回の案件には使えないんだよなぁ」とか
「他のチームが作ったクラスがあるけど、そのままではこの画面から使うのに使いにくい。だけど
 他のチームが作ったクラスなので、自分は直接触っちゃいけないって言われているんだよなぁ」
といった時に、「おっ!こうすればいいじゃん」と思うのがこのパターンです。


サンプルでは、再利用したいクラスを SomethingCheck クラスとし、
Adapter(適合者)は、ICheck インターフェースを実装している、SomethingCheckWrapper クラス または、SomethingCheckDelegation クラスとしています。
再利用したいクラスは、窓口が Integer 型なのに対し、今回は画面のテキストボックスに入力された値( String 型)
を渡したいので、ICheck インターフェースを通して SomethingCheck クラスのメソッドを呼び出しています。

このカテゴリーの先頭へ このページの先頭へ

■クラス図

ここで用いるサンプルのクラス図です。
準備中...

このカテゴリーの先頭へ このページの先頭へ

■サンプルの説明

サンプルの見た目
チェックボタンをクリックすると、テキストボックスに入力された文字が、
数値の 7 より大きい値かどうかのチェックを行います。
サンプルのプロジェクトダウンロード

このカテゴリーの先頭へ このページの先頭へ

■コード

アプリケーションのエントリポイント StartUpForm です。
Adapter パターンには、委譲によるもの(サンプルでは SomethingCheckDelegation )と
継承によるもの(サンプルでは SomethingCheckWrapper )の 2 種類存在しますが、
ここでは、継承によるものを用いて実装しています。
どちらを用いても、結果は同じです。

StartUpForm.vb
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 クラスです。

SomethingCheck.vb
'一皮被せられるクラス
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(適合者)のインターフェースです。

ICheck.vb
'Adapterのインターフェース
Public Interface ICheck

  '7以上かどうかチェックするメソッド定義
  Function IsOverSeven() As Boolean

End Interface
このカテゴリーの先頭へ このページの先頭へ

継承によるアダプタのクラスです。ICheck インターフェースを実装しています。

SomethingCheckWrapper.vb
'継承によるアダプタ
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 インターフェースを実装しています。
サンプルの中では使用されていません。

SomethingCheckDelegation.vb
'委譲によるアダプタ
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 から作らなくても、済んでしまう事が解っていただけたでしょうか。
サンプルの例では、再利用したいクラスのメソッドが簡単なメソッドであった為、利点が解りにくいかもしれませんが、
複雑なロジックであった場合、このパターンの威力が発揮されると思います。
再利用できる部分は再利用して、楽しちゃいましょう。

このカテゴリーの先頭へ このページの先頭へ