連載
.NETで始めるデザインパターン

第3回 リファクタリングにより導き出すStrategyパターン

太陽システム株式会社 中西 庸文
2005/03/19

Imports System
Imports System.Collections
Imports NUnit.Framework
Imports DesignPatterns.Core.TemplateMethod

Namespace DesignPatterns.Tests.TemplateMethod
  ' XpValuesクラスのテスト・クラス
  <TestFixture()> Public Class XpValuesTest
    ' XpValuesクラスのインスタンスを格納するフィールド変数
    Private _target As XpValues

    ' テストの初期化
    <SetUp()> Public Sub Init()
      ' XpValuesオブジェクトの生成(プレーン・テキスト形式で出力できる)
      _target = XpValues.CreatePlainTextFormatableXpValues("XP 2nd Edition")
    End Sub

    ' XpValuesオブジェクトを生成するテスト
    <Test()> Public Sub CreateInstance()
      Assert.AreEqual("XP 2nd Edition", _target.Name, "名前が違います。")
      Assert.AreEqual(0, _target.Count, "価値の数が違います。")
    End Sub

    ' XPの「5つの価値」の内容を登録するテスト
    <Test()> Public Sub Add()
      _target.Add("コミュニケーション")
      _target.Add("シンプル")
      _target.Add("フィードバック")
      _target.Add("勇気")
      _target.Add("敬意")

      Assert.AreEqual(5, _target.Count, "価値の数が違います。")
      Assert.AreEqual("コミュニケーション", _target(0), "価値が違います。")
      Assert.AreEqual("シンプル", _target(1), "価値が違います。")
      Assert.AreEqual("フィードバック", _target(2), "価値が違います。")
      Assert.AreEqual("勇気", _target(3), "価値が違います。")
      Assert.AreEqual("敬意", _target(4), "価値が違います。")
    End Sub

    ' 「5つの価値」の登録内容を走査するテスト
    <Test()> Public Sub GetEnumerator()
      _target.Add("コミュニケーション")
      _target.Add("シンプル")

      Dim en As IEnumerator = _target.GetEnumerator()

      Assert.IsTrue(en.MoveNext(), "次の要素は存在するはずです。")
      Assert.AreEqual("コミュニケーション", en.Current, "要素が違います。")
      Assert.IsTrue(en.MoveNext(), "次の要素は存在するはずです。")
      Assert.AreEqual("シンプル", en.Current, "要素が違います。")
      Assert.IsFalse(en.MoveNext(), "次の要素は存在しないはずです。")
    End Sub

    ' プレーン・テキスト形式で「5つの価値」を出力するテスト
    <Test()> Public Sub PlainTextFormat()
      _target.Add("コミュニケーション")
      _target.Add("シンプル")
      _target.Add("フィードバック")
      _target.Add("勇気")
      _target.Add("敬意")

      Dim expected As String = _
        "XP 2nd Editionの5つの価値" & vbCrLf & _
        "・コミュニケーション" & vbCrLf & _
        "・シンプル" & vbCrLf & _
        "・フィードバック" & vbCrLf & _
        "・勇気" & vbCrLf & _
        "・敬意" & vbCrLf

      Assert.AreEqual(expected, _target.Format(), "結果が違います。")
    End Sub

    ' HTML形式で「5つの価値」を出力するテスト
    <Test()> Public Sub HtmlTextFormat()
      ' XpValuesオブジェクトの生成(HTML形式で出力できる)
      _target = XpValues.CreateHtmlTextFormatableXpValues("XP 2nd Edition")

      _target.Add("コミュニケーション")
      _target.Add("シンプル")
      _target.Add("フィードバック")
      _target.Add("勇気")
      _target.Add("敬意")

      Dim expected As String = _
        "<p>XP 2nd Editionの5つの価値</p>" & vbCrLf & _
        "<ul>" & vbCrLf & _
        "<li>コミュニケーション" & vbCrLf & _
        "<li>シンプル" & vbCrLf & _
        "<li>フィードバック" & vbCrLf & _
        "<li>勇気" & vbCrLf & _
        "<li>敬意" & vbCrLf & _
        "</ul>" & vbCrLf

      Assert.AreEqual(expected, _target.Format(), "結果が違います。")
    End Sub

  End Class

End Namespace
XpValuesクラスのテスト・コード(VB.NET)
 
