#はじめに
本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「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 | 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を統一的に扱うためのインターフェイス。
namespace AbstractFactory
{
public interface IItem
{
void SetCaption(string caption);
string MakeHTML();
}
}
#抽象的な部品:Linkクラス
URLを取り扱うクラス。
MakeHTML()の具体的な実装がないため、こちらは抽象クラス。
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同様に抽象クラス。
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クラスに実装する。
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クラスを生成するクラス。
ただしインターフェイスの定義の為、各クラスのインスタンス生成は
具体的な工場クラスで実装する。
また、生成する工場は呼び出し元から渡される引数をもとに生成する。
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タグを生成
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()メソッドが呼び出される。
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()が呼び出される。
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を生成する。
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を生成する。
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();
}
}
}
#実装してみて
・今後、工場(この場合、違うHTMLにて出力)するケースを増やす場合でも、新しく工場の実装を増やせば良い。(main処理は修正不要)。
・Itemクラスをインターフェイスにすることでlistクラス、Itemクラスは確実にMakeHTML()メソッドを実装されることを担保される。
・pageクラス(この場合、実装クラスのListpageクラス)のMakeHTMLクラスにおけるforEach構文
でイテレータのカウンタをitemにして実装することが可能。