개요

 앞서 배운 FactoryMethod 패턴과 TemplateMethod 패턴의 개념이 겹치는 AbstractFactory, 추상적 공장 이란 이름의 패턴에 대해 정리해본다.

 AbstractFactory패턴을 한마디로 말하자면 추상적인 공장에서는 추상적인 제품을 만드는데, 추상적인 부품들을 사용해서 만든다. 말이 복잡해 보이지만 앞선 패턴들을 학습하면 이게 무슨말인지 대충 느낌이 온다.

 

 제품을 만드는 공장도 추상화 되어있고, 부품도 추상화 되어 있으며, 완성품도 추상화 되어 있다. 이에 대응하는 하위 클래스에서 구체적인 공장, 부품, 완성품 클래스를 구현한 후 사용자의 입력에 따라 동적으로 구체적인 공장, 부품, 완성품이 선택되도록 할 수 있다.

 

 예제에서 보면 사용자의 입력을 받는 Client(Main 클래스)에서는 어떠한 하위 클래스의 이름도 코드에 포함되지 않는다. 사용자의 입력(Commend)에 따라 적절한 하위 클래스(List 부품과 완성품, 공장)가 선택된다.

 

 

역할

AbstractProduct 역할

  팩토리로 만들어지는 부품이나 완성품의 인터페이스를 결정한다. 예제에서는 Page(완성품), Link(부품), Tray(부품) 클래스.

 

AbstractFactory 역할

  완성품 인스턴스를 생성하기 위한 인터페이스를 결정한다. 예제에서는 Factory클래스.

 

Client 역할

  사용자로써 AbstractProduct와 AbstractFactory가 정의한 인터페이스 만으로 동작한다. 구체화 완성품이나 공장이 어떻게 동작하는지 알 필요가 없다.

 

ConcreteProduct 역할

  구체적으로 부품이나 완성품의 구현한다. 예제에서는 ListPage, ListLink, ListTray 클래스.

 

ConcreteFactory 역할

  구체적으로 공장을 구현한다. 예제에서는 ListFactory 클래스.

 

 

이득 및 주의사항

 구체적인 공장, 완성품, 부품 등을 추가하고 사용자가 외부에서 선택만 할 수 있도록 함으로써 사용자는 기존의 인터페이스를 벗어나지 않고 새로운 공장을 쉽게 선택 할 수 있다. 개발자도 Client(Main 클래스)나 추상화 되어 있는 공장과 제품은 손대지 않아도 되기에 비용을 줄일 수 있다.

 

 단, 기존의 공장 - 부품 - 완성품 단계에서 부품을 추가하려 한다면 기존의 모든 구체화된 공장에 새로운 부품을 추가해줘야만 하기 때문에 많은 비용이 소모됨을 주의해야 한다.

 

 예제에서 보면 공장이 되는 Factory 클래스에서는 완성품(Item 클래스)의 인스턴스로 item.makeHTML() 메서드를 호출하는 것만으로 부품이 되는 Link 클래스와 Tray 클래스, 완성품이 되는 Page 클래스의 makeHTML() 까지 모두 호출이 된다. 이 부분이 객체지향 프로그래밍이 무엇인지 보여주는 부분인데, 만약 item 인스턴스의 내용물을 검색해서 이를 조건으로 동작하게 한다면 이는 객체지향적 프로그래밍이라 하기에 적합하지 않다. 

 이러한 객체지향적인 접근은 사실 자연스럽게 익혀져 있는데, 예를 들어 우리가 어떤 Collection 인스턴스를 다룬다고 생각할 때, 해당 인스턴스가 저장하고 있는 내용물이 무엇인지 확인하는 코드를 작성하지 않는 것과 같다. 그저 해당 인스턴스가 갖고 있는 메서드를 동작시키기만 하면 되는 것이다.

 

 

예제 코드

//예제 Object
package abstractFactory.factory;

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


//AbstractProduct1 완성품
package abstractFactory.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 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();
}


//AbstractProduct2 부품
package abstractFactory.factory;

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


//AbstractProduct3 부품
package abstractFactory.factory;

import java.util.ArrayList;

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
package abstractFactory.factory;

public abstract class Factory {
	public static Factory getFactory(String classname) {
		Factory factory = null;
		try {
			factory = (Factory)Class.forName(classname).newInstance();
		} catch(ClassNotFoundException 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
package abstractFactory;

import abstractFactory.factory.*;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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 naver = factory.createLink("네이버", "http://www.naver.com/");
		Link google = factory.createLink("구글", "http://www.google.com/");
		
		Tray traynews = factory.createTray("신문");
		traynews.add(joins);
		traynews.add(chosun);
		
		
		Tray traysearch = factory.createTray("검색엔진");
		traysearch.add(naver);
		traysearch.add(google);
		
		Page page = factory.createPage("LinkePage", "영진닷컴");
		page.add(traynews);
		page.add(traysearch);
		page.output();
		
	}

}


//ConcreteProduct1 완성품
package abstractFactory.listfactory;

import java.util.Iterator;

import abstractFactory.factory.Item;
import abstractFactory.factory.Page;

public class ListPage extends Page{
	public ListPage(String title, String author) {
		super(title, author);
	}
	
	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 = (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();
	}
}


//ConcreteProduct2 부품
package abstractFactory.listfactory;

import abstractFactory.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";
	}
}


//ConcreteProduct3 부품
package abstractFactory.listfactory;

import java.util.Iterator;

import abstractFactory.factory.Item;
import abstractFactory.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 = (Item)it.next();
			buffer.append(item.makeHTML());
		}
		buffer.append("</ul>\n");
		buffer.append("</li>\n");
		
		return buffer.toString();
	}
}


//ConcreteFactory
package abstractFactory.listfactory;

import abstractFactory.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) {
		// TODO Auto-generated method stub
		return new ListTray(caption);
	}

	@Override
	public Page createPage(String title, String author) {
		// TODO Auto-generated method stub
		return new ListPage(title, author);
	}
	
}

'디자인 패턴' 카테고리의 다른 글

10. Strategy 패턴  (0) 2020.12.09
9. Bridge 패턴  (0) 2020.12.08
7. Builder 패턴  (0) 2020.12.04
6. Prototype 패턴  (0) 2020.12.03
5. Singleton 패턴  (0) 2020.12.02

+ Recent posts