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

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

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

■リファクタリング1:「Compose Method(K)」(メソッドの組み立て)によるアルゴリズムの骨組みの形成

 今回のリファクタリングにおける最初の目的は、似たようなアルゴリズムからアルゴリズムの骨組みを形成することだ。

 そこで、「Compose Method(K)」(メソッドの組み立て)を行う(「K」の意味については、「前回」を参照してほしい)。

 このリファクタリングはComposed Methodパターンを導くためのリファクタリングである。

 Composed Methodパターンは、Kent Beck氏による書籍『Smalltalk ベストプラクティス・パターン』の中で、以下のように説明されている。

プログラムを一つのことのみをするメソッドに分割しよう。メソッド内部のメッセージは同じ抽象度のレベルになるようにする。こうするとプログラムは自然と数行のみの短いメソッドの集まりになる。

 「Composed Method」とは、単一の責務を持つ小さなメソッド群から成り立つメソッドのことである。よいComposed Methodは同じ詳細レベルのコードから形成される。

 つまり「Compose Method(K)」(メソッドの組み立て)リファクタリングとは、メソッド内部に異なる詳細レベルのコードがある場合、その異なる部分を抽出してメソッド呼び出しに置き換え、メソッドの詳細レベルをそろえることだ。

 このようにメソッドを再組織化することで、そのメソッドが持つ責務が明確になる。また、抽出された各メソッドには読み手に意図が伝わるような名前を付けることで、コードの可読性が向上する。

 それではさっそくリファクタリングを開始しよう。

 ここでは、「ヘッダの出力」および「フッタの出力」の必要があるHtmlTextFormatterクラスのFormatメソッドから始めることにする。

 HtmlTextFormatterクラスのFormatメソッドがComposed Methodとなるように、「メソッドの抽出(F)」を行い、Formatメソッドを「タイトルの出力」「ヘッダの出力」「価値の出力」「フッタの出力」といったそれぞれ単一の責務を持ったメソッド群に分割する。

using System;
using System.Text;

namespace DesignPatterns.Core.TemplateMethod
{
  public class HtmlTextFormatter : TextFormatter
  {
    // メソッドの抽出が行われてComposed MethodになったFormatメソッド
    public override 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();
    }

    // タイトルの出力
    private string TitleText(XpValues xpValues)
    {
      return "<p>" + GetTitleFor(xpValues) + "</p>\r\n";
    }

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

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

    // フッタの出力
    private string ValuesFooterText()
    {
      return "</ul>\r\n";
    }
  }
}
Composed Methodパターンが適用されたHtmlTextFormatterクラス(C#)

 コンパイルしてテストを実行しよう。正常にテスト(テスト・コードについては前回を参照)が通るはずだ。

 次に、PlainTextFormatterクラスに対しても同様のリファクタリングを行う。

 ただしPlainTextFormatterクラスのFormatメソッドでは、「ヘッダの出力」および「フッタの出力」の必要がないことを明示的にするために、それぞれnullを返すValuesHeaderTextメソッドとValuesFooterTextメソッドを追加する。

using System;
using System.Text;

namespace DesignPatterns.Core.TemplateMethod
{
  public class PlainTextFormatter : TextFormatter
  {
    // メソッドの抽出が行われてComposed MethodになったFormatメソッド
    public override 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();
    }

    // タイトルの出力
    private string TitleText(XpValues xpValues)
    {
      return GetTitleFor(xpValues) + "\r\n";
    }

    // ヘッダの出力(nullを返す)
    private string ValuesHeaderText()
    {
      return null;
    }

    // 価値の出力
    private string EachValueText(string xpValue)
    {
      return "・" + xpValue + "\r\n";
    }

    // フッタの出力(nullを返す)

    private string ValuesFooterText()
    {
      return null;
    }
  }
}
Composed Methodパターンが適用されたPlainTextFormatterクラス(C#)

 再度、コードをコンパイルしてテストを実行してみよう。これも正常に実行できるはずだ。

 これで「Compose Method(K)」(メソッドの組み立て)が完了した。

 その結果として、HtmlTextFormatterクラスのFormatメソッドとPlainTextFormatterクラスのFormatメソッドは、それぞれ単一の責務を持つメソッド群から構成されるComposed Methodになった。

■リファクタリング2:「メソッドの引き上げ(F)」による派生クラス間で<同一なメソッド>の基本クラスへの引き上げ

 次の目的は、派生クラス間における処理の重複を削除するために、「Compose Method(K)」(メソッドの組み立て)の結果として抽出されたメソッド群(本稿の例では、TitleTextメソッド、ValuesHeaderTextメソッド、EachValueTextメソッド、ValuesFooterTextメソッド)のうち、派生クラス間で共通のメソッド・シグネチャと処理内容を持つ<同一なメソッド>を、派生クラスから基本クラスへと引き上げることだ。このようなメソッドに対して「メソッドの引き上げ(F)」を行う。

 しかし、抽出されたメソッド群がそれぞれ派生クラス(本稿の例では、PlainTextFormatterクラスとHtmlTextFormatterクラス)間で(確かにメソッド・シグネチャは同じだが)その処理内容が異なるユニークなメソッドであるため、ここではこれらのメソッド群の「メソッドの引き上げ(F)」のリファクタリングを行う必要はない。従って、次のステップへと進むことにする。

■リファクタリング3:「メソッド名の変更(F)」による派生クラス間で<ユニークなメソッド>のメソッド・シグネチャの統一

 次の目的は、派生クラス間で<アルゴリズムの骨組みとなるメソッド>(具体的には、Formatメソッド)の処理内容を共通化するために、「Compose Method(K)」(メソッドの組み立て)の結果として抽出されたメソッド群のうち、派生クラス間で処理内容の異なる<ユニークなメソッド>(具体的には、TitleTextメソッド、ValuesHeaderTextメソッド、EachValueTextメソッド、ValuesFooterTextメソッド)のメソッド・シグネチャを統一することだ(Composed MethodであるFormatメソッドの内部では、これらのユニークなメソッド群の呼び出しを行っている。よって、メソッド・シグネチャを統一すれば、これらのメソッド呼び出し方法が共通化されるので、Formatメソッドの処理内容が派生クラス間で共通になる)。

 そこで通常は、このようなメソッドに対して「メソッド名の変更(F)」を行う。

 しかし、今回のリファクタリングでは、抽出されたメソッド群はすでに、それぞれ派生クラス間で共通のメソッド・シグネチャを持つユニークなメソッドとなっている。また、これらのユニークなメソッドを内部で呼び出しているアルゴリズムの骨組みとなるメソッド(=Formatメソッド)の処理内容(=アルゴリズム)もすでに派生クラス間で共通化されているため、ここでは実際のリファクタリング作業は必要ない。従って、次のステップへと進むことにする。

■リファクタリング4:「メソッド名の変更(F)」による派生クラス間で共通化された<アルゴリズムの骨組みとなるメソッド>のメソッド・シグネチャの統一

 次の目的は、派生クラス間で共通化された<アルゴリズムの骨組みとなるメソッド>(=Formatメソッド)自体のメソッド・シグネチャを統一することだ。

 そこで通常は、アルゴリズムの骨組みとなるメソッドに対して「メソッド名の変更(F)」を行う。

 しかし、今回のリファクタリングでは、すでにアルゴリズムの骨組みとなるメソッド(=Formatメソッド)は、派生クラス間で同一のメソッド・シグネチャになっているため、このリファクタリングも不要である。従って、さらに次のステップへと進むことにする。


 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 記事ランキング

本日 月間