Imports System
Imports System.Collections
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  ' XpValuesクラス
  Public Class XpValues : Implements IEnumerable

    Private _name As String
    ' 出力形式のタイプを保持するフィールド変数
    Private _formatType As Integer
    Private _values As ArrayList = New ArrayList

    ' プライベート・コンストラクタ
    Private Sub New(ByVal name As String, ByVal formatType As Integer)
      Me._name = name
      Me._formatType = formatType
    End Sub

    ' 保持する「価値」をプレーン・テキスト形式で出力できるインスタンスを生成
    Public Shared Function CreatePlainTextFormatableXpValues(ByVal name As String) As XpValues
      Return New XpValues(name, 0)
    End Function

    ' 保持する「価値」をHTML形式で出力できるインスタンスを生成
    Public Shared Function CreateHtmlTextFormatableXpValues(ByVal name As String) As XpValues
      Return New XpValues(name, 1)
    End Function

    ' 名前を取得する

    Public ReadOnly Property Name() As String
      Get
        Return _name
      End Get
    End Property

    ' 「価値」の数を取得する
    Public ReadOnly Property Count() As Integer
      Get
        Return _values.Count
      End Get
    End Property

    ' 「価値」を取得する
    Default Public ReadOnly Property Item(ByVal index As Integer) As String
      Get
        Return _values(index).ToString()
      End Get
    End Property

    ' 「価値」を登録する
    Public Sub Add(ByVal xpValue As String)
      _values.Add(xpValue)
    End Sub

    ' XpValues を反復処理できる列挙子を取得する
    Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
      Return _values.GetEnumerator()
    End Function

    ' 保持する「価値」を出力する

    Public Function Format() As String
      If (_formatType = 0) Then  ' プレーン・テキスト形式
        Dim builder As StringBuilder = New StringBuilder
        builder.Append(GetTitle() & vbCrLf)
        For Each xpValue As String In _values
          builder.Append("・" & xpValue & vbCrLf)
        Next
        Return builder.ToString()
      Else  ' HTML形式
        Dim builder As StringBuilder = New StringBuilder
        builder.Append("<p>" & GetTitle() & "</p>" & vbCrLf)
        builder.Append("<ul>" & vbCrLf)
        For Each xpValue As String In _values
          builder.Append("<li>" & xpValue & vbCrLf)
        Next
        builder.Append("</ul>" & vbCrLf)
        Return builder.ToString()
      End If
    End Function

    ' タイトルを取得する
    Private Function GetTitle() As String
      Return _name & "の" & _values.Count.ToString() & "つの価値"
    End Function

  End Class

End Namespace
リファクタリング前のXpValuesクラスの実装コード(VB.NET)
 
Imports System

Namespace DesignPatterns.Core.TemplateMethod

  Public Class TextFormatter

  End Class

End Namespace
新しく作成したTextFormatterクラス(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class TextFormatter

    ' XpValuesクラスからコピー
    Public Function Format() As String
      If (_formatType = 0) Then
        Dim builder As StringBuilder = New StringBuilder
        builder.Append(GetTitle() & vbCrLf)
        For Each xpValue As String In _values
          builder.Append("・" & xpValue & vbCrLf)
        Next
        Return builder.ToString()
      Else
        Dim builder As StringBuilder = New StringBuilder
        builder.Append("<p>" & GetTitle() & "</p>" & vbCrLf)
        builder.Append("<ul>" & vbCrLf)
        For Each xpValue As String In _values
          builder.Append("<li>" & xpValue & vbCrLf)
        Next
        builder.Append("</ul>" & vbCrLf)

        Return builder.ToString()
      End If
    End Function

    ' XpValuesクラスから移動

    Private Function GetTitle() As String
      Return _name & "の" & _values.Count.ToString() & "つの価値"
    End Function

  End Class

End Namespace
XpValuesクラスから移動されたTextFormatterクラスのFormatメソッドとGetTitleメソッド(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class TextFormatter

    ' 移動元のオブジェクト参照をパラメータにする

    Public Function Format(ByVal myXpValues As XpValues) As String
      If (myXpValues.FormatType = 0) Then
        Dim builder As StringBuilder = New StringBuilder
        builder.Append(GetTitleFor(myXpValues) & vbCrLf)
        For Each xpValue As String In myXpValues
          builder.Append("・" & xpValue & vbCrLf)
        Next
        Return builder.ToString()
      Else
        Dim builder As StringBuilder = New StringBuilder
        builder.Append("<p>" & GetTitleFor(myXpValues) & "</p>" & vbCrLf)
        builder.Append("<ul>" & vbCrLf)
        For Each xpValue As String In myXpValues
          builder.Append("<li>" & xpValue & vbCrLf)
        Next
        builder.Append("</ul>" & vbCrLf)
        Return builder.ToString()
      End If
    End Function

    ' メソッド名の変更

    Private Function GetTitleFor(ByVal myXpValues As XpValues) As String
      Return myXpValues.Name & "の" & myXpValues.Count.ToString() & "つの価値"
    End Function

  End Class

End Namespace
XpValuesクラスのフィールドの参照がプロパティの参照へ置き換えられたTextFormatterクラスのFormatメソッドとGetTitleメソッド(VB.NET)
 
Imports System
Imports System.Collections
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class XpValues : Implements IEnumerable
    ……中略……

    ' プロパティを追加
    Public ReadOnly Property FormatType() As Integer
      Get
        Return _formatType
      End Get
    End Property
    ……中略……

    Public Function Format() As String
      ' 生成したTextFormatterオブジェクトの
      ' Formatメソッドを呼び出す
      Return New TextFormatter().Format(Me)
    End Function

  End Class

End Namespace
XpValuesクラスにおける移動元Formatメソッドの変更とFormatTypeプロパティの追加(VB.NET)
 
Imports System
Imports System.Collections

Namespace DesignPatterns.Core.TemplateMethod

  Public Class XpValues : Implements IEnumerable
    ……中略……

    ' フィールドを追加
    Private _formatter As TextFormatter

    Private Sub New(ByVal name As String, ByVal formatType As Integer)
      Me._name = name
      Me._formatType = formatType
      ' インスタンス生成
      Me._formatter = New TextFormatter
    End Sub
    ……中略……

    Public Function Format() As String
      ' インスタンス生成をやめ、フィールド参照へ変更
      Return _formatter.Format(Me)
    End Function

  End Class

End Namespace
XpValuesクラスにおけるTextFormatterオブジェクトのフィールド化(VB.NET)
 
Imports System
Imports System.Collections

Namespace DesignPatterns.Core.TemplateMethod

  Public Class XpValues : Implements IEnumerable
    ……中略……

    ' パラメータの抽出が行われたプライベート・コンストラクタ
    Private Sub New(ByVal name As String, ByVal formatType As Integer, ByVal formatter As TextFormatter)
      Me._name = name
      Me._formatType = formatType
      ' インスタンス生成をパラメータに置き換え
      Me._formatter = formatter
    End Sub

    Public Shared Function CreatePlainTextFormatableXpValues(ByVal name As String) As XpValues
      ' 生成したインスタンスをパラメータとして引き渡す
      Return New XpValues(name, 0, New TextFormatter)
    End Function

    Public Shared Function CreateHtmlTextFormatableXpValues(ByVal name As String) As XpValues
      ' 生成したインスタンスをパラメータとして引き渡す
      Return New XpValues(name, 1, New TextFormatter)
    End Function
    ……中略……
  End Class

End Namespace
プライベート・コンストラクタにTextFormatterオブジェクトのパラメータが追加されたXpValuesクラス(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class TextFormatter

    ' 仮想メソッドにする
    Public Overridable Function Format(ByVal myXpValues As XpValues) As String      If (myXpValues.FormatType = 0) Then
        Dim builder As StringBuilder = New StringBuilder
        builder.Append(GetTitleFor(myXpValues) & vbCrLf)
        For Each xpValue As String In myXpValues
          builder.Append("・" & xpValue & vbCrLf)
        Next
        Return builder.ToString()
      Else
        Dim builder As StringBuilder = New StringBuilder
        builder.Append("<p>" & GetTitleFor(myXpValues) & "</p>" & vbCrLf)
        builder.Append("<ul>" & vbCrLf)
        For Each xpValue As String In myXpValues
          builder.Append("<li>" & xpValue & vbCrLf)
        Next
        builder.Append("</ul>" & vbCrLf)
        Return builder.ToString()
      End If
    End Function
    ……中略……
  End Class

End Namespace
サブクラスでオーバーライドできるようにしたTextFormatterクラスのFormatメソッド(VB.NET)
 
Imports System

Namespace DesignPatterns.Core.TemplateMethod

  ' プレーン・テキスト形式で「価値」の出力を行うStrategy
  Public Class PlainTextFormatter : Inherits TextFormatter

    Public Overrides Function Format(ByVal myXpValues As XpValues) As String
      Return Nothing
    End Function

  End Class

End Namespace
新しく作成したPlainTextFormatterクラス(VB.NET)
 
Imports System
Imports System.Collections
'Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class XpValues : Implements IEnumerable
    ……中略……

    Public Shared Function CreatePlainTextFormatableXpValues(ByVal name As String) As XpValues
      ' プレーン・テキスト形式で「価値」の出力を行うStrategyの生成
      Return New XpValues(name, 0, New PlainTextFormatter)
    End Function
    ……中略……
  End Class

End Namespace
PlainTextFormatterオブジェクトを生成するようになったXpValuesクラスのCreatePlainTextFormatabletXpValuesメソッド(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class PlainTextFormatter : Inherits TextFormatter

    Public Overrides Function Format(ByVal myXpValues As XpValues) As String
      Dim builder As StringBuilder = New StringBuilder
      builder.Append(GetTitleFor(myXpValues) & vbCrLf)
      For Each xpValue As String In myXpValues
        builder.Append("・" & xpValue & vbCrLf)
      Next
      Return builder.ToString()
    End Function

  End Class

End Namespace
スーパークラスのロジックが移動されたPlainTextFormatterクラスのFormatメソッド(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class TextFormatter

    Public Overridable Function Format(ByVal myXpValues As XpValues) As String
      If (myXpValues.FormatType = 0) Then
        ' 例外をスロー
        Throw New Exception
      Else
        Dim builder As StringBuilder = New StringBuilder
        builder.Append("<p>" & GetTitleFor(myXpValues) & "</p>" & vbCrLf)
        builder.Append("<ul>" & vbCrLf)
        For Each xpValue As String In myXpValues
          builder.Append("<li>" & xpValue & vbCrLf)
        Next
        builder.Append("</ul>" & vbCrLf)
        Return builder.ToString()
      End If
    End Function

    ' アクセス修飾子を変更
    Protected Function GetTitleFor(ByVal myXpValues As XpValues) As String
      Return myXpValues.Name & "の" & myXpValues.Count.ToString() & "つの価値"
    End Function

  End Class

End Namespace
サブクラスへプレーン・テキスト出力のためのロジックが移動された後のTextFormatterクラス(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  ' HTML形式で「価値」の出力を行うStrategy
  Public Class HtmlTextFormatter : Inherits TextFormatter

    Public Overrides Function Format(ByVal myXpValues As XpValues) As String
      Dim builder As StringBuilder = New StringBuilder
      builder.Append("<p>" & GetTitleFor(myXpValues) & "</p>" & vbCrLf)
      builder.Append("<ul>" & vbCrLf)
      For Each xpValue As String In myXpValues
        builder.Append(EachValueText(xpValue))
      Next
      builder.Append("</ul>" & vbCrLf)
      Return builder.ToString()
    End Function

  End Class

End Namespace
新しく作成したHtmlTextFormatterクラス(VB.NET)
 
Imports System
Imports System.Collections

Namespace DesignPatterns.Core.TemplateMethod

  Public Class XpValues : Implements IEnumerable
    ……中略……

    Public Shared Function CreateHtmlTextFormatableXpValues(ByVal name As String) As XpValues
      ' HTML形式で「価値」の出力を行うStrategyの生成
      Return New XpValues(name, 1, New HtmlTextFormatter)
    End Function
    ……中略……
  End Class

End Namespace
HtmlTextFormatterオブジェクトを生成するようになったXpValuesクラスのCreateHtmlTextFormatableXpValuesメソッド(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  Public Class TextFormatter

    Public Overridable Function Format(ByVal myXpValues As XpValues) As String
      If (myXpValues.FormatType = 0) Then
        Throw New Exception
      Else
        ' 例外をスロー
        Throw New Exception
      End If
    End Function
    ……中略……
  End Class

End Namespace
サブクラスへHTMLテキスト出力のロジックが移動された後のTextFormatterクラス(VB.NET)
 
Imports System
Imports System.Text

Namespace DesignPatterns.Core.TemplateMethod

  ' 抽象クラスにする
  Public MustInherit Class TextFormatter

    ' 抽象メソッドにする
    Public MustOverride Function Format(ByVal myXpValues As XpValues) As String
    ……中略……
  End Class

End Namespace
抽象クラスに変更したTextFormatter クラスと抽象メソッドに変更したFormatメソッド(VB.NET)
 
Imports System
Imports System.Collections

Namespace DesignPatterns.Core.TemplateMethod

  Public Class XpValues : Implements IEnumerable

    Private _name As String
    ' 出力形式のタイプを保持するフィールド変数の削除
    Private _formatType As Integer
    Private _values As ArrayList = New ArrayList
    Private _formatter As TextFormatter

    ' プライベート・コンストラクタのパラメータ削除
    Private Sub New(ByVal name As String, ByVal formatType As Integer, ByVal formatter As TextFormatter)
      Me._name = name
      Me._formatType = formatType
      Me._formatter = formatter
    End Sub

    Public Shared Function CreatePlainTextFormatableXpValues(ByVal name As String) As XpValues
      ' パラメータの削除
      Return New XpValues(name, 0, New PlainTextFormatter)
    End Function

    Public Shared Function CreateHtmlTextFormatableXpValues(ByVal name As String) As XpValues
      ' パラメータの削除
      Return New XpValues(name, 1, New HtmlTextFormatter)
    End Function
    ……中略……

    ' プロパティの削除
    Public ReadOnly Property FormatType() As Integer
      Get
        Return _formatType
      End Get
    End Property
    ……中略……
  End Class

End Namespace
XpValuesクラスにおける不要となったFormatTypeプロパティの削除とそれに伴う変更(VB.NET)
 
インデックス・ページヘ  「.NETで始めるデザインパターン」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間