C#デザインパターン  - TemplateMethod

ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。
解説らしい解説は載せていませんので、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』も合わせてお読みください。

※当ソースは、Visual C# 2008 Express Editionで動作を確認しています。
  Visual Studio 2005をお使いの方は、ブログの記事を残しておきますので、そちらをご覧ください。



■AbstractDisplay.cs
using System;
using Gushwell.Dejavu;

namespace Gushwell.DesignPatterns {
    // 抽象クラスAbstractDisplay
    public abstract class AbstractDisplay {
        public abstract void Open();  // サブクラスに実装をまかせる抽象メソッド(1) Open
        public abstract void Print(); // サブクラスに実装をまかせる抽象メソッド(2) Print
        public abstract void Close(); // サブクラスに実装をまかせる抽象メソッド(3) Close

        // この抽象クラスで実装しているテンプレートメソッド:Display
        // 具体的に何をやるかは、サブクラスの(Open, Print, Closeの)実装による
        public void Display() {
            Open();                   // まずopenして…
            5.Times(() => Print());   // 5回printを繰り返して…
            Close();                  // …最後にCloseする。
            // これがDisplayメソッドで実装されている内容。
        }
    }
}




■CharDisplay.cs
using System;

namespace Gushwell.DesignPatterns {
    public class CharDisplay : AbstractDisplay {
        // CharDisplayは、AbstractDisplayのサブクラス。
        private char ch; // 表示すべき文字。

        public CharDisplay(char ch) {   // コンストラクタで渡された文字chを、
            this.ch = ch;               // フィールドに記憶しておく。
        }

        // スーパークラスでは抽象メソッドだった。ここでオーバーライドして実装。
        public override void Open() {
            Console.Write("<<");        // 開始文字列として__S_T_R_I_N_G__を表示する。
        }

        // printメソッドもここで実装する。これがdisplayから繰り返して呼び出される。
        public override void Print() {
            Console.Write(ch);          // フィールドに記憶しておいた文字を1個表示する。
        }

        // closeメソッドもここで実装。
        public override void Close() {
            Console.WriteLine(">>");    // 終了文字列__S_T_R_I_N_G__を表示。
        }
    }
}




■StringDisplay.cs
using System;
using System.Text;

namespace Gushwell.DesignPatterns {
    public class StringDisplay : AbstractDisplay {
        // StringDisplayも、AbstractDisplayのサブクラス。
        private string str;             // 表示するべき文字列。
        private int width;              // バイト単位で計算した文字列の「幅」。
        public StringDisplay(string str) {
            // コンストラクタで渡された文字列stringを、
            this.str = str;             // フィールドに記憶。
            this.width = GetWidth(str); // それからバイト単位の幅もフィールドに記憶しておいて、後で使う。
        }

        // オーバーライドして定義するopenメソッド。
        public override void Open() {
            PrintLine();                // このクラスのメソッドPrintLineで線を引いている。
        }

        // Printメソッドは、
        // フィールドに記憶しておいた文字列の前後に__S_T_R_I_N_G__をつけて表示。
        public override void Print() {
            Console.WriteLine("|" + str + "|");
        }

        // Closeメソッドは、
        // Openと同じくPrintLineメソッドで線を引いている。
        public override void Close() {
            PrintLine();
        }

        // OpenとCloseから呼ばれるPrintLineメソッドだ。
        // privateなので、このクラスの中だけで使われる。
        private void PrintLine() {
            Console.Write("+");         // 枠の角を表現する__S_T_R_I_N_G__マークを表示。
            // width個の__S_T_R_I_N_G__を表示して、枠線として用いる。
            Console.Write(new string('-', width));
            Console.WriteLine("+");     // 枠の角を表現する__S_T_R_I_N_G__マークを表示。
        }

        // 文字幅を求める
        private int GetWidth(string str) {
            return Encoding.GetEncoding("shift-jis").GetByteCount(str);
        }
    }
}




■Program.cs
using System;

namespace Gushwell.DesignPatterns {
    public class Program {
        public static void Main(System.String[] args) {
            // 'H'を持ったCharDisplayのインスタンスを1個作る。
            AbstractDisplay d1 = new CharDisplay('H');

            // __S_T_R_I_N_G__を持ったStringDisplayのインスタンスを1個作る。
            AbstractDisplay d2 = new StringDisplay("Hello, world.");
            
            // __S_T_R_I_N_G__を持ったStringDisplayのインスタンスを1個作る。
            AbstractDisplay d3 = new StringDisplay("こんにちは。");

            d1.Display(); // d1,d2,d3とも、すべて同じAbstractDisplayのサブクラスのインスタンス
            d2.Display(); // だから、継承したdisplayメソッドを呼び出すことができる。
            d3.Display(); // 実際の動作は個々のクラスCharDisplayやStringDisplayで定まる。
        }
    }
}





■nTimes.cs
using System;

namespace Gushwell.Dejavu {
    public static class nTimes {
        public static void Times(this int n, Action<int> action) {
            for (int i = 0; i < n; i++) {
                action(i);
            }
        }

        public static void Times(this int n, Action action) {
            for (int i = 0; i < n; i++) {
                action();
            }
        }
    }
}