|
|
連載
.NETで始めるデザインパターン
第4回 リファクタリングにより導き出すTemplate Methodパターン
太陽システム株式会社 中西 庸文
(Microsoft MVP 2005 - Solutions Architect)
2005/04/23 |
|
|
■リファクタリング5:「メソッドの引き上げ(F)」による<アルゴリズムの骨組みとなるメソッド>の基本クラスへの引き上げ
次の目的は、派生クラス間におけるアルゴリズムの骨組みの重複を削除するために、<アルゴリズムの骨組みとなるメソッド>(=Formatメソッド)を派生クラス(具体的には、PlainTextFormatterクラスとHtmlTextFormatterクラス)から基本クラス(具体的には、TextFormatterクラス)へと引き上げることだ。
そこで、「メソッドの引き上げ(F)」を行い、派生クラス間で共通のメソッド・シグネチャと処理内容を持ったFormatメソッドを基本クラスであるTextFormatterクラスへと引き上げる。
しかし、派生クラス間で(処理内容が)ユニークなメソッドであるTitleTextメソッド、ValuesHeaderTextメソッド、EachValueTextメソッド、ValuesFooterメソッドは、そのメソッド・シグネチャは派生クラス間で同一だが、その処理内容が異なるために、そのままでは基本クラスに引き上げることができない。
これは、これらのユニークなメソッドを抽象メソッドとして基本クラスに定義し、それを派生クラスでオーバーライドする必要があることを意味する。このようにすることで、アルゴリズムの骨組みを基本クラス側で共通化することが可能になる。
一方、アルゴリズムの骨組みとなるメソッドであるFormatメソッドは、派生クラス間でメソッド・シグネチャと処理内容が同一となっており、どちらの派生クラスから「メソッドの引き上げ(F)」を開始してもよい。今回はHtmlTextFormatterクラスからFormatメソッドを引き上げることにする。
using System;
using System.Text;
namespace DesignPatterns.Core.TemplateMethod
{
public abstract class TextFormatter
{
// HtmlTextFormatterクラスから引き上げられたFormatメソッド
public abstract string Format(XpValues xpValues);
public virtual 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();
}
// 抽象メソッドとして定義したTitleTextメソッド
protected abstract string TitleText(XpValues xpValues);
// 抽象メソッドとして定義したValuesHeaderTextメソッド
protected abstract string ValuesHeaderText();
// 抽象メソッドとして定義したEachValueTextメソッド
protected abstract string EachValueText(string xpValue);
// 抽象メソッドとして定義したValuesFooterTextメソッド
protected abstract string ValuesFooterText();
……中略……
}
}
|
|
メソッドが引き上げられたTextFormatterクラス(C#) |
|
次にHtmlTextFormatterクラスでは、基本クラスであるTextFormatterクラスへと引き上げたFormatメソッドを削除し、TitleTextメソッド、ValuesHeaderTextメソッド、EachValueTextメソッド、ValuesFooterメソッドを、基本クラスに定義した抽象メソッドをオーバーライドする形へとそれぞれ変更する。
using System;
using System.Text;
namespace DesignPatterns.Core.TemplateMethod
{
public class HtmlTextFormatter : TextFormatter
{
// 削除された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();
}
// オーバーライドされたTitleTextメソッド
protected override string TitleText(XpValues xpValues)
{
return "<p>" + GetTitleFor(xpValues) + "</p>\r\n";
}
// オーバーライドされたValuesHeaderTextメソッド
protected override string ValuesHeaderText()
{
return "<ul>\r\n";
}
// オーバーライドされたEachValueTextメソッド
protected override string EachValueText(string xpValue)
{
return "<li>" + xpValue + "\r\n";
}
// オーバーライドされたValuesFooterTextメソッド
protected override string ValuesFooterText()
{
return "</ul>\r\n";
}
}
}
|
|
引き上げたメソッドが削除され、抽象メソッドをオーバーライドする形となったHtmlTextFormatterクラス(C#) |
|
これだけではコンパイルが通らないので、PlainTextFormatterクラスのTitleTextメソッド、ValuesHeaderTextメソッド、EachValueTextメソッド、ValuesFooterメソッドも、それぞれ基本クラスに定義した抽象メソッドをオーバーライドする形へと変更する。
using System;
using System.Text;
namespace DesignPatterns.Core.TemplateMethod
{
public class PlainTextFormatter : TextFormatter
{
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();
}
// オーバーライドされたTitleTextメソッド
protected override string TitleText(XpValues xpValues)
{
return GetTitleFor(xpValues) + "\r\n";
}
// オーバーライドされたValuesHeaderTextメソッド
protected override string ValuesHeaderText()
{
return null;
}
// オーバーライドされたEachValueTextメソッド
protected override string EachValueText(string xpValue)
{
return "・" + xpValue + "\r\n";
}
// オーバーライドされたValuesFooterTextメソッド
protected override string ValuesFooterText()
{
return null;
}
}
}
|
|
抽象メソッドをオーバーライドする形となった PlainTextFormatterクラス(C#) |
|
ここでコンパイルしてテストを実行してみよう。正常にコンパイルが完了し、テストも通るはずだ。
HtmlTextFormatterクラスからのメソッドの引き上げの結果として、PlainTextFormatterクラスのFormatメソッドは、基本クラスであるTextFormatterクラスにすでに定義されているFormatメソッドと同一となり、派生クラス側でオーバーライドする必要がないので削除する。
using System;
using System.Text;
namespace DesignPatterns.Core.TemplateMethod
{
public class PlainTextFormatter : TextFormatter
{
// 削除された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();
}
……中略……
}
}
|
|
引き上げたメソッドが削除されたPlainTextFormatterクラス(C#) |
|
これでTextFormatterクラスのFormatメソッドは、派生クラス側でオーバーライドされている個所がなくなったので、仮想メソッドから通常のメソッドへと変更する。
using System;
using System.Text;
namespace DesignPatterns.Core.TemplateMethod
{
public abstract class TextFormatter
{
// 仮想メソッドから通常のメソッドへと変更されたFormatメソッド
public virtual 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();
}
……中略……
}
}
|
|
仮想メソッドから通常のメソッドへの変更が行われたTextFormatterクラス(C#) |
|
再度、コンパイルしてテストを実行してみよう。これも正常に実行できるはずだ。
■リファクタリング5の途中過程(抽象メソッドのデフォルト実装への置き換え)
PlainTextFormatterクラスのValuesHeaderTextメソッドとValuesFooterTextメソッドは、nullを返しているだけで何もしていない。ところが、HtmlTextFormatterクラスでは固有の実装が行われている。
そこで、基本クラスであるTextFormatterクラスで抽象メソッドとして定義されているこれらのメソッドを仮想メソッドとして定義し、nullを返すデフォルトの実装を用意してやれば、必要な場合だけ派生クラスでオーバーライドすることができる。
using System;
using System.Text;
namespace DesignPatterns.Core.TemplateMethod
{
public abstract class TextFormatter
{
……中略……
// 抽象メソッドからデフォルトの実装へと置き換えられた ValuesHeaderTextメソッド
protected abstract string ValuesHeaderText();
protected virtual string ValuesHeaderText()
{
return null;
}
…
// 抽象メソッドからデフォルトの実装へと置き換えられた ValuesFooterTextメソッド
protected abstract string ValuesFooterText();
protected virtual string ValuesFooterText()
{
return null;
}
……中略……
}
}
|
|
デフォルトの実装が用意されたTextFormatterクラス(C#) |
|
これでPlainTextFormatterクラスのValuesHeaderTextメソッドとValuesFooterTextメソッドは必要なくなったので、削除する。
using System;
namespace DesignPatterns.Core.TemplateMethod
{
public class PlainTextFormatter : TextFormatter
{
……中略……
// 削除されたValuesHeaderTextメソッド
protected override string ValuesHeaderText()
{
return null;
}
……中略……
// 削除されたValuesFooterTextメソッド
protected override string ValuesFooterText()
{
return null;
}
}
}
|
|
メソッドが削除されたPlainTextFormatterクラス(C#) |
|
これで「メソッドの引き上げ(F)」が完了した。以上ですべてのリファクタリングは終わりだ。最終的なリファクタリング結果を次のページで示す。