デザインパターン AbstractFactory:関連する部品を組み合わせる

概要

関連オブジェクト群の生成方法を抽象クラスだけで指定するパターン
具象側のオブジェクト群を入れ替える事でグループ単位での追加/修正が容易になる

クラス図


本例

抽象

AbstractProduct(製品)

package abstractFactory.factory;

public abstract class Item {
 protected String caption;
 public Item(String caption) {
  this.caption = caption;
 }
 public abstract String makeHTML();
}


package abstractFactory.factory;

public abstract class Tray extends Item {
 protected ArrayList<Item> tray = new ArrayList<Item>();

 public Tray(String caption) {
  super(caption);
 }

 public void add(Item item) {
  this.tray.add(item);
 }
}


package abstractFactory.factory;

public abstract class Link extends Item {
 public Link(String caption, String url) {
  super(caption);
  this.url = url;
 }
}


package abstractFactory.factory;

public abstract class Page {
 protected String title;
 protected String author;
 protected ArrayList<Item> content = new ArrayList<Item>();
 public Page(String title, String author) {
  this.title = title;
  this.author = author;
 }

 public void add(Item item) {
  this.content.add(item);
 }

 public void output() {
  try {
   String filename = this.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();
}

AbstractFactory(工場)

package abstractFactory.factory;

public abstract class Factory {
 public static Factory getFactory(String classname) {
  Factory factory = null;
  try {
   利用者から引数で指定されたクラス名(listfactory/ListFactory)をインスタンス化する
   factory = (Factory) Class.forName(classname).newInstance();
  } catch (InstantiationException | IllegalAccessException | ClassNotFoundException 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(依頼者)

Main起動時に引数で「listfactory/ListFactory」を指定する

package abstractFactory;

public class Main {
 public static void main(String[] args) {
  if (args.length != 1) {
   System.exit(0);
  }
  listfactory/ListFactoryのインスタンスを取得
  Factory factory = Factory.getFactory(args[0]);

  Link sankei = factory.createLink("産経新聞", "https://www.sankei.com/");
  Link yomiuri = factory.createLink("読売新聞", "http://www.yomiuri.co.jp/");
  Link yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
  Link google = factory.createLink("Google", "http://www.google.com;");

  Tray traynews = factory.createTray("新聞");
  traynews.add(sankei);
  traynews.add(yomiuri);

  Tray trayweb = factory.createTray("WEB");
  trayweb.add(yahoo);
  trayweb.add(google);

  Page page = factory.createPage("LinkPage", "株式会社米良太事務所");
  page.add(traynews);
  page.add(trayweb);
  page.output();
 }
}

結果
※ListPage部分
<html><head><title>LinkPage</title></head>
<body>
 <h1>LinkPage</h1>
 <ul>
  ※ListTray部分
  <li>
   新聞
   <ul>
    ※ListLink部分
    <li><a href="https://~">産経新聞</a></li>
    <li><a href="http://~">読売新聞</a></li>
   </ul>
  </li>
  ※ListTray部分
  <li>
   WEB
   <ul>
    ※ListLink部分
    <li><a href="http://~">Yahoo!</a></li>
    <li><a href="http://~;">Google</a></li>
   </ul>
  </li>
 </ul>
 <hr>
 <address>株式会社米良太事務所</address>
</body>
</html>

具象

ConcreteProduct(製品)

依頼者は具象クラスしか参照しない
具象クラスは別途コンパイルが必要

package abstractFactory.listfactory;

public class ListLink extends Link {
 public ListLink(String caption, String url) {
  super(caption, url);
 }

 @Override
 public String makeHTML() {
  return "<li><a href=\"" + this.url + "\">" + this.caption + "</a></li>";
  // <li><a href="this.url">this.caption</a></li>
 }
}


package abstractFactory.listfactory;

public class ListTray extends Tray {
 public ListTray(String caption) {
  super(caption);
 }

 @Override
 public String makeHTML() {
  StringBuffer buffer = new StringBuffer();
  buffer.append("<li>\n");
  buffer.append(this.caption + "\n");
  buffer.append("<ul>\n");
  Iterator<Item> 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();
 }
}


package abstractFactory.listfactory;

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>" + this.title + "</title></head>\n");
  buffer.append("<body>\n");
  buffer.append("<h1>" + this.title + "</h1>\n");
  buffer.append("<ul>\n");;
  Iterator<Item> it = this.content.iterator();
  while (it.hasNext()) {
   Item item = (Item) it.next();
   buffer.append(item.makeHTML());
   Page、Tray、ListはmakeHTMLメソッドを実装しているのでどのオブジェクトでもHTMLが生成される
  }
  buffer.append("</ul>\n");
  buffer.append("<hr><address>" + this.author + "</address>\n");
  buffer.append("</body></html>\n");
  return buffer.toString();
 }
}

ConcreteFactory(工場)

package abstractFactory.listfactory;

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);
 }
}