デザインパターン
Facade

デザインパターン勉強会 第十五回:Facadeパターン

はじめに

本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「Java言語で学ぶデザインパターン入門」をベースに学習を進めますが、サンプルコードはC#に置き換えて解説します。

第1回:Iteratorパターン

第2回:Adapterパターン

第3回:Template Methodパターン

第4回:Factory Methodパターン

第5回:Singletonパターン

第6回:Prototypeパターン

第7回:Builderパターン

第8回:Abstract Factoryパターン

第9回:Bridgeパターン

第10回:Strategyパターン

第11回:Compositeパターン

第12回:Decoratorパターン

第13回:Visitorパターン

第14回:Chain of Responsivilityパターン


Facadeパターンとは

複雑な内部処理をまとめ、システムの外側にシンプルなAPI(Application Programming Interface:ソフトウェアの機能や管理するデータなどを、外部の他のプログラムから呼び出して利用するための手順やデータ形式を定めた規約)を提供するデザインパターンを指す。

Fig1.png

役割名 役割
Facade システムを構成するその他のクラスの機能をまとめたAPIをシステム外部に提供する
その他のクラス 各クラスで定義された機能を実装する
Client Facadeを利用する

※Facadeクラス自体をその他のクラスとみなし、再帰的にFacadeパターンを適用することも可能である。


サンプルプログラムのクラス図

Fig2.png


各クラスの役割

以下に各クラスの役割を示す。

クラス名 役割 概要
Database その他のクラス ユーザー名を利用し、リソースファイルに登録されているメールアドレスの検索を行う
HtmlWriter その他のクラス 指定された情報を利用し、HTMLタグを生成する
PageMaker Facade DatabaseとHtmlWriterを利用したHTMLページ作成処理を実行する
Program Client 動作確認用の実行クラス

Databaseクラス

ユーザー名を利用し、リソースファイルに登録されているメールアドレスの検索を行う。

using System;
using System.Collections.Generic;
namespace FacadePatternConsole.PageMaker
{
    /// <summary>
    /// ユーザー名を利用し、リソースファイルに登録されているメールアドレスの検索を行う
    /// </summary>
    class Database
    {
        private Database() { }

        /// <summary>
        /// ユーザ名からメールアドレスを検索する
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public static string GetUserMailAddr(string userName)
        {
            return MailData.ResourceManager.GetString(userName);
        }
    }
}


HtmlWriterクラス

指定された情報を利用し、HTMLタグを生成する。

各メソッドの機能は以下の通り。

メソッド名 役割
WriteTitle HTMLページのタイトル部分を構成するタグ(html, head, title, body, h1)を生成する
WriteParagraph HTMLページの段落を構成するタグ(p)を生成する
WriteLink HTMLページのリンクを構成するタグ(a href)を生成する
WriteMailTo メールアドレス部分の文字列を生成する
Close body, headの末尾を生成する
using System;
using System.IO;

namespace FacadePatternConsole.PageMaker
{
    /// <summary>
    /// 指定された情報を利用し、HTMLタグを生成する
    /// </summary>
    class HtmlWriter:IDisposable
    {
        public StreamWriter _writer { get; set; }

        public HtmlWriter(StreamWriter writer)
        {
            this._writer = writer;
        }

        /// <summary>
        /// HTMLページのタイトル部分を構成するタグ(html, head, title, body, h1)を生成する
        /// </summary>
        /// <param name="title">HTMLページのタイトル</param>
        public void WriteTitle(string title)
        {
            _writer.Write("<html>");
            _writer.Write("<head>");
            _writer.Write("<title>" + title + "</title>");
            _writer.Write("</head>");
            _writer.Write("<body>\n");
            _writer.Write("<h1>" + title + "<h1>");
        }
        /// <summary>
        /// HTMLページの段落を構成するタグ(p)を生成する
        /// </summary>
        /// <param name="msg"></param>
        public void WriteParagraph (string msg)
        {
            _writer.Write("<p>" + msg + "</p>\n");
        }
        /// <summary>
        /// HTMLページのリンクを構成するタグ(a href)を生成する
        /// </summary>
        /// <param name="href"></param>
        /// <param name="caption"></param>
        public void WriteLink(string href, string caption)
        {
            _writer.Write("<a href=\"" + href+ "\">" + caption + "</p>\n");
        }
        /// <summary>
        /// メールアドレス部分の文字列を生成する
        /// </summary>
        /// <param name="mailAddr"></param>
        /// <param name="userName"></param>
        public void WriteMailTo(string mailAddr, string userName)
        {
            WriteLink(("mailto:" + mailAddr), userName);
        }
        /// <summary>
        /// body, headの末尾を生成する
        /// </summary>
        public void Close()
        {
            _writer.Write("</body>");
            _writer.Write("</html>\n");
        }

        public void Dispose()
        {
            _writer.Dispose();
        }
    }
}

PageMakerクラス

DatabaseクラスとHtmlWriterクラスを利用し、指定したユーザのWebページのHTMLを生成する。

HTMLページの生成に必要な操作(リソースからの情報取得、HTMLタグの生成・構成)は全てPageMaker.MakeWelcomePageメソッド内で完結している。
このため、Clientは適切な引数を指定してPageMaker.MakeWelcomePageメソッドを呼び出すのみでよい。

using System;
using System.IO;
using System.Text;

namespace FacadePatternConsole.PageMaker
{
    /// <summary>
    /// DatabaseとHtmlWriterを利用したHTMLページ作成処理を実行する
    /// </summary>
    class PageMaker
    {
        private PageMaker() { }

        /// <summary>
        /// 指定したユーザとファイル名でHTMLページを作成する
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="fileName"></param>
        public static void MakeWelcomePage(string userName, string fileName)
        {
            // メールアドレスの検索
            string mailAddr = Database.GetUserMailAddr(userName);
            // htmlの作成
            using (HtmlWriter writer = new HtmlWriter(new StreamWriter(fileName,false,Encoding.Unicode)))
            {
                writer.WriteTitle("Welcome to" + userName + "'s Page!");
                writer.WriteParagraph(userName + "のページへようこそ");
                writer.WriteParagraph("メールまってますね。");
                writer.WriteMailTo(mailAddr, userName);
                writer.Close();
            }
            // HTML出力先の表示
            Console.WriteLine(fileName + @" is created for " + mailAddr + @" (" + userName + @")");
            Console.ReadKey();
        }
    }
}

Programクラス

PageMakerクラスのMakeWelcomePageメソッドを呼び出し、 HTMLファイルの生成を指示する。

HTMLファイル生成の手順はMakeWelcomePageメソッド内でカプセル化されており、Programクラス側ではMakeWelcomePageメソッドの呼び出しのみを行えばよい。

using System;
namespace FacadePatternConsole
{
    class Program
    {
        /// <summary>
        /// 動作確認用の実行クラス
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            PageMaker.PageMaker.MakeWelcomePage("Piyo_Taroh", "welcome.html");
        }
    }
}

実行結果

Console.JPG
PiyoTaroh.JPG


Facadeパターン使用によるメリット

Facadeパターンは、複数のクラス・メソッドからなる複雑な処理をまとめて1つのAPIを提供する。この特徴より、以下の利点が得られると考えられる。

  • クラス・メソッドの集合(パッケージ)の外部との結合度を下げ、再利用性を高める
  • 複数の処理の集合に対し一律の窓口を提供することで、利用者側での実装負担を軽減する

パッケージ・フレームワークの規模が大きくなればなるほどクラスやメソッドは増加し、利用したい機能についてどの操作を行えばよいかが不明瞭になりがちである。このような場合にFacadeパターンを適用し、提供したい機能と呼び出すメソッドが一対一対応するように設計するとよいだろう。


サンプルコード

以下に公開しています。

https://github.com/aconit96/FacadePatternConsole