구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 패턴
즉, 관련성있는 여러 종류의 객체를 일관된 방식으로 생성하는 경우에 유용하다
싱글톤 패턴, 팩토리 메서드 패턴을 사용
생성(Creational)패턴의 하나 - 객체 생성에 관련된 패턴이라는 뜻
즉, 부품의 구체적인 구현에는 주목하지 않고 인터페이스에 주목해 부품을 조립하고 제품으로 완성
AbstractProduct(추상적인 제품) - 제품의 공통 인터페이스
AbstractFactory(추상적인 공장) - 실제 팩토리 클래스의 공통 인터페이스
Client(의뢰자) - AbstractFactory와 AbstractProduct만을 사용해 주어진 역할을 실행
ConcreteFactory(구체적인 공장) - 구체적인 팩토리 클래스로 AbstractFactory 클래스의 추상 메서드를 오버라이드함으로써 구체적인 제품을 생성한다.
ConcreteProduct(구체적인 제품) - ConcreteFactory에서 생성되는 구체적인 제품
이름반 봐서는 두 패턴이 비슷해보이지만, 명확한 차이점이 있다
package factory;
public abstract class Item {
protected String caption; // 목차를 의미
public Item(String caption) {
this.caption = caption;
}
public abstract String makeHTML();
}
package factory;
public abstract class Link extends Item {
protected String url;
public Link(String caption, String url) {
super(caption);
this.url = url;
}
}
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);
}
}
package factory;
import java.io.*;
import java.util.ArrayList;
public abstract class Page {
protected String title;
protected String author;
protected ArrayList 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();
}
getFactory()는 클래스의 이름을 지정해서 구체적인 공장(ConcreteFactory)의 인스턴스를 작성
getFactory 안에서는 Class 클래스의 forName 메소드를 사용해서, 그 클래스를 동적으로 읽는다
newInstance 메소드를 이용해서, 그 클래스의 인스턴스를 한 개 작성하고 이것이 getFactory()의 반환값이 된다
주의해야할건 getFactory()안에는 ConcreteFactory의 인스턴스를 만들지만 반환값은 추상적인 공장이라는 점
package factory;
public abstract class Factory {
public static Factory getFactory(String classname) { // 구체적인 공장의 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;
}
// 세개의 시그니처 메소드는 각 추상적인 공장에서 부품이나 제품을 작성할때 이용하는 메소드
public abstract Link createLink(String caption, String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title, String author);
}
import
되어있는게 factory
패키지뿐인 점에서 알 수 있듯이 구체적인 부품, 제품, 공장을 전혀 이용하지 않는다import factory.*;
public class Main {
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Usage: java Main class.name.of.ConcreteFactory");
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 joins = factory.createLink("중앙일보", "http://www.joins.com/");
Link chosun = factory.createLink("조선일보", "http://www.chosun.com/");
Link us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
Link kr_yahoo = factory.createLink("Yahoo!Korea", "http://www.yahoo.co.kr/");
Link excite = factory.createLink("Excite", "http://www.excite.com/");
Link google = factory.createLink("Google", "http://www.google.com/");
Tray traynews = factory.createTray("신문"); // 신문 트레이
traynews.add(joins);
traynews.add(chosun);
Tray trayyahoo = factory.createTray("Yahoo!"); // 야후 트레이
trayyahoo.add(us_yahoo);
trayyahoo.add(kr_yahoo);
Tray traysearch = factory.createTray("검색엔진"); // 검색엔진 트레이
traysearch.add(trayyahoo); // link뿐 아니라 tray도 들어가는 모습
traysearch.add(excite);
traysearch.add(google);
Page page = factory.createPage("LinkPage", "영진닷컴");
page.add(traynews);
page.add(traysearch);
page.output();
}
}
package listfactory;
import factory.*;
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);
}
}
package listfactory;
import factory.*;
public class ListLink extends Link {
public ListLink(String caption, String url) {
super(caption, url);
}
@Override
public String makeHTML() {
return " <li><a href=\"" + url + "\">" + caption + "</a></li>\n";
}
}
package listfactory;
import factory.*;
import java.util.Iterator;
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(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();
}
}
<address>
태그를 사용해 표현하고 있다.ul
태그 안에 둬서 while문 안에서 append되는 item.makeHTML()
의 출력결과가 ul
안에 들어가는것을 전제로한다package listfactory;
import factory.*;
import java.util.Iterator;
public class ListPage extends Page {
public ListPage(String title, String author) {
super(title, author);
}
@Override
public String makeHTML() {
StringBuffer sb = new StringBuffer();
sb.append("<html><head><title>" + title + "</title></head>\n");
sb.append("<body>\n");
sb.append("<h1>" + title + "</h1>\n");
sb.append("<ul>\n");
sb it = content.iterator();
while (it.hasNext()) {
Item item = (Item)it.next();
sb.append(item.makeHTML());
}
sb.append("</ul>\n");
sb.append("<hr><address>" + author + "</address>");
sb.append("</body></html>\n");
return sb.toString();
}
}
단순히 HTML 링크 페이지를 만드는 것이 목적이라면 너무 거창하다.
구체적인 공장이 하나뿐이라면 추상적/구체적으로 나눌 필요가 전혀 없다.
그래서 아래에서는 예시에 다른 구체적인 공장을 추가해보자(다른 형식의 html)
ListFactory 패키지는 <ul>
을 하용한 디자인, 이번에는 css를 사용한 디자인을 한다
package divfactory;
import factory.*;
public class DivFactory extends Factory {
@Override
public Link createLink(String caption, String url) {
return new DivLink(caption, url);
}
@Override
public Tray createTray(String caption) {
return new DivTray(caption);
}
@Override
public Page createPage(String title, String author) {
return new DivPage(title, author);
}
}
package divfactory;
import factory.*;
public class DivLink extends Link {
public DivLink(String caption, String url) {
super(caption, url);
}
@Override
public String makeHTML() {
return "<td><a href=\"" + url + "\">" + caption + "</a></td>\n";
}
}
package divfactory;
import factory.*;
import java.util.Iterator;
public class DivTray extends Tray {
public DivTray(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 it = tray.iterator();
while (it.hasNext()) {
Item item = (Item)it.next();
buffer.append(item.makeHTML());
}
buffer.append("</tr></table>");
buffer.append("</td>");
return buffer.toString();
}
}
package tablefactory;
import factory.*;
import java.util.Iterator;
public class DivPage extends Page {
public DivPage(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 it = content.iterator();
while (it.hasNext()) {
Item 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();
}
}
예를 들어 factory 패키지에 화상을 표시하는 Picture라는 부품을 추가했다고 가정한다. 그러면 이미 존재하는 구체적인 공장 전부에 Picture에 대응하는 수정을 해야한다. 이미 만들어진 구체적인 공장이 많으면 많을수록 힘든 작업이 된다