デザインパターン ~Abstract Factory~
設計
1. はじめに
GoFのデザインパターンにおける、Abstract Factoryパターンについてまとめます。
2. Abstract Factoryパターンとは
- Abstract Factoryは、抽象的な工場という意味になります。
- 抽象的とは、具体的にどのように実装されているかについては考えず、インターフェースだけに注目している状態のことです。
- Abstract Factoryパターンは、部品の具体的な実装には注目せず、インターフェースに注目します。そしてそのインターフェースだけを使って、部品を組み立て、製品にまとめる方式です。
- GoFのデザインパターンでは、生成に関するデザインパターンに分類されます。
3. サンプルクラス図
4. サンプルプログラム
お気に入り一覧を、HTML形式で出力するプログラムです。
4-1. Factoryクラス
抽象的な工場を表すを行うクラスです。Link、Tray、Pageを作成します。
package factory;
public abstract class Factory {
public abstract Link createLink(String caption, String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title);
public static Factory getFactory(String classname) {
Factory factory = null;
try {
factory = (Factory) Class.forName(classname).newInstance();
} catch (ClassNotFoundException e) {
System.err.println("クラス " + classname + " が見つかりません。");
} catch (Exception e) {
e.printStackTrace();
}
return factory;
}
}
4-2. Itemクラス
LinkとTrayを統一的に扱うクラスです。
package factory;
public abstract class Item {
protected String caption;
public Item(String caption) {
this.caption = caption;
}
public abstract String makeHTML();
}
4-3. Linkクラス
抽象的な部品:HTMLリンクを表すクラスです。
package factory;
public abstract class Link extends Item {
protected String url;
public Link(String caption, String url) {
super(caption);
this.url = url;
}
}
4-4. Trayクラス
抽象的な部品:LinkやTrayを集めたクラスです。
package factory;
import java.util.ArrayList;
public abstract class Tray extends Item {
protected ArrayList tray = new ArrayList();
public Tray(String caption) {
super(caption);
}
public void add(Item item) {
tray.add(item);
}
}
4-5. Pageクラス
抽象的な部品:HTMLページを表すクラスです。
package factory;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
public abstract class Page {
protected String title;
protected ArrayList content = new ArrayList();
public Page(String title) {
this.title = title;
}
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();
}
4-6. ListFactoryクラス
具体的な工場を表すクラスです。ListLink、ListTray、ListPageを作成します。
package listfactory;
import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;
public class ListFactory extends Factory {
public Link createLink(String caption, String url) {
return new ListLink(caption, url);
}
public Tray createTray(String caption) {
return new ListTray(caption);
}
public Page createPage(String title) {
return new ListPage(title);
}
}
4-7. ListLinkクラス
具体的な部品:HTMLリンクを表すクラスです。
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";
}
}
4-8. ListTrayクラス
具体的な部品:LinkやTrayを集めたクラスです。
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 it = tray.iterator();
while (it.hasNext()) {
Item item = (Item) it.next();
buffer.append(item.makeHTML());
}
buffer.append("</ul>\n");
buffer.append("</li>\n");
return buffer.toString();
}
}
4-9. ListPageクラス
具体的な部品:HTMLページを表すクラスです。
package listfactory;
import java.util.Iterator;
import factory.Item;
import factory.Page;
public class ListPage extends Page {
public ListPage(String title) {
super(title);
}
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 it = content.iterator();
while (it.hasNext()) {
Item item = (Item) it.next();
buffer.append(item.makeHTML());
}
buffer.append("</ul>\n");
buffer.append("</body></html>\n");
return buffer.toString();
}
}
4-10. Mainクラス
メイン処理を行うクラスです。
import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;
public class Main {
public static void main(String[] args) {
Factory factory = Factory.getFactory("listfactory.ListFactory");
Link qiita = factory.createLink("Qiita", "https://qiita.com//");
Link dot = factory.createLink("ドットインストール", "https://dotinstall.com/");
Link yahoo = 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 pgTray = factory.createTray("プログラミング");
pgTray.add(qiita);
pgTray.add(dot);
Tray searchTray = factory.createTray("検索サイト");
searchTray.add(yahoo);
searchTray.add(excite);
searchTray.add(google);
Page page = factory.createPage("お気に入り");
page.add(pgTray);
page.add(searchTray);
page.output();
}
}
4-11. 実行結果
5. メリット
例えば、サンプルプログラムに新たな具体的な工場を追加する場合、Factory、Link、Tray、Pageのサブクラス作り、それぞれの抽象メソッドを実装することになります。つまり、factoryパッケージのクラスが持っている抽象的な部分を具体化していくだけになります。このとき、いくら具体的な工場を追加しても、抽象的な工場を修正する必要がありません。
6. GitHub
7. デザインパターン一覧
8. 参考
今回の記事、及びサンプルプログラムは、以下の書籍を元に作成させて頂きました。
大変分かりやすく、勉強になりました。感謝申し上げます。 デザインパターンやサンプルプログラムについての説明が詳細に書かれていますので、是非書籍の方もご覧ください。