노무현 대통령 배너

Factory Method Pattern 팩토리 메서드 패턴을 액션스크립트로 컨버팅 – Head First Design Pattern

by on 5.27, 2009, under Design Pattern

nystylepizza헤드퍼스트 디자인 패턴의 제 4 장의 내용은 팩토리 패턴 입니다. 이전 포스트에서 심플 팩토리로 워밍업 했었는데요, 형태 자체는 거의 동일하므로 심플 팩토리를 읽고 이 포스트를 읽는다면 보다 확실한 이해를 할 수 있으리라 생각합니다.

우선 팩토리 메서드 패턴의 정의를 보도록 하죠.

“팩토리 메서드 패턴 – 팩토리 메서드 패턴에서는 객체를 생성하기 위한 인터페이스[01] 를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만듭니다. 팩토리 메서드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것이죠.”

피자 프랜차이즈가 분점을 내게 되었습니다. 뉴욕과 시카고는 서울과 대구 양키스와 레드삭스 만큼이나 달라서 피자에 들어가는 재료가 꽤나 다릅니다. 실제로 업무에서도 객체를 생산하는 구조는 동일하지만, 구상 클래스들의 형식이 달라서 그에따른 카테고리를 나눠서 객체를 생성해야 하는때가 빈번하게 생기는데 바로 그런 경우에 팩토리 메서드 패턴을 사용하면 좋을것 같습니다.

추상층 부터 보겠습니다.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package
{
	public class PizzaStore
	{
		public function createPizza( $item:String ):Pizza
		{
			throw new Error( "createPizza( $item:String ) 추상메서드" )
		}
 
		public function orderPizza( $type:String ):Pizza
		{
			//Simple Factory에서는 factory.createPizza( $type ) 로 만들었지만 여기서는 추상메서드를 통해 피자를 만듬
			var pizza:Pizza = createPizza( $type );
			pizza.prepare();
			pizza.bake();
			pizza.cut();
			pizza.box();
 
			return pizza;
		}
	}
}

심플 팩토리의 PizzaStore 에서는 SimplePizzaFactory 객체를 만들고 그 객체의 레퍼런스를 이용해 createPizza( “{피자종류}” ) 를 직접 호출하는 방식을 취했지만, 팩토리 메서드 패턴에서는 위에서 보시는 바와 같이 피자를 만드는 메서드를 같은 객체에 있는 createPizza() 추상 메서드를 호출합니다. 뉴욕 피자를 만들것인지 시카고 피자를 만들것인지는 추상층에서는 알고 있을 필요가 없습니다. (알 수 없습니다.)

그럼 심플 팩토리에서 SimplePizzaFactory 클래스 역할을 하게되는 ChicagoPizzaStore[02] 를 보겠습니다.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package
{
	public class ChicagoPizzaStore extends PizzaStore
	{
		public override function createPizza( $item:String ):Pizza
		{
			var pizza:Pizza = null;
 
			if ( $item == "cheese" )
			{
				pizza = new ChicagoStyleCheesePizza()
			}
			else if ( $item == "pepperoni" )
			{
				pizza = new ChicagoStylePepperoniPizza()
			}
			else if ( $item == "clam" )
			{
				pizza = new ChicagoStyleClamPizza()
			}
			else if ( $item == "veggie" )
			{
				pizza = new ChicagoStyleVeggiePizza()
			}
 
			return pizza;
		}
	}
}

클래스 이름만 바뀌었을 뿐 심플 팩토리와 완전히 동일하군요. 다만 심플 팩토리와는 달리 PizzaStore 클래스를 확장(상속)하고 createPizza() 메서드가 override 되었습니다.

그럼 시카고 분점의 페퍼로니 피자는 어떻게 만드는지 볼 차례가 되었군요.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package
{
	public class ChicagoStylePepperoniPizza extends Pizza
	{
		public function ChicagoStylePepperoniPizza()
		{
			name = "시카고 스타일 페퍼로니피자";
			dough = "익스트라 크러스트 도우";
			sauce = "플럼 토마토 소스";
			toppings.push( "으깬 모짜렐라 치즈" )
			toppings.push( "검은 올리브" )
			toppings.push( "시금치" )
			toppings.push( "가지" )
			toppings.push( "슬라이스 페퍼로니" )
		}
 
		public override function cut():void
		{
			trace( "피자를 네모난 모양으로 자름" )
		}
	}
}

이 클래스가 Pizza 클래스를 확장하였으므로 슈퍼클래스의 변수에 저장한다는 내용입니다. 특이한 내용은 없고, 시카고 스타일 피자는 격자로 잘라야 하므로 Pizza 클래스의 cut() 메서드를 오버라이드 했습니다. (사진들 보니까 그냥 보통 피자들처럼 방사형으로 자르더만…)

피자 구상 객체들의 추상층인 Pizza 클래스 역시 심플 팩토리와 완전히 동일합니다.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package
{
	public class Pizza
	{
		protected var name:String;
		protected var dough:String;
		protected var sauce:String;
		protected var toppings:Array = new Array()
 
		public function getName():String
		{
			return name;
		}
 
		public function prepare():void
		{
			var display:String = "n-----";
			display += name + "를 준비합니다 -----";
			display += "n도우 반죽중... : " + dough;
			display += "n소스 추가중... : " + sauce;
			var t:String = "";
			for (var i:uint = 0; i < toppings.length; i++ )
			{
				t += toppings[ i ].toString() + ", ";
			}
			display += ("n토핑 추가중... : " + t);
 
			trace( "n", display )
		}
 
		public function bake():void
		{
			trace( "피자를 굽습니다 :", name )
		}
 
		public function cut():void
		{
			trace( "피자를 자릅니다 :", name )
		}
 
		public function box():void
		{
			trace( "피자를 포장합니다 :", name )
		}
	}
}

이 포스트 공개설정 해 놓고 한참있다 보니 정작 호스트코드를 안넣었네요. 어쩐지 뭔가가 빠졌다 싶었습니다.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package
{
	import flash.display.Sprite;
 
	public class Main extends Sprite
	{
		public function Main():void
		{
			/*
			 * Simple Factory 에서 피자를 만드는 방식
			 * var factory:SimplePizzaFactory = new SimplePizzaFactory()
			 * var store:PizzaStore = new PizzaStore( factory )
			 * var pizza:Pizza = store.orderPizza( "cheese" )
			*/
			var nyStore:PizzaStore = new NYPizzaStore();
			var chicagoStore:PizzaStore = new ChicagoPizzaStore()
 
			var pizza:Pizza = nyStore.orderPizza( "cheese" )
			trace( "주문한 피자 나왔습니다 :", pizza.getName() )
 
			pizza = chicagoStore.orderPizza( "veggie" )
			trace( "주문한 피자 나왔습니다 :", pizza.getName() )
		}
	}
}

trace 내용은 아래와 같습니다.

—–뉴욕 스타일 치즈피자를 준비합니다 —–
도우 반죽중… : 씬 크러스트 도우
소스 추가중… : 마리나라 소스
토핑 추가중… : 깍둑썬 레지오노 치즈,
피자를 굽습니다 : 뉴욕 스타일 치즈피자
피자를 자릅니다 : 뉴욕 스타일 치즈피자
피자를 포장합니다 : 뉴욕 스타일 치즈피자
주문한 피자 나왔습니다 : 뉴욕 스타일 치즈피자

—–시카고 스타일 야채피자를 준비합니다 —–
도우 반죽중… : 익스트라 크러스트 도우
소스 추가중… : 플럼 토마토 소스
토핑 추가중… : 으깬 모짜렐라 치즈, 검은 올리브, 시금치, 가지,
피자를 굽습니다 : 시카고 스타일 야채피자
피자를 네모난 모양으로 자름
피자를 포장합니다 : 시카고 스타일 야채피자
주문한 피자 나왔습니다 : 시카고 스타일 야채피자

주석에서 보는 바와 같이 Simple Factory에서는 SimplePizzaFactory 객체를 만들어 PizzaStore() 생성자에 던져야 했지만, 팩토리 메서드에서는 각 분점의 팩토리(NYPizzaStore 등)를 만들면서 상위형으로 업캐스팅 하여 팩토리 객체 자체가 특정 분점의 팩토리를 알고 있도록 만듭니다.

시카고 스타일은 이런식!!

시카고 스타일은 이런식!!

팩토리 메서드는 이런식으로 서브클래스에서 오버라이드 된 메서드를 통해 객체 생성 작업을 서브클래스에 캡슐화 시킬 수 있습니다. 위의 모든 코드에서 객체를 생성하는 부분은 오로지 ChicagoPizzaStore 에 집중되어 있는걸 볼 수 있죠. 이렇게 하면 슈퍼클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성 코드를 서로 분리하는 효과를 가져오게 되어 유지보수나 코드 수정에 매우 유리한 구조가 되는 것입니다.

피자 사진을 찾는다고 플리커에서 시카고 스타일 피자를 검색했는데, 이건 정말 딱 제가 좋아하는 스타일의 피자네요. Chicago Style Deep Dish Pizza !!! 우리나라에서는 이런 스타일의 피자를 본적이 없는것 같습니다. 아마도 재료비가 엄청나서 만들지 않을것 같네요. (피자 먹고싶어~)

 

Factory Pattern (Abstract Methood) Example 액션스크립트 코드 다운로드 (145)
이 글을 복사해서 퍼가시는건 허용하지 않습니다. 글의 주소를 다른곳에 알려주시는 것은 환영합니다.
  1. 여기에서의 인터페이스는 interface 키워드를 사용하는 인터페이스를 지칭한다기 보다는 추상메서드도 포함하는 좀더 넓은 의미의 인터페이스를 의미합니다. []
  2. 뉴욕 분점에서는 당연히 NYPizzaStore를 이용하겠죠. []

관련된 글

:, , , , , , , , , , , , ,

2 Comments for this entry

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Meta