Help us understand the problem. What is going on with this article?

C#で学ぶデザインパターン入門 ③Template Method

More than 3 years have passed since last update.

概要

@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)と呼ぶ。デザインパターンの文脈に限らない、継承の一般的な原則。

関連しているパターン

感想や疑問

  • このパターンを使うというより、結果このパターンになるように派生クラスのロジックを基底クラスに寄せるよう努力する姿勢が大事なように思いました。
  • 一方で共通化すればするほど派生クラスでの自由度が減るので、いい具合に設計しよう。

C#で学ぶデザインパターン入門

①Iterator
②Adapter
③Template Method
④Factory Method
⑤Singleton
⑥Prototype
⑦Builder
⑧AbstractFactory
⑨Bridge
⑩Strategy
⑪Composite Pattern
⑫Decorator Pattern

toshi0607
最近はGoとServerlessが好きです!
https://toshi0607.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした