LoginSignup
5

More than 5 years have passed since last update.

デザインパターン勉強会 第8回:Abstract Factoryパターン

Last updated at Posted at 2017-09-19
1 / 2

はじめに

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

第1回:Iteratorパターン
第2回:Adapterパターン
第3回:Template Methodパターン
第4回:Factory Methodパターン
第5回:Singletonパターン
第6回:Prototypeタイプ
第7回:Builderタイプ

Abstract Factoryパターンとは

Factoryメソッドはインターフェイスを提供して、具体的な実装はすべてインターフェイスの実装クラス側で行います。Abstract Factoryメソッドは関連する部品(クラス)も含めてインスタンスを生成してくれる手順のインターフェイスを提供します。
よって作りたい部品を渡せばfactoryインターフェイスを実装したfactoryメソッドで部品のインスタンス生成してくれます。

サンプルクラス図

AbstractFactory.png

サンプルクラス一覧

パッケージ 名 前 解 説
Abstractfactory IItem LinkとTrayを統一的に扱うためのインターフェイス
Abstractfactory Link 抽象的な部品:HTMLのリンクを表す
Abstractfactory Tray 抽象的な部品:LinkやTrayを集めた部品
Abstractfactory Page HTMLのページを生成する
Abstractfactory Factory 部品を作成する
listfactory ListLink 具体的な部品:HTMLのリンクを表すクラス
listfactory ListTray 具体的な部品:LinkやTrayを集めたクラス
listfactory ListPage 具体的な部品:HTMLのページを表すクラス
listfactory Listfactory 具体的な工場を表すクラス(ListLink, ListTray, ListPageを作る)
- Main 動作テスト用のクラス

抽象的な部品:IItemクラス

LinkとTrayを統一的に扱うためのインターフェイス。

Item.cs
namespace AbstractFactory
{
    public interface IItem
    {
        void SetCaption(string caption);
        string MakeHTML();

    }
}

抽象的な部品:Linkクラス

URLを取り扱うクラス。
MakeHTML()の具体的な実装がないため、こちらは抽象クラス。

Link.cs
namespace AbstractFactory
{
    public abstract class Link : Item
    {
        protected string url;
        public Link(string caption, string url) : base(caption)
        {
            this.url = url;
        }
    }
}

抽象的な部品:Trayクラス

複数のLinkやItemを集めるクラス。
MakeHTML()の具体的な実装がないため、こちらもLink同様に抽象クラス。

Tray.cs
namespace AbstractFactory
{
    public abstract class Tray : IItem
    {
        protected string caption;

        protected List<IItem> tray = new List<IItem>();
        public Tray(string caption) : base()
        {
            this.caption = caption;
        }

        public void Add(IItem item)
        {
            tray.Add(item);
        }

        public void SetCaption(string caption)
        {
            this.caption = caption;
        }

        public abstract string MakeHTML();
    }
}

Pageクラス

継承先のPageクラスで生成したHTML形式の文字列をファイル出力する。
※ファイル出力のみの為、継承元のPageクラスに実装する。

Page.cs
namespace AbstractFactory
{
    public abstract class Page
    {
        protected string title;
        protected string author;
        protected List<IItem> content = new List<IItem>();
        public Page(string title, string author)
        {
            this.title = title;
            this.author = author;
        }

        public void Add(IItem item)
        {
            this.content.Add(item);
        }

        public void Output(string HtmlFileName)
        {
            try
            {
                string filename = HtmlFileName + ".html";
                using (StreamWriter writer = new StreamWriter(filename, false, Encoding.UTF8))
                {
                    writer.Write(this.MakeHTML());
                }
                Console.WriteLine($"{filename}を作成しました。");
            }
            catch (IOException e)
            {
                Console.Error.WriteLine(e);
            }
        }

        public abstract string MakeHTML();
    }
}

抽象的な工場:Factoryクラス

Item、Link、Trayクラスを生成するクラス。
ただしインターフェイスの定義の為、各クラスのインスタンス生成は
具体的な工場クラスで実装する。
また、生成する工場は呼び出し元から渡される引数をもとに生成する。

Factory.cs
namespace AbstractFactory
{
    public abstract class Factory

    {
        public static Factory GetFactory(string AsmName ,string ClassName)
        {
            Factory factory = null;
            try
            {
                Assembly asm = Assembly.Load(AsmName);
                factory = (Factory)asm.CreateInstance(
                   ClassName,
                   false,
                   BindingFlags.CreateInstance,
                   null,
                   null,
                   null,
                   null
                );
            }

            catch (TypeLoadException)
            {
                Console.Error.WriteLine($"クラス{ClassName}が見つかりません。");
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.StackTrace);
            }
            return factory;
        }

        public abstract Link CreateLink(string caption, string url);
        public abstract Tray CreateTray(string caption);
        public abstract Page CreatePage(string title, string author);
    }
}

具体的な部品:ListLinkクラス

Linkクラスの実装部。
引数で指定した見出し(caption)とURLをもとにHTMLタグを生成

ListLink.cs
namespace ListFactory
{
    using AbstractFactory;

    public class ListLink : Link
    {
        public ListLink(string caption, string url) : base(caption, url) { }

        public override string MakeHTML()
        {
            return $"  <li><a href=\"{url}\">{caption}</a></li>\n";
        }
    }
}

具体的な部品:ListTrayクラス

Linkクラスの実装部。
MakeHTML()の実装がありここでLinkとTrayからHTMLを生成する。
※ListItemとListTrayのMakeHTML()が実装されているため、イテレータ(ForEach構文)にて
インターフェイス側のIItemを指定しており、それぞれのMakeHTML()メソッドが呼び出される。

ListTray.cs
namespace ListFactory
{
    public class ListTray : Tray
    {
        public ListTray(string caption) : base(caption) { }

        public override string MakeHTML()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<li>\n");
            sb.Append($"{caption}\n");
            sb.Append("<ul>\n");

            foreach (IItem i in tray)
            {
                sb.Append(i.MakeHTML());
            }

            sb.Append("</ul>\n");
            sb.Append("</li>\n");
            return sb.ToString();
        }
    }
}

具体的な部品:ListPageクラス

Pageクラスの実装部。
ListTray、ListLinkにHead、bodyを付与してHTML形式の文字列を生成する。
※ListTrayと同様にForEach構文でIItemの型を指定している為、変数がListTray、ListLinkのいずれの型であっても
問題なくMakeHTML()が呼び出される。

ListPage.cs
namespace ListFactory
{
    public class ListPage : Page
    {
        public ListPage(string title, string author) : base(title, author) { }
        public override string MakeHTML()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"<html><head><title>{title}</title></head>\n");
            sb.Append("<body>\n");
            sb.Append($"<h1>{title}</h1>");
            sb.Append("<ul>\n");

            foreach (IItem i in content)
            {
                sb.Append(i.MakeHTML());
            }

            sb.Append("</ul>\n");
            sb.Append($"<hr><address>{author}</address>");
            sb.Append("</body></html>\n");
            return sb.ToString();
        }
    }
}

具体的な工場:ListFactoryクラス

ListLink、ListTray、ListPageを生成する。

ListFactory.cs
namespace ListFactory
{
    using AbstractFactory;

    // ConcreteFactory
    public class ListFactory : Factory
    {
        public override Link CreateLink(string caption, string url)
        {
            return new ListLink(caption, url);
        }

        public override Tray CreateTray(string caption)
        {
            return new ListTray(caption);
        }

        public override Page CreatePage(string title, string author)
        {
            return new ListPage(title, author);
        }
    }
}

メイン処理:Mainクラス

作成したいHTMLを作る工場を指定してインスタンス生成する。
生成した工場クラスに作りたいリンクおよびページの見出し、URLを渡すことで
Tray、Link、Pageを生成する。

Main.cs
namespace Main
{
    class Program
    {
        // Main処理
         static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                //第1引数:アセンブリ名 第2引数:名前空間.クラス名
                Console.WriteLine("Example 1: C# AbstractFactory ListFactory ListFactory.ListFactory");
                Environment.Exit(0);
            }

            Factory AbstractFactory = Factory.GetFactory(args[0], args[1]);
            Link asahi = AbstractFactory.CreateLink("朝日新聞", "http://www.asashi.com/");
            Link yomiuri = AbstractFactory.CreateLink("読売新聞", "http://www.yomiuri.co.jp/");

            Link usYahoo = AbstractFactory.CreateLink("Yahoo!", "http://www.yahoo.com/");
            Link jpYahoo = AbstractFactory.CreateLink("Yahoo!Japan", "http://www.yahoo.co.jp/");
            Link excite = AbstractFactory.CreateLink("Excite", "http://www.excite.co.jp/");
            Link google = AbstractFactory.CreateLink("Google", "http://www.google.com/");

            Tray traynews = AbstractFactory.CreateTray("新聞");
            traynews.Add(asahi);
            traynews.Add(yomiuri);

            Tray trayyahoo = AbstractFactory.CreateTray("Yahoo!");
            trayyahoo.Add(usYahoo);
            trayyahoo.Add(jpYahoo);

            Tray traysearch = AbstractFactory.CreateTray("サーチエンジン");
            traysearch.Add(trayyahoo);
            traysearch.Add(excite);
            traysearch.Add(google);

            Page page = AbstractFactory.CreatePage("LinkPage", "AbstractFactory Sample");
            page.Add(traynews);
            page.Add(traysearch);
            page.Output(args[1]);

            // 実行が一瞬で終わって確認ため、キーの入力を待ちます
            Console.ReadLine();
        }
    }
}

実行結果

キャプチャ.PNG

実装してみて

・今後、工場(この場合、違うHTMLにて出力)するケースを増やす場合でも、新しく工場の実装を増やせば良い。(main処理は修正不要)。
・Itemクラスをインターフェイスにすることでlistクラス、Itemクラスは確実にMakeHTML()メソッドを実装されることを担保される。
・pageクラス(この場合、実装クラスのListpageクラス)のMakeHTMLクラスにおけるforEach構文
でイテレータのカウンタをitemにして実装することが可能。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5