概要
@hyuki 先生著の『Javaで学ぶデザインパターン入門』(2004年、SB Creative)の1章ずつをベースに、サンプルコードをC#で置き換えながら勉強していく記事です。
※著者の @hyuki 先生には適切に書籍への参照を入れれば問題ない旨ご確認いただいています。
本題
Template Methodパターン
第3回はTemplate Methodパターンです。Template Methodパターンは本の表現を借りると、「スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を定める」ようなデザインパターンです。
サンプルコード
早速具体的な事例を見てみましょう。『Javaで学ぶデザインパターン入門』(2004年、SB Creative)に掲載されているコードをC#で(大体)書き換えます。
// コンソールアプリケーションで実行を確認しました
using System;
using System.Text;
namespace TemplateMethodPattern
{
class Program
{
static void Main(string[] args)
{
AbstractDisplay d1 = new CharDisplay('H');
AbstractDisplay d2 = new StringDisplay("Hello, Wirld");
AbstractDisplay d3 = new StringDisplay("こんにちは!");
d1.Display();
// => << HHHHH >>
d2.Display();
// =>
// +------------+
// |Hello, Wirld|
// |Hello, Wirld|
// |Hello, Wirld|
// |Hello, Wirld|
// |Hello, Wirld|
// +------------+
d3.Display();
// =>
// +------------+
// |こんにちは!|
// |こんにちは!|
// |こんにちは!|
// |こんにちは!|
// |こんにちは!|
// +------------+
// 実行が一瞬で終わって確認できないので、キーの入力を待ちます
Console.ReadLine();
}
}
public abstract class AbstractDisplay
{
public abstract void Open();
public abstract void Print();
public abstract void Close();
// テンプレートメソッド
public void Display()
{
this.Open();
for(int i = 0; i < 5; i++)
{
this.Print();
}
this.Close();
}
}
// テンプレートメソッドの挙動はサブクラスでの実装に依る
public class CharDisplay : AbstractDisplay
{
private char Ch { get; set; }
public CharDisplay(char ch)
{
this.Ch = ch;
}
public override void Open()
{
Console.Write("<<");
}
public override void Print()
{
Console.Write(Ch);
}
public override void Close()
{
Console.WriteLine(">>");
}
}
public class StringDisplay : AbstractDisplay
{
private string Str { get; set; }
private int Width { get; set; }
public StringDisplay(string str)
{
this.Str = str;
Encoding sjisEnc = Encoding.GetEncoding("Shift_JIS");
this.Width = sjisEnc.GetByteCount(str);
}
public override void Open()
{
this.PrintLine();
}
public override void Print()
{
Console.WriteLine($"|{this.Str}|");
}
public override void Close()
{
this.PrintLine();
}
private void PrintLine()
{
Console.Write("+");
for(int i = 0; i < this.Width; i ++)
{
Console.Write("-");
}
Console.WriteLine("+");
}
}
}
効能
- ロジックが共通化できる
- あるクラスの派生クラス(複数)内にある似た機能を持つメソッドで問題があったとき、共通化されたロジック内の問題なら派生クラスのメソッド1つ1つでなくテンプレートメソッドの修正で済む。
使用上の注意
- サブクラスを実装する際に、テンプレートメソッドの部品が基底クラスのどの部分で呼び出されるのかを把握しておく必要がある。
メモ
- スーパークラス型の変数にサブクラスのインスタンスのどれを代入しても正しく動作するようにするという原則をThe Liskov Substituion Principale(LSP)と呼ぶ。デザインパターンの文脈に限らない、継承の一般的な原則。
関連しているパターン
- Factory Methodパターン
- Strategyパターン
感想や疑問
- このパターンを使うというより、結果このパターンになるように派生クラスのロジックを基底クラスに寄せるよう努力する姿勢が大事なように思いました。
- 一方で共通化すればするほど派生クラスでの自由度が減るので、いい具合に設計しよう。
C#で学ぶデザインパターン入門
①Iterator
②Adapter
③Template Method
④Factory Method
⑤Singleton
⑥Prototype
⑦Builder
⑧AbstractFactory
⑨Bridge
⑩Strategy
⑪Composite Pattern
⑫Decorator Pattern