AbstractFactoryパターンとは
抽象的な部品を組み合わせて抽象的な製品を作る。
部品の具体的な実装には注目せず、インターフェース(API)に注目する。
そして、そのインターフェース(API)だけを使って部品を組み立て、製品をまとめる。
AbstractProduct(抽象的な製品)の役
AbstractProduct役はAbstractFactory役によって作り出される抽象的な部品や製品のインターフェースを定める。
package factory;
/**
* HTMLのハイパーリンクを抽象的に表現したクラス
*
*/
public abstract class Link extends Item{
// リンクの飛び先のURL保持用
protected String url;
public Link(String caption, String url) {
super(caption);
this.url = url;
}
}
package factory;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
/**
* HTMLページ全体を抽象的に表現したクラス
*
*/
public abstract class Page {
protected String title;
protected String author;
protected ArrayList<Item> content = new ArrayList<>();
public Page(String title, String author) {
this.title = title;
this.author = author;
}
public void add(Item item) {
content.add(item);
}
public void output() {
try {
String fileName = title + ".html";
Writer writer = new FileWriter(fileName);
writer.write(this.makeHtml());
writer.close();
System.out.println(fileName + "を作成しました。");
} catch (IOException e) {
e.printStackTrace();
}
}
public abstract String makeHtml();
}
package factory;
import java.util.ArrayList;
/**
* 複数のLinkやTrayを集めてひとまとまりにしたものを表すクラス
*
*/
public abstract class Tray extends Item{
protected ArrayList<Item> tray = new ArrayList<>();
public Tray(String caption) {
super(caption);
}
public void add(Item item) {
tray.add(item);
}
}
AbstractFactory(抽象的な工場)の役
AbstractFactory役はAbstractProduct役のインスタンスを作り出すためのインターフェースを定める。
package factory;
/**
* クラス名を指定して具体的な工場のインスタンを作成するクラス
*
*/
public abstract class Factory {
public static Factory getFactory(String className) {
Factory factory = null;
try {
factory = (Factory)Class.forName(className).newInstance();
} catch (ClassCastException e) {
System.out.println("クラス " + className + "が見つかりません。");
} catch (Exception e) {
e.printStackTrace();
}
return factory;
}
public abstract Link createLink(String caption, String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title, String author);
}
Client(依頼者)の役
Client役はAbstractFactory役とAbstractProduct役のインターフェースだけを使って仕事を行う役。
Client役は具体的な部品や製品や工場については知らない。
import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;
public class Main {
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Usage: java Main class.mame.of.ConcreateFactory");
System.out.println("Example 1: java Main listfactory,ListFactory");
System.out.println("Example 2: java Main tablefactory,TableFactory");
System.exit(0);
}
Factory factory = Factory.getFactory(args[0]);
Link asahi = factory.createLink("朝日新聞", "http://www.asahi.com/");
Link yomiuri = factory.createLink("読売新聞", "http://www.yomiuri.co.jp/");
Link usYahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
Link jpYahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.co.jp");
Link excite = factory.createLink("Excite", "http://www.excite.com/");
Link google = factory.createLink("Google", "http://www.google.com/");
Tray trayNews = factory.createTray("新聞");
trayNews.add(asahi);
trayNews.add(yomiuri);
Tray trayYahoo = factory.createTray("Yahoo!");
trayYahoo.add(usYahoo);
trayYahoo.add(jpYahoo);
Tray traySearch = factory.createTray("サーチエンジン");
traySearch.add(trayYahoo);
traySearch.add(excite);
traySearch.add(google);
Page page = factory.createPage("LinkPage", "田中 太郎");
page.add(trayNews);
page.add(traySearch);
page.output();
}
}
ConcreteProduct(具体的な製品)の役
ConcteteProduct役ではAbstract役のインターフェース(API)を実装する。
package listFactory;
import factory.Link;
public class ListLink extends Link{
public ListLink(String caption, String url) {
super(caption, url);
}
public String makeHtml() {
return " <li><a href=\"" + url + "\">" + caption + "</a></li>\n";
}
}
package listFactory;
import java.util.Iterator;
import factory.Item;
import factory.Page;
public class ListPage extends Page{
public ListPage(String title, String author) {
super(title, author);
}
@Override
public String makeHtml() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html><head><title>" + title + "</title></head>\n");
buffer.append("<body>\n");
buffer.append("<h1>" + title + "</h1>\n");
buffer.append("<ul>\n");
Iterator<Item> it = content.iterator();
while (it.hasNext()) {
Item item = it.next();
buffer.append(item.makeHtml());
}
buffer.append("</ul>\n");
buffer.append("<hr><address>" + author + "</address>");
buffer.append("</body></html>\n");
return buffer.toString();
}
}
package listFactory;
import java.util.Iterator;
import factory.Item;
import factory.Tray;
public class ListTray extends Tray {
public ListTray(String caption) {
super(caption);
}
public String makeHtml() {
StringBuffer buffer = new StringBuffer();
buffer.append("<li>\n");
buffer.append(caption + "\n");
buffer.append("<ul>\n");
Iterator<Item> it = tray.iterator();
while (it.hasNext()) {
Item item = it.next();
buffer.append(item.makeHtml());
}
buffer.append("</ul>\n");
buffer.append("</li>\n");
return buffer.toString();
}
}
ConcreteFactory(具体的な工場)の役
ConcreteFactory役ではAbstractFactory役のインターフェース(API)を定める。
package listFactory;
import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;
public class ListFactory extends Factory {
@Override
public Link createLink(String caption, String url) {
return new ListLink(caption, url);
}
@Override
public Tray createTray(String caption) {
return new ListTray(caption);
}
@Override
public Page createPage(String title, String author) {
return new ListPage(title, author);
}
}
引数でtableFactory.TableFactoryを渡して実行して作成されたhtmlファイル
別の具体的な工場を追加する
工場を追加するのは簡単であった。
どのようなクラスを作り、どのようなメソッドを実装すれば良いかが明確なため。
package tableFactory;
import factory.Link;
public class TableLink extends Link {
public TableLink(String caption, String url) {
super(caption, url);
}
@Override
public String makeHtml() {
return "<td><a href=\"" + url + "\">" + caption + "</a></td>\n";
}
}
package tableFactory;
import java.util.Iterator;
import factory.Item;
import factory.Page;
public class TablePage extends Page {
public TablePage(String title, String author) {
super(title, author);
}
@Override
public String makeHtml() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html><head><title>" + title + "</title></head>\n");
buffer.append("<body>\n");
buffer.append("<h1>" + title + "</h1>\n");
buffer.append("<table width=\"80%\" border=\"3\">\n");
Iterator<Item> it = content.iterator();
while (it.hasNext()) {
Item item = it.next();
buffer.append("<tr>" + item.makeHtml() + "</tr>");
}
buffer.append("</table>\n");
buffer.append("<hr><address>" + author + "</address>");
buffer.append("</body></html>\n");
return buffer.toString();
}
}
package tableFactory;
import java.util.Iterator;
import factory.Item;
import factory.Tray;
public class TableTray extends Tray {
public TableTray(String caption) {
super(caption);
}
@Override
public String makeHtml() {
StringBuffer buffer = new StringBuffer();
buffer.append("<td>");
buffer.append("<table width=\"100%\" border=\"1\"><tr>");
buffer.append("<td bgcolor=\"#cccccc\" align=\"center\" colspan=\"" + tray.size() + "\"><b>" + caption + "</b></td>");
buffer.append("</tr>\n");
buffer.append("<tr>\n");
Iterator<Item> it = tray.iterator();
while (it.hasNext()) {
Item item = it.next();
buffer.append(item.makeHtml());
}
buffer.append("</tr></table>");
buffer.append("</td>");
return buffer.toString();
}
}
package tableFactory;
import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;
public class TableFactory extends Factory {
@Override
public Link createLink(String caption, String url) {
return new TableLink(caption, url);
}
@Override
public Tray createTray(String caption) {
return new TableTray(caption);
}
@Override
public Page createPage(String title, String author) {
return new TablePage(title, author);
}
}
Client役であるMainは全く変更せず、実行時の引数をtableFactory.TableFactoryにして実行して作成されたhtmlファイル
こちらを参考にさせていただきました。
増補改訂版Java言語で学ぶデザインパターン入門