글
디자인 패턴중 Creational Pattern 타입의 차이점에 대해서...
프로그램 개발
2008/07/12 22:14
디자인 패턴을 공부하면서 가장 쉽게 자기 프로그램에 적용할 만한 것이 생성 패턴들 이라고 생각된다. 아무래도 프로그램의 로직에 연관을 둔 패턴이 아니고, 생성 방법에 대한 고찰이기 때문이라고 생각하는데...
뭐 하여튼 많은 생성 패턴이 있지만 DP책을 보고 공부하다 보면 이 생성패턴이 저 생성패턴이랑 엄청 비슷한데 왜 차이를 둔거지 하는 생각이 사실 들기도 한다.
GoF Design Patterns 책을 보면 생성 패턴을 Abstract Factory, Builder, Factory Method, Prototype, Singleton 이상 5가지로 구분해서 소개한다. 이중에서 Singleton, Prototype 은 다른 생성 패턴과는 사용예가 조금 상이한 패턴인 관계로 공부하면서 헷갈린다라고 생각하기는 힘들다. 반면, 다른 3가지 경우는 상당히 유사하기 때문에 차이점을 확실하게 해두지 않으면 두고두고 헷갈리는 패턴들이다.
코드를 가지고 비교해보자.
※ 코드는 wikipedia.org 에서 발췌함.
* Abstract Factory
차이점이라는 것은 대충 이렇다.
한개의 피자를 만든다고 생각해보자. (헤드 퍼스트 DP를 보면 대부분의 생성 패턴 이야기는 피자 가게로 풀어나간다;;)
피자를 만드는 과정을 생각해보자.
일단 재료를 공수하는게 무엇보다 중요할 것이다. 요즘 인기 있는 드라마 식객에서 김래원씨가 말한 이야기도 있듯이 좋은 재료는 그 자체만으로도 좋은 음식이라고 했으니 무엇보다 재료를 어떤 것을 구하는 가가 굉장히 중요한 요소일 것이다.
그 다음으로 중요한 것은 무엇일까? 아마 레시피라고 보통 말하는 피자를 만드는 특유의 방법이 아닐까 생각한다. 물건을 만드는 방법도 이와 다르지 않아서 당연히 만드는 방법에 차이가 있다면 최종 결과물 자체도 많은 차이점이 있을 것이라고 생각한다.
자, 그럼 피자를 만들어서 먹기만할까? 꼭 그런것은 아니라고 생각한다. 분명히 완제품으로 나오는 냉동 식품도 많지 않을까? 아니면 도미노 피자를 먹던지 피자헛의 피자를 시켜먹던지 하는 방법도 있을 것이다.
물건을 만드는 이런 과정들을 이용해서 3가지의 생성패턴을 구분하는 것이 가능하다.
일단 Abstract Factory 패턴의 경우는 물건을 만드는데 들어가는 재료를 만드는 과정을 추상화시킨 것이라고 표현할 수 있다. 위의 코드상의 예제에서 보듯 Application 을 구성하는 버튼을 생성하기 위해서 팩토리를 이용했다는 사실을 알 수 있다. 이 경우에는 생성되는 재료의 종류를 덩어리채 결정할 수 있다는 점이 좀 특이한 점이라고 생각해야한다. 코드의 예에서 보듯 윈도우 시스템에는 윈도우 팩토리가, 맥 시스템에서는 팩 팩토리 클래스가 관여하면서 제품 구성에 필요한 재료 집합을 완전히 바꾸어 버린다는 것을 알 수 있다.
그럼 물건을 만드는 과정을 추상화하는 것은 무엇일까? 이는 Factory Method 패턴을 이용하는 것이라고 볼 수 있다. 위의 코드 경우 이미지를 읽어들이는 클래스를 만들는 과정에서 어떤 이미지를 읽어들이는 지를 패턴안에서 결정한다는 점이 특이하다.
그럼 Builder 패턴은 무엇인가? 이것은 아예 패턴 자체가 물건을 만드는 과정과 재료를 결정해놓고 최종 결과물만을 뱉어낸 다는 점이 중요하다. 피자로 치면 말그대로 도미노 피자, 피자헛 피자의 제품 한개를 주문해서 시키는 행위와 비슷하다고 할 수 있다.
패턴이 워낙에 비슷해서... -_-;; 사실 글을 적는 와중에도 헷갈리는데... 어쨋든 확실한건 3가지 패턴이 분명히 다르다는 점이고, 제가 적어놓은 설명보다는 위의 슬라이드 한장이 더욱 이해가 확실할 지도 모르겠군요 ㄷㄷ.
뭐 하여튼 많은 생성 패턴이 있지만 DP책을 보고 공부하다 보면 이 생성패턴이 저 생성패턴이랑 엄청 비슷한데 왜 차이를 둔거지 하는 생각이 사실 들기도 한다.
GoF Design Patterns 책을 보면 생성 패턴을 Abstract Factory, Builder, Factory Method, Prototype, Singleton 이상 5가지로 구분해서 소개한다. 이중에서 Singleton, Prototype 은 다른 생성 패턴과는 사용예가 조금 상이한 패턴인 관계로 공부하면서 헷갈린다라고 생각하기는 힘들다. 반면, 다른 3가지 경우는 상당히 유사하기 때문에 차이점을 확실하게 해두지 않으면 두고두고 헷갈리는 패턴들이다.
코드를 가지고 비교해보자.
※ 코드는 wikipedia.org 에서 발췌함.
* Abstract Factory
/** Factory Method
* GUIFactory example
*/
abstract class GUIFactory {
public static GUIFactory getFactory() {
int sys = readFromConfigFile("OS_TYPE");
if (sys == 0) {
return new WinFactory();
} else {
return new OSXFactory();
}
}
public abstract Button createButton();
}
class WinFactory extends GUIFactory {
public Button createButton() {
return new WinButton();
}
}
class OSXFactory extends GUIFactory {
public Button createButton() {
return new OSXButton();
}
}
abstract class Button {
public abstract void paint();
}
class WinButton extends Button {
public void paint() {
System.out.println("I'm a WinButton: ");
}
}
class OSXButton extends Button {
public void paint() {
System.out.println("I'm an OSXButton: ");
}
}
public class Application {
public static void main(String[] args) {
GUIFactory factory = GUIFactory.getFactory();
Button button = factory.createButton();
button.paint();
}
// Output is either:
// "I'm a WinButton:"
// or:
// "I'm an OSXButton:"
}
public class ImageReaderFactory
{
public static ImageReader getImageReader( InputStream is )
{
int imageType = figureOutImageType( is );
switch( imageType )
{
case ImageReaderFactory.GIF:
return new GifReader( is );
case ImageReaderFactory.JPEG:
return new JpegReader( is );
// etc.
}
}
}
* Builderpublic interface ImageReader
{
public DecodedImage getDecodedImage();
}
public class GifReader implements ImageReader
{
public GifReader( InputStream in )
{
// check that it's a gif, throw exception if it's not, then if it is
// decode it.
}
public DecodedImage getDecodedImage()
{
return decodedImage;
}
}
public class JpegReader implements ImageReader
{
//....
}
사실 코드를 놓고서 보아도 생성하는 방식에 차이는 대동소이하기 때문에 정확히 구분을 짓고서 코드를 보는게 더 좋을지도 모르겟다./** "Product" */
class Pizza {
private String dough = "";
private String sauce = "";
private String topping = "";
public void setDough(String dough) {
this.dough = dough;
}
public void setSauce(String sauce) {
this.sauce = sauce;
}
public void setTopping(String topping) {
this.topping = topping;
}
}
/** "Abstract Builder" */
abstract class PizzaBuilder {
protected Pizza pizza;
public Pizza getPizza() {
return pizza;
}
public void createNewPizzaProduct() {
pizza = new Pizza();
}
public abstract void buildDough();
public abstract void buildSauce();
public abstract void buildTopping();
}
/** "ConcreteBuilder" */
class HawaiianPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("cross");
}
public void buildSauce() {
pizza.setSauce("mild");
}
public void buildTopping() {
pizza.setTopping("ham+pineapple");
}
}
/** "ConcreteBuilder" */
class SpicyPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("pan baked");
}
public void buildSauce() {
pizza.setSauce("hot");
}
public void buildTopping() {
pizza.setTopping("pepperoni+salami");
}
}
/** "Director" */
class Cook {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(PizzaBuilder pb) {
pizzaBuilder = pb;
}
public Pizza getPizza() {
return pizzaBuilder.getPizza();
}
public void constructPizza() {
pizzaBuilder.createNewPizzaProduct();
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildTopping();
}
}
/** A given type of pizza being constructed. */
public class BuilderExample {
public static void main(String[] args) {
Cook cook = new Cook();
PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
cook.setPizzaBuilder(hawaiianPizzaBuilder);
cook.constructPizza();
Pizza pizza = cook.getPizza();
}
}
차이점이라는 것은 대충 이렇다.
한개의 피자를 만든다고 생각해보자. (헤드 퍼스트 DP를 보면 대부분의 생성 패턴 이야기는 피자 가게로 풀어나간다;;)
피자를 만드는 과정을 생각해보자.
일단 재료를 공수하는게 무엇보다 중요할 것이다. 요즘 인기 있는 드라마 식객에서 김래원씨가 말한 이야기도 있듯이 좋은 재료는 그 자체만으로도 좋은 음식이라고 했으니 무엇보다 재료를 어떤 것을 구하는 가가 굉장히 중요한 요소일 것이다.
그 다음으로 중요한 것은 무엇일까? 아마 레시피라고 보통 말하는 피자를 만드는 특유의 방법이 아닐까 생각한다. 물건을 만드는 방법도 이와 다르지 않아서 당연히 만드는 방법에 차이가 있다면 최종 결과물 자체도 많은 차이점이 있을 것이라고 생각한다.
자, 그럼 피자를 만들어서 먹기만할까? 꼭 그런것은 아니라고 생각한다. 분명히 완제품으로 나오는 냉동 식품도 많지 않을까? 아니면 도미노 피자를 먹던지 피자헛의 피자를 시켜먹던지 하는 방법도 있을 것이다.
물건을 만드는 이런 과정들을 이용해서 3가지의 생성패턴을 구분하는 것이 가능하다.
일단 Abstract Factory 패턴의 경우는 물건을 만드는데 들어가는 재료를 만드는 과정을 추상화시킨 것이라고 표현할 수 있다. 위의 코드상의 예제에서 보듯 Application 을 구성하는 버튼을 생성하기 위해서 팩토리를 이용했다는 사실을 알 수 있다. 이 경우에는 생성되는 재료의 종류를 덩어리채 결정할 수 있다는 점이 좀 특이한 점이라고 생각해야한다. 코드의 예에서 보듯 윈도우 시스템에는 윈도우 팩토리가, 맥 시스템에서는 팩 팩토리 클래스가 관여하면서 제품 구성에 필요한 재료 집합을 완전히 바꾸어 버린다는 것을 알 수 있다.
그럼 물건을 만드는 과정을 추상화하는 것은 무엇일까? 이는 Factory Method 패턴을 이용하는 것이라고 볼 수 있다. 위의 코드 경우 이미지를 읽어들이는 클래스를 만들는 과정에서 어떤 이미지를 읽어들이는 지를 패턴안에서 결정한다는 점이 특이하다.
그럼 Builder 패턴은 무엇인가? 이것은 아예 패턴 자체가 물건을 만드는 과정과 재료를 결정해놓고 최종 결과물만을 뱉어낸 다는 점이 중요하다. 피자로 치면 말그대로 도미노 피자, 피자헛 피자의 제품 한개를 주문해서 시키는 행위와 비슷하다고 할 수 있다.
패턴이 워낙에 비슷해서... -_-;; 사실 글을 적는 와중에도 헷갈리는데... 어쨋든 확실한건 3가지 패턴이 분명히 다르다는 점이고, 제가 적어놓은 설명보다는 위의 슬라이드 한장이 더욱 이해가 확실할 지도 모르겠군요 ㄷㄷ.