Post

Factory Pattern

Factory Pattern 정리 [μ±…μ—μ„œ 자주 μ–ΈκΈ‰ν•˜λŠ” λ‚΄μš©]

  • κ΅¬ν˜„μ„ λ°”νƒ•μœΌλ‘œ ν”„λ‘œκ·Έλž˜λ°ν•œλ‹€. β†’ new Object()
  • λ³€ν™”λ₯Ό λ°”νƒ•μœΌλ‘œ ν”„λ‘œκ·Έλž˜λ°ν•œλ‹€. β†’ GetObject()

상황

객체λ₯Ό μƒμ„±ν•˜λŠ” 뢀뢄이 계속 변경될 λ•Œ, μš°λ¦¬λŠ” ν”Όμž 클래슀λ₯Ό ν†΅ν•΄μ„œ ν”Όμžλ₯Ό 생성할 것인닀. ν•˜μ§€λ§Œ ν”Όμžμ˜ μ’…λ₯˜λŠ” 많고, νŒλ§€ν•˜λŠ” ν”ΌμžλŠ” 계속 β€œλ³€ν•œλ‹€.” μ•žμ—μ„œλ„ κ°•μ‘°ν•œ 원칙은 λ³€ν•˜λŠ” λΆ€λΆ„κ³Ό λ³€ν•˜μ§€ μ•ŠλŠ” 뢀뢄을 λΆ„λ¦¬ν•˜λŠ” 것이닀. νŒλ§€ν•˜λŠ” ν”Όμžκ°€ λ³€κ²½λ˜λ©΄ μ•„λž˜μ˜ λ©”μ†Œλ“œλŠ” 계속 μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
Pizza orderPizza(string type){
	Pizza pizza;
	if(type.equals("cheese"))
		pizza=new CheesePizza();
	else if(type.equals("pepperoni"))
		pizza=new PepperoniPizza();
	else if(type.equals("clam")
		pizza=new ClamPizza();
	else if(type.equals("veggie"))
		pizza=new VeggiePizza();
}
...

λ§Œμ•½, ν”Όμžκ°€κ²Œκ°€ μ—¬λŸ¬κ°œλΌλ©΄, ν”Όμžλ₯Ό μƒμ„±ν•˜λŠ” ν΄λž˜μŠ€λŠ” ν•˜λ‚˜κ°€ μ•„λ‹Œ λ‹€μˆ˜κ°€ λœλ‹€. μ΄λ•Œ μ—¬λŸ¬ 클래슀λ₯Ό μˆ˜μ •ν•΄μ•Όν•œλ‹€. 이런 μ˜μ‘΄μ„±μ„ μ—†μ• κΈ° μœ„ν•΄ SimpleFactory 클래슀λ₯Ό λ„μž…ν•œλ‹€.(즉, 객체λ₯Ό μƒμ„±ν•˜λŠ” 뢀뢄을 클래슀둜 뢄리함)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SimpleFactory{
	public Pizza createPizza(String type){
		Pizza pizza = null;
		if(type.equals("cheese")){
			pizza = new CheesePizza();
		}else if(type.equals("pepperoni")){
			pizza = new PepperoniPizza();
		}else if(type.equals("clam")){
			pizza = new ClamPizza();
		}else if(type.equals("veggie")){
			pizza = new VeggiePizza();
		}
		return pizza;
	}
}

ν•˜μ§€λ§Œ 이런 SImple FactoryλŠ” λ””μžμΈ νŒ¨ν„΄μ΄ μ•„λ‹ˆκ³ , κ΅¬ν˜„ μŠ€ν‚¬μ •λ„μ΄λ‹€.

Factory Pattern λž€?

μ—¬λŸ¬ ν•˜μœ„ 클래슀λ₯Ό 생성할 λ•Œ, 각 ν΄λž˜μŠ€λ³„λ‘œ μ˜μ‘΄μ„±μ„ κ°–κ³  μ‹Άμ§€ μ•Šμ„ λ•Œ 주둜 μ‚¬μš©ν•˜λŠ” νŒ¨ν„΄μœΌλ‘œ 객체 μƒμ„±μ˜ μ±…μž„μ„ 클래슀 λ˜λŠ” λ©”μ„œλ“œμ— μœ„μž„ν•œλ‹€. 결과적으둜 객체 μƒμ„±λ‘œμ§μ΄ μ£Όμš” λΉ„μ¦ˆλ‹ˆμŠ€ 둜직과 λΆ„λ¦¬λœλ‹€.

When?

λ¬Έμ œμƒν™©: ν•˜μœ„ 클래슀λ₯Ό μƒμ„±ν•˜λŠ” μ½”λ“œλ₯Ό λΆ„λ¦¬ν•˜κ³  싢을 λ•Œ

  • ꡬ체적인 μ½”λ“œ μƒν™©μœΌλ‘œλŠ” λ‹€λ₯Έ ν΄λž˜μŠ€μ—μ„œ new instance_type λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜κ³  μ‹Άμ§€ μ•Šμ„ λ•Œ

ν•˜μœ„ 클래슀λ₯Ό μ§μ ‘μƒμ„±ν•˜λ©΄, ν•˜μœ„ ν΄λž˜μŠ€κ°€ μΆ”κ°€λ˜κ±°λ‚˜, λ³€κ²½, μ‚­μ œλ  λ•Œλ§ˆλ‹€ 기쑴의 μ½”λ“œλ₯Ό μˆ˜μ •ν•΄μ•Ό ν•˜λ‹ˆ μ˜μ‘΄μ„±μ΄ 생긴닀. 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄, νŒ©ν† λ¦¬λΌλŠ” 클래슀λ₯Ό 톡해 λͺ¨λ“  생성을 μœ„μž„ν•œλ‹€.

  • 주둜, νŒ©ν† λ¦¬ νŒ¨ν„΄μœΌλ‘œ κ΅¬ν˜„ν•œ ν›„(λ§Œμ•½ 더 μœ μ—°ν•΄μ•Ό ν•œλ‹€λ©΄) 좔상화 νŒ¨ν„΄, ν”„λ‘œν† νƒ€μž…, λΉŒλ” νŒ¨ν„΄μœΌλ‘œ μ—…λ°μ΄νŠΈν•œλ‹€.

κ΅¬ν˜„μ„ 톡해 Single Responsibility Principle을 λ§Œμ‘±ν•œλ‹€.

Factory Method Pattern

μ •μ˜

νŒ©ν† λ¦¬ λ©”μ†Œλ“œ νŒ¨ν„΄μ€ 객체λ₯Ό 생성할 λ•Œ ν•„μš”ν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 클래슀둜 μ •μ˜ν•œλ‹€.(κ°„λ‹¨ν•œ ꡬ성이 μ•„λ‹ˆλΌλ©΄ abstract methodλ₯Ό μ‚¬μš©ν•œλ‹€.) μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” 것은 ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ κ²°μ •ν•œλ‹€.

상황

μœ„μ˜ μƒν™©μ—μ„œ μ–ΈκΈ‰ν–ˆλ˜ 것 처럼, μ μš©λ˜λŠ” ν΄λž˜μŠ€κ°€ ν•˜λ‚˜κ°€ μ•„λ‹ˆκ³ , μƒμ„±ν•˜λŠ” 방식이 λ‹€λ₯΄λ‹€λ©΄ μœ„μ™€ 같은 λ°©λ²•μœΌλ‘œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μ–΄λ ΅λ‹€. λ§Œμ•½ 쀑ꡭ, ν•œκ΅­, λ―Έκ΅­ 지점이 μžˆλ‹€κ³  κ°€μ •ν•˜λ©΄ PizzaStoreλŠ” Factory둜 쀑ꡭ λ―Έκ΅­ ν•œκ΅­ 지점을 λͺ¨λ‘ κ°€μ§€κ³  μžˆμ–΄μ•Όν•œλ‹€. λΆˆν•„μš”ν•œ ꡬ성이닀. 그렇기에 Method Pattern을 μ΄μš©ν•œλ‹€.

  1. μƒμœ„ ν΄λž˜μŠ€μ—μ„œ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” 뢀뢄을 abstrct method()둜 μ •μ˜ν•œλ‹€.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
     public abstract class PizzaStore {
    	 
         protected abstract Pizza createPizza(String item);
    	 
         public Pizza orderPizza(String type) {
             Pizza pizza = createPizza(type);
             System.out.println("--- Making a " + pizza.getName() + " ---");
             pizza.prepare();
             pizza.bake();
             pizza.cut();
             pizza.box();
             return pizza;
         }
     }
    
  2. ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ 이λ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬, μ•Œλ§žμ€ ν”Όμžλ₯Ό μƒμ„±ν•œλ‹€.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
     public class NYPizzaStore extends PizzaStore {
    	
         Pizza createPizza(String item) {
             if (item.equals("cheese")) {
                 return new NYStyleCheesePizza();
             } else if (item.equals("veggie")) {
                 return new NYStyleVeggiePizza();
             } else if (item.equals("clam")) {
                 return new NYStyleClamPizza();
             } else if (item.equals("pepperoni")) {
                 return new NYStylePepperoniPizza();
             } else return null;
         }
     }
    

μ΄λ ‡κ²Œν•˜λ©΄, λ˜‘κ°™μ€ μ‹œμŠ€ν…œ(orderPizza λ“±)λ₯Ό μ΄μš©ν•˜λ©΄μ„œ λ™μ μœΌλ‘œ μ•Œλ§žμ€ ν”Όμžλ₯Ό 생성할 수 μžˆλ‹€, μΆ”ν›„ λ‹€λ₯Έ ν”Όμžκ°€ μΆ”κ°€λœλ‹€λ©΄ ν•΄λ‹Ή ν”Όμžν΄λž˜μŠ€λ₯Ό μƒμ„±ν•˜κ³  Factory에 μΆ”κ°€ν•˜λ©΄λœλ‹€.

μ˜μ‘΄μ„± μ—­μ „

λ§Œμ•½, νŒ©ν† λ¦¬ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ§€ μ•Šκ³  초기 μƒν™©μ²˜λŸΌ PizzaStore ν΄λž˜μŠ€μ—μ„œ 객체λ₯Ό μƒμ„±ν•œλ‹€λ©΄, μ—¬λŸ¬ Pizza μ’…λ₯˜μ˜ μ˜μ‘΄μ„±μ„ κ°–κ²Œλœλ‹€. 각 μ’…λ₯˜μ˜ PizzaλŠ” κ΅¬μ„±ν΄λž˜μŠ€μ΄μ§€λ§Œ, Pizzaκ°€ λ³€ν•œλ‹€λ©΄ PizzaStore의 μ½”λ“œλ„ λ³€κ²½ν•΄μ•Ό ν•œλ‹€. 즉, PizzaStoreλŠ” μ—¬λŸ¬ μ’…λ₯˜μ˜ Pizza의 μ˜μ‘΄μ„±μ„ κ°€μ§„λ‹€. 즉, κ΅¬ν˜„μ— μ˜μ‘΄ν•˜κ²Œ λœλ‹€.

μ˜μ‘΄μ„± 역전원칙은 β€œκ΅¬ν˜„μ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³ , μΆ”μƒν™”λœ 것에 μ˜μ‘΄ν•œλ‹€β€λΌλŠ” 원칙이닀.

1
2
3
4
5
6
7
8
9
10
11
12
Pizza orderPizza(string type){
	Pizza pizza;
	if(type.equals("cheese"))
		pizza=new CheesePizza();
	else if(type.equals("pepperoni"))
		pizza=new PepperoniPizza();
	else if(type.equals("clam")
		pizza=new ClamPizza();
	else if(type.equals("veggie"))
		pizza=new VeggiePizza();
}
...

νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄μ„ μ μš©ν•˜λ©΄, 각 ν”Όμž ν΄λž˜μŠ€κ°€ μ•„λ‹Œ PizzaλΌλŠ” abstract class에 μ˜μ‘΄ν•˜κ²Œ λœλ‹€.

μ˜μ‘΄μ„± μ—­μ „ 원칙 μ§€ν‚€λŠ” 법

  1. λ³€μˆ˜μ— ꡬ상 클래슀의 레퍼런슀λ₯Ό μ €μž₯ν•˜μ§€ μ•ŠλŠ”λ‹€.(new μ—°μ‚°μž)
  2. ꡬ상 ν΄λž˜μŠ€μ—μ„œ μœ λ„λœ 클래슀λ₯Ό λ§Œλ“€μ§€ μ•ŠλŠ”λ‹€.
  3. 베이슀 ν΄λž˜μŠ€μ— 이미 κ΅¬ν˜„λ˜μ–΄ μžˆλŠ” λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œν•˜μ§€ μ•ŠλŠ”λ‹€. (베이슀 ν΄λž˜μŠ€κ°€ μ œλŒ€λ‘œλœ μΆ”μƒν™”λ˜μ§€ μ•ŠκΈ°μ—)

μœ„μ˜ 원칙을 μ§€ν‚€λŠ” Java ν”„λ‘œκ·Έλž¨μ€ 거의 μ—†λ‹€. λ‹€λ§Œ, μ•žμœΌλ‘œ 지속적인 λ³€ν™”κ°€ ν•„μš”ν•˜λ‹€λ©΄ νŒ¨ν„΄μ„ μ€€μˆ˜ν•΄μ•Ό μœ μ§€λ³΄μˆ˜μ— νŽΈν•˜λ‹€.

단점

νŒ©ν† λ¦¬ λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•˜λ©΄ ν˜•μ‹ μ•ˆμ •μ„±(type - safety)에 μ§€μž₯이 μžˆμ„ 수 μžˆλ‹€. μœ„μ˜ μ˜ˆμ—μ„œλ„ string μž…λ ₯λ˜λŠ” μΈμžκ°€ 잘λͺ»μ „λ‹¬λ˜λ©΄ null을 λ°˜ν™˜ν•˜κ²Œ λœλ‹€.

  • μœ„μ˜ ν•΄κ²°λ°©λ²•μœΌλ‘œλŠ” ν˜•μ‹μ„ κ³ μ •ν•œλ‹€. 정적 μƒμˆ˜ ν˜Ήμ€ enum, λ§€κ°œλ³€μˆ˜λ₯Ό λ‚˜νƒ€λ‚΄λŠ” νŠΉλ³„ν•œ 객체λ₯Ό 톡해 ν•΄κ²°ν•  수 μžˆλ‹€.

좔상 νŒ©ν† λ¦¬ νŒ¨ν„΄

좔상 νŒ©ν† λ¦¬ νŒ¨ν„΄μ€ ꡬ상 ν΄λž˜μŠ€μ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³ λ„ μ„œλ‘œ μ—°κ΄€λ˜κ±°λ‚˜ 의쑴적인 객체둜 이루어진 μ œν’ˆκ΅°μ„ μƒμ„±ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ œκ³΅ν•œλ‹€. 일련된 μ΄λΌλŠ” λ§μ—μ„œ, νŒ©ν† λ¦¬ λ©”μ„œλ“œμ™€ λ‹€λ₯΄λ‹€. νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€λ§Œ μƒμ„±ν•˜λŠ” 반면, 좔상 νŒ©ν† λ¦¬ νŒ¨ν„΄μœΌλ‘œλŠ” 일련된(μ—°κ΄€λœ)μΈμŠ€ν„΄μŠ€λ₯Ό λͺ¨λ‘ 생성할 수 μžˆλ‹€.

상황

μ•žμ„œ ν”Όμžλ₯Ό μ œμž‘ν–ˆμ„ λ•Œ, 이제 각 μ§€μ λ§ˆλ‹€ 재료λ₯Ό 달리 μ‚¬μš©ν•œλ‹€κ³  ν•œλ‹€. μ’…λ₯˜λŠ” μ•„λž˜μ™€ 같이 총 6κ°œκ°€ μžˆλ‹€.

1
2
3
4
5
6
  Dough dough;
	Sauce sauce;
	Veggies veggies[];
	Cheese cheese;
	Pepperoni pepperoni;
	Clams clam;

이럴 경우, 각 μ§€μ μ—μ„œ ν”Όμžλ₯Ό 생성할 λ•Œ νŒ©ν† λ¦¬λ₯Ό μ΄μš©ν•˜μ—¬ 재료λ₯Ό μ„€μ •ν•΄μ€˜μ•Όν•œλ‹€.

μ•„λž˜λŠ” λ‰΄μš• ν”Όμž 재료 νŒ©ν† λ¦¬μ΄λ‹€.

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
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
 
	public Dough createDough() {
		return new ThinCrustDough();
	}
 
	public Sauce createSauce() {
		return new MarinaraSauce();
	}
 
	public Cheese createCheese() {
		return new ReggianoCheese();
	}
 
	public Veggies[] createVeggies() {
		Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
		return veggies;
	}
 
	public Pepperoni createPepperoni() {
		return new SlicedPepperoni();
	}

	public Clams createClam() {
		return new FreshClams();
	}
}

이제 ν”Όμž 생성과정을 천천히 μ‚΄νŽ΄λ³΄λ©΄

  1. λ‰΄μš• ν”Όμž κ°€κ²Œμ—μ„œ 치즈 ν”Όμžλ₯Ό μ£Όλ¬Έν•œλ‹€. NYPizzaStore.orderPizza(”cheese”);
  2. orderPizza λ§€μ†Œλ“œ λ‚΄λΆ€μ—μ„œ createPizza(”cheese”)λ₯Ό ν˜ΈμΆœν•œλ‹€.
  3. μ²˜μŒμ— μ§€μ •λœ μ›μž¬λ£Œ νŒ©ν† λ¦¬λ₯Ό 톡해, μΉ˜μ¦ˆν”Όμžκ°€ μƒμ„±λœλ‹€.

    1
    
     Pizza pizza = new CheesePizza(ingredientFactory);
    
    • cheese ν”Όμž 클래슀 일뢀
    1
    2
    3
    4
    5
    6
    
     void prepare() {
             System.out.println("Preparing " + name);
             dough = ingredientFactory.createDough();
             sauce = ingredientFactory.createSauce();
             cheese = ingredientFactory.createCheese();
         }
    

단점

μƒˆλ‘œμš΄ μ œν’ˆμ„ μΆ”κ°€ν•˜κ±°λ‚˜, μƒˆλ‘œμš΄ 도핑 좔가와 같이 λ‚΄λΆ€λ₯Ό λ³€κ²½ν•˜λ©΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ „λ©΄ μˆ˜μ •ν•΄μ•Ό ν•œλ‹€. 즉, μ„œλΈŒ 클래슀의 μΈν„°νŽ˜μ΄μŠ€λ„ μ „λΆ€ μˆ˜μ •ν•΄μ•Ό ν•œλ‹€. 그렇기에 μƒˆλ‘œμš΄ μš”μ†Œκ°€ 잘 μΆ”κ°€λ˜μ§€ μ•Šκ³ , λ§Œλ“œλŠ” 방식이 μ—¬λŸ¬κ°œ ν•„μš”ν•œ 경우 μœ μš©ν•˜λ‹€. 즉, λ³€ν™”μ˜ μš”μ†Œκ°€ μž¬λ£Œκ°€ μ•„λ‹Œ ν”ΌμžμΈ 경우 μœ μš©ν•˜λ‹€.

νŒ©ν† λ¦¬ λ©”μ„œλ“œ vs 좔상 νŒ©ν† λ¦¬

μ‚¬μš©λ°©μ‹

  • 상속 및 μ˜€λ²„λΌμ΄λ”©, 즉 μ„œλΈŒ(ν•˜μœ„) ν΄λž˜μŠ€μ—μ„œ 생성을 λ‹΄λ‹Ή
  • 객체 ꡬ성(factoryλ₯Ό λ©€λ²„λ³€μˆ˜λ‘œ), νŒ©ν† λ¦¬μ—μ„œ 생성을 λ‹΄λ‹Ή
This post is licensed under CC BY 4.0 by the author.