디자인 패턴

[디자인 패턴] 팩토리 메서드 패턴

Ash_jisu 2023. 12. 3. 17:19

팩토리 메서드 패턴 정의 

객체 생성과 관련된 디자인 패턴으로 객체 생성 역할을 담당하면서 각 클라이언트에서 구현 클래스에 

직접 의존하지 않도록 분리하는 패턴이다. 물론 새로운 클래스가 추가 되었을때 Facotory클래스를 

수정해야하긴 한다. 

한마디로, 인스턴스를 생성을 서브 클래스로 책임을 넘겨준다.

 

장점

  • 확장에 열려있고 변경에 닫혀있게 된다.
  • 중복된 코드가 줄어들고 상속하는 모든 클래스가 동일한 공통 로직을 사용할 수 있다.
  • 객체가 변화할 여지가 있을때 상속하는 클래스를 하나 더 만들어서 구현해주면 된다.

단점

  • 많은 클래스 파일이 생긴다.

 

 

 

 


패턴 적용

이미지 표현

 

코드

간략하게 PizzaStore코드만 보여주겠다

PizzaStore.java

public class PizzaStore {
	Pizza orderPizza(String pizza_type) {
		Pizza pizza = new Pizza(); //
		
		//피자 종류를 바탕으로 올바른 구상 클래스의 인스턴스를 만들고,
		//pizza 인스턴스 변수에 그 인스턴스를 대입함
		//모든 피자 클래스는 Pizza 인터페이스를 구현함
		
		if(pizza_type.equals("cheese")) {
			pizza = new CheesePizza();
		} else if(pizza_type.equals("greek")) {
			pizza = new GreekPizza();
		} else if(pizza_type.equals("pepperoni")) {
			pizza = new PepperoniPizza();
		}
		
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		
		return pizza;
	}
}

 

 

문제점

위 방식에서 피자 종류가 추가 된다고 생각을 해보자. 그러면 PizzaStore에서 else if문이 하나

더 늘고 추가로 00Pizza 클래스가 추가 될 것 이다.  그렇다면 PizzaStore에는 피자 종류 선택과

피자 조리 코드가 동시에 있는 와중에 계속해서 코드가 변경되야한다. 

 

해결방법

피자 종류를 선택할 수 있게 관리하는 PizzaFactory를 둔다. 기존 PizzaStore에 존재하던

피자 고르는 if절 부분을 PizzaFactory옮겨준다. 이렇게 되면 새로운 피자가 추가되더라도

PizzaStore은 코드를 수정할 필요가 없고 관련 내용이 들어간 PizzaFactory코드만 수정해주면 된다.

 


패턴 적용 후 

적용 이미지

 

 

 

PizzaStore.java

public class PizzaStore {
	SimplePizzaFactory factory;
	
	
	public PizzaStore(SimplePizzaFactory factory) {
		this.factory = factory;
	}
	
	public Pizza orderPizza(String pizza_type) {
		Pizza pizza;
		
		pizza = factory.createPizza(pizza_type);
		
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		
		return pizza;
	}
}

 

SimplePizzaFactory.java

//원래 피자선택이랑 조리를 Store에서 다했다면 이제는 분리해서 Store에서는 조리만
//PizzaFactory에서는 피자선택후 피자종류반환만 전담
public class SimplePizzaFactory {
	public Pizza createPizza(String pizza_type) {
		Pizza pizza = null;
		
		if(pizza_type.equals("cheese")) {
			pizza = new CheesePizza();
		}else if(pizza_type.equals("pepperoni")) {
			pizza = new PepperoniPizza();
		}
		
		return pizza;
	}
}

 

Pizza.java

public abstract class Pizza {
	String pizza_name;
	
	public void prepare() {
		System.out.println("도우, 소스, 토핑을 하는중..");
	}
	public void bake() {
		System.out.println("화덕에 피자를 굽습니다..");
	}
	public void cut() {
		System.out.println("피자를 자릅니다..");
		
	}
	public void box() {
		System.out.println("상자에 피자를 담습니다..");
	}
	
	
	public String getName() {
		return pizza_name;
	}
}

 

CheesePizza.java

public class CheesePizza extends Pizza{
	public CheesePizza() {
		pizza_name = "치즈피자";
	}
}

 

 

PizzaTest.java

public class PizzaTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SimplePizzaFactory factory = new SimplePizzaFactory();
		PizzaStore store = new PizzaStore(factory);
		
		System.out.println("\n주문받기");
		Pizza pizza1 = store.orderPizza("cheese");
		System.out.println(pizza1.getName());
		
		System.out.println("\n주문받기");
		Pizza pizza2 = store.orderPizza("pepperoni");
		System.out.println(pizza2.getName());
		
	}
}

 

결과값

 

피자 종류는 여러개지만 구현하는 방법은 비슷해서 CheesePizza코드만 추가했다.

추가로 Pizza를 인터페이스가 아닌 추상클래스로 구현했는데 인터페이스로 구현해도 된다.