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

第4回 リファクタリングにより導き出すTemplate Methodパターン

太陽システム株式会社 中西 庸文
Microsoft MVP 2005 - Solutions Architect)
2005/04/23
Page1 Page2 Page3 Page4

●リファクタリングの結果

 最終状態のクラス図を以下に示す。

リファクタリングの結果として導かれたTemplate Methodパターンのクラス図

 ここまでに行ったリファクタリングは「Template Methodの形成(K)」と呼ばれているものだ。

 その手順を簡単に振り返ってみよう。

1. 「Compose Method(K)」(メソッドの組み立て)によるアルゴリズムの骨組みの形成
 HtmlTextFormatterクラスのFormatメソッドとPlainTextFormatterクラスのFormatメソッドをそれぞれ単一の責務を持つメソッドに分解して再組織化

2. 「メソッドの引き上げ(F)」による派生クラス間で<同一なメソッド>の基本クラスへの引き上げ
 (今回は必要がなかったのでスキップ)

3. 「メソッド名の変更(F)」による派生クラス間で<ユニークなメソッド>のメソッド・シグネチャの統一
 (今回は必要がなかったのでスキップ)

4. 「メソッド名の変更(F)」による派生クラス間で共通化された<アルゴリズムの骨組みとなるメソッド>のメソッド・シグネチャの統一
 (今回は必要がなかったのでスキップ)

5.「メソッドの引き上げ(F)」による<アルゴリズムの骨組みとなるメソッド>の基本クラスへの引き上げ
 HtmlTextFormatterクラスのFormatメソッドとPlainTextFormatterクラスのFormatメソッドを基本クラスであるTextFormatterクラスへと引き上げる。それに伴って派生クラス間でユニークなメソッドを基本クラスに抽象メソッドとして定義し、HtmlTextFormatterクラスとPlainTextFormatterクラスでそれぞれオーバーライドする

 クラス間をまたがって重複するアルゴリズムが存在するような場合には、このようなリファクタリングを実施する。これによってTemplate Methodパターンが適用され、アルゴリズムは可変部分(ホット・スポット)と固定部分(コールド・スポット)に分離される。

 今回の例では、Template MethodとなったTextFormatterクラスのFormatメソッドはコールド・スポットに、FormatメソッドからフックされるTitleTextメソッド、ValuesHeaderTextメソッド、EachValueTextメソッド、ValuesFooterメソッドはそれぞれホット・スポットに分離された。

 新しい形式で価値を出力する必要が発生すれば、TextFormatterクラスの派生クラスを用意して、ホット・スポットのメソッドを必要に応じてオーバーライドするだけでよい。

●まとめ

 最終状態のすべてのコードを以下に示す。これらのコードでは、1つのことが1カ所でのみ行われていて、2度書かれていることはない。これは「一度、たった一度だけ」(Once and only once)というシンプルなルールに基づいている。

 このルールと今回のリファクタリングで紹介したComposed Methodは、リファクタリングの基本となるものなので、ぜひ覚えておいていだだきたい。

using System;
using System.Collections;

namespace DesignPatterns.Core.TemplateMethod
{
  // XpValuesクラス
  public class XpValues : IEnumerable
  {
    private string name;
    private ArrayList values = new ArrayList();
    private TextFormatter formatter;

    // プライベート・コンストラクタ
    private XpValues(string name, TextFormatter formatter)
    {
      this.name = name;
      this.formatter = formatter;
    }

    // 保持する「価値」をプレーン・テキスト形式で出力できるインスタンスを生成
    public static XpValues CreatePlainTextFormatableXpValues(string name)
    {
      return new XpValues(name, new PlainTextFormatter());
    }

    // 保持する「価値」をHTML形式で出力できるインスタンスを生成
    public static XpValues CreateHtmlTextFormatableXpValues(string name)
    {
      return new XpValues(name, new HtmlTextFormatter());
    }

    // 名前を取得する
    public string Name
    {
      get { return name; }
    }

    // 「価値」の数を取得する
    public int Count
    {
      get { return values.Count; }
    }

    // 「価値」を取得する
    public string this[int index]
    {
      get { return values[index].ToString(); }
    }

    // 「価値」を登録する
    public void Add(string xpValue)
    {
      values.Add(xpValue);
    }

    // XpValues を反復処理できる列挙子を取得する
    public IEnumerator GetEnumerator()
    {
      return values.GetEnumerator();
    }

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

    public string Format()
    {
      return formatter.Format(this);
    }
  }
}
XpValuesクラス(C#)
 
using System;
using System.Text;

namespace DesignPatterns.Core.TemplateMethod
{
  // TextFormatterクラス
  public abstract class TextFormatter
  {
    //「価値」を出力する
    public string Format(XpValues xpValues)
    {
      StringBuilder builder = new StringBuilder();
      builder.Append(TitleText(xpValues));
      builder.Append(ValuesHeaderText());
      foreach (string xpValue in xpValues)
        builder.Append(EachValueText(xpValue));
      builder.Append(ValuesFooterText());
      return builder.ToString();
    }

    // タイトルの出力
    protected abstract string TitleText(XpValues xpValues);

    // ヘッダの出力
    protected virtual string ValuesHeaderText()
    {
      return null;
    }

    // 価値の出力
    protected abstract string EachValueText(string xpValue);

    // フッタの出力
    protected virtual string ValuesFooterText()
    {
      return null;
    }

    // タイトルを取得する
    protected string GetTitleFor(XpValues xpValues)
    {
      return xpValues.Name + "の" + xpValues.Count.ToString() + "つの価値";
    }
  }
}
TextFormatterクラス(C#)
 
using System;

namespace DesignPatterns.Core.TemplateMethod
{
  // PlainTextFormatterクラス
  public class PlainTextFormatter : TextFormatter
  {
    // タイトルの出力
    protected override string TitleText(XpValues xpValues)
    {
      return GetTitleFor(xpValues) + "\r\n";
    }

    // 価値の出力
    protected override string EachValueText(string xpValue)
    {
      return "・" + xpValue + "\r\n";
    }
  }
}
PlainTextFormatterクラス(C#)
 
using System;

namespace DesignPatterns.Core.TemplateMethod
{
  // HtmlTextFormatterクラス
  public class HtmlTextFormatter : TextFormatter
  {
    // タイトルの出力
    protected override string TitleText(XpValues xpValues)
    {
      return "<p>" + GetTitleFor(xpValues) + "</p>\r\n";
    }

    // ヘッダの出力
    protected override string ValuesHeaderText()
    {
      return "<ul>\r\n";
    }

    // 価値の出力
    protected override string EachValueText(string xpValue)
    {
      return "<li>" + xpValue + "\r\n";
    }

    // フッタの出力
    protected override string ValuesFooterText()
    {
      return "</ul>\r\n";
    }
  }
}
HtmlTextFormatterクラス(C#)

 前回は、条件分岐を除去するためのリファクタリングからStrategyパターンが導かれる実例を、そして今回は、重複するアルゴリズムを除去するためのリファクタリングからTemplate Methodパターンが導かれる実例を、それぞれコードを中心に解説した。

 その中でリファクタリングとパターンの相性が非常に良いことが分かっていただけたのではないだろうか。

 次回からは、リファクタリングからまた別のパターンを導くための方法を紹介する。End of Article


中西 庸文
Microsoft MVP for Visual Developer - Solutions Architect

1998年 太陽システム株式会社入社。オブジェクト指向、アジャイル開発プロセスの啓蒙活動に従事。現在は「協調」と「自主的な行動」を基盤としたチーム開発を行うために、プロジェクトファシリテーション(PF)の重要性を強く感じている。 VB&C#デザインパターンINETA Japan 加盟コミュニティ)を運営中。


 

 INDEX
  .NETで始めるデザインパターン
  第4回 リファクタリングにより導き出すTemplate Methodパターン
    1.リファクタリングから Template Method パターンへ
    2.リファクタリング:「Compose Method(K)」
    3.リファクタリング:「メソッドの引き上げ(F)」
  4.リファクタリングの結果
 
インデックス・ページヘ  「.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 記事ランキング

本日 月間