Notice
suyeonme
[디자인 패턴] 팩토리 패턴(Factory Pattern) 본문
헤드 퍼스트 디자인 패턴을 읽고 정리한 내용입니다.
1. 간단한 팩토리(Simple Factory)
- 디자인 패턴이라기 보다는 프로그래밍에서 자주 쓰이는 관용구이다.
- 객체 생성 작업을 팩토리 클래스로 캡슐화해놓으면 구현을 변경할 때 팩토리 클래스 하나만 고치면 된다. 즉 코드에서 중복되는 내용을 제거할 수 있고, 관리할 때도 한군데만 관리하면 된다.
- 객체 인스턴스를 만들 때 인터페이스만 있으면 된다. 인터페이스를 바탕으로 하여 유연성과 확장성이 뛰어난 코드를 작성할 수 있다.
- 팩토리(factory): 객체 생성을 처리하는 클래스
1.1 문제 상황
당신은 피자가게를 운영한다. 따라서 아래의 코드를 작성하였다. 하지만 코드는 몇 가지 문제점이 있다.
- 피자 종류가 추가, 제거, 변경등이 일어날 때마다 코드를 계속 고쳐야한다.
Pizza orderPizza(String type) {
Pizza = pizza;
// 구상 클래스 선택
if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("greek")) {
pizza = new GreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}
// ...
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
1.2 객체 생성 팩토리로 교체
// 간단한 팩토리(Simple Factory)
public class SimplePizzaFactory {
public pizza createPizza(String type) {
Pizza pizza = null;
// 구상 클래스 선택
if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("greek")) {
pizza = new GreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}
}
}
public class PizzaStore {
SimplePizzaFactory = factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
2. 팩토리 메서드 패턴(Factory Method Pattern)
- 객체를 생성할 때 필요한 인터페이스를 만든다.
- 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정한다. 팩토리 메소드 패턴을 사용하면 클래스 인스턴스 만드는 일을 서브 클래스에게 맡기게 된다. 즉, 사용하는 서브 클래스에 따라 생산되는 객체 인스턴스가 결정된다.
- 상속으로 객체를 생성한다. 따라서 클래스를 확장하고 팩토리 메소드를 오버라이드한다. 즉 클라이언트와 구상 형식을 분리한다.
2.1 의존성 뒤집기 원칙(Dependency Inversion Principle)
- 추상화된 것에 의존하게 만들고 구상 클래스에 의존하지 않게 만든다. 고수준 구성 요소가 저수준 구성요소에 의존하면 안되며, 항상 추상화에 의존하게 만들어야한다. (PizzaStore는 고수준 구성 요소, Pizza 클래스는 저수준 구성 요소)
- 변수에 구상 클래스의 레퍼런스를 저장하지 않는다.
- 구상 클래스에서 유도된 클래스를 만들지 않는다.
- 베이스 클래스에 이미 구현되어있는 메소드를 오버라이드하지 않는다.
2.2 피자 가게 프레임워크 만들기
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type); // 팩토리 메서드
}
2.3 서브 클래스 만들기
서브 클래스에서 구상 클래스 인스턴스를 생성한다.
public class MYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if(type.equals("cheese")) {
pizza = new NYCheesePizza();
} else if(type.equals("greek")) {
pizza = new NYGreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new NYPepperoniPizza();
} else {
pizza = null;
}
}
}
2.4 Pizza 클래스 만들기
public abstract class Pizza {
String name;
String dough;
String sauce;
List<String> toppings = new ArrayList<String>();
void prepare() {
System.out.pringln("준비중: " + name);
System.out.pringln("도우를 돌리는 중");
System.out.pringln("소스를 뿌리는 중");
System.out.pringln("토핑을 올리는 중");
for(String topping : toppings) {
System.out.println(" " + topping);
}
}
void bake() {
System.out.pringln("175도에서 25분간 굽기");
}
void cut() {
System.out.pringln("피자를 자르기");
}
void box() {
System.out.pringln("피자를 포장하기");
}
}
// 구상 서브 클래스
public class NYCheesePizza extends Pizza {
public NYCheesePizza() {
name = "뉴욕 스타일 소스와 치즈 피자";
dough = "씬 크러스트 도우";
sauce = "마리나라 소스";
toppings.add("잘게 썬 레지아노 치즈");
}
}
3. 추상 팩토리 패턴(Abstract Factory Pattern)
- 구상 클래스에 의존하지 않고도 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생산하는 인터페이스를 제공한다.
- 구상 클래스는 서브 클래스에서 만든다.
- 추상 팩토리 패턴을 사용하면 클라이언트에서 추상 인터페이스로 일련의 제품을 공급받을 수 있다. 따라서 클라이언트와 팩토리에서 생산되는 제품을 분리할 수 있다.
- 객체 구성(composition)으로 객체를 생성하며 제품군을 만드는 추상 형식을 제공한다. 제품이 생산되는 방법은 이 형식의 서브클래스에서 정의한다.
3.1 원재료를 생산하는 팩토리용 인터페이스 생성
public interface PizzaIngredientFactory {
public Dough createDough;
public Sauce createSauce;
public Cheese createCheese;
public Veggies[] createVeggies;
public Pepperoni createPepperoni;
public Clams createClams;
}
3.2 원재료(Ingredients) 클래스 생성
// 뉴욕 원재료 팩토리
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() };
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClams() {
return new FreshClams();
}
}
3.3 Pizza 클래스 변경
public abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Veggies[] veggies;
Cheese cheese;
Pepperoni pepperoni;
Clams clams;
abstract void prepare();
void bake() {
System.out.pringln("175도에서 25분간 굽기");
}
void cut() {
System.out.pringln("피자를 자르기");
}
void box() {
System.out.pringln("피자를 포장하기");
}
}
// 구상 서브 클래스
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("준비중: " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createDough();
cheese = ingredientFactory.createCheese();
}
}
3.4 PizzaStore 구현 클래스 생성
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if(item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
} else if(item.equals("veggie")) {
pizza = new VeggiePizza(ingredientFactory);
}
// ...
return pizza;
}
}
'프로그래밍👩🏻💻 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 커맨드패턴(Command Pattern) (0) | 2022.07.10 |
---|---|
[디자인 패턴] 싱글톤 패턴(Singleton Pattern) (0) | 2022.07.04 |
[디자인 패턴] 데코레이터 패턴(Decorator Patter) (0) | 2022.06.19 |
[디자인 패턴] 옵저버 패턴(Observer Pattern) (0) | 2022.06.18 |
[디자인 패턴] 전략 패턴(Strategy Pattern) (0) | 2022.06.18 |
Comments