3. Factory & Factory Method Pattern
2021. 9. 15. 13:07ㆍComputer Sciences/Design Patterns
문제
우리 동네에는 피자집이 하나 있다. 이 피자집에서는 예전부터 한 가지 피자만 만들어서 팔았다. 그러나 손님들이 다른 메뉴도 만들어달라는 요청이 들어왔고 이에 따라 신메뉴를 만들어서 제공했다.
// 기존 방식
public class PizzaStore {
Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
// 새로운 방식
public class PizzaStore {
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;
}
}
그런데 이렇게 만들어놓고 보니 메뉴가 추가되거나 삭제될 때마다 orderPizza()
의 코드를 변경해야 한다. orderPizza()
는 주문된 피자를 만들기만 하면 되는 메서드인데 메뉴의 추가/삭제 마다 변경되는 것이 문제이다. 이를 팩토리 패턴으로 해결할 수 있다.
// 팩토리 클래스
// 인스턴스를 생성하는 역할을 담당한다.
public class SimplePizzaFactory {
public Pizza makePizza(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();
} else if (type.equals("clam")) { // 조개 피자 메뉴 추가
pizza = new ClamPizza();
}
return pizza;
}
}
// 팩토리 패턴 적용
public class PizzaStore {
SimplePizzaFactory simplePizzaFactory;
// 생성자를 통해 팩토리 인스턴스를 받는다.
public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
this.simplePizzaFactory = simplePizzaFactory;
}
Pizza orderPizza(String type) {
Pizza pizza;
// 팩토리 패턴을 통해 이제 메뉴의 추가/삭제는 팩토리 클래스에서 담당한다.
simplePizzaFactory.createPizza(type);
// 공통 코드
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
팩토리 패턴
- 객체 생성 부분을 캡슐화한다.
- 코드의 결합도가 낮아진다.
- 변화에 유연하다.
- 프로그래밍을 하는데 있어서 자주쓰이는 관용구에 가깝다.
다시 문제
메뉴까지 늘린 피자집은 매출이 매우 올라갔다. 그래서 프랜차이즈 사업까지 하려고 한다. 먼저 서울과 부산에 지점을 내기로 마음먹었다. 그리고 각 분점들은 각 지역의 특색에 맞는 팩토리를 사용하도록 하게 한다. 이는 팩토리 패턴 또는 팩토리 메서드 패턴을 적용하여 해결할 수 있다.
SeoulPizzaFactory ssFactory = new SeoulPizzaFactory();
PizzaStore ssStore = new PizzaStore(ssFactory);
ssStore.orderPizza("Cheese");
BusanPizzaFactory bsFactory = new BusanPizzaFactory();
PizzaStore bsStore = new PizzaStore(bsFactory);
bsStore.orderPizza("Cheese");
위와 같은 방법은 팩토리 패턴을 적용한 방법이다. 이번엔 팩토리 메서드 패턴을 적용해보자.
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
// 팩토리 객체가 아닌 PizzaStore에 있는 createPizza()를 호출한다.
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 팩토리 객체를 받는 것이 아닌 PizzaStore의 추상 메서드로 변경됐다.
// 이 클래스를 상속받는 서브 클래스들이 이를 구현하여 사용한다.
abstract Pizza makePizza(String type);
}
// 서울역 피자집
public class SeoulStationPizzaStore extends PizzaStore {
Pizza makePizza(String type) {
if(type.equals("cheese")) {
return new SeoulStationCheesePizza();
} else if (type.equals("pepperoni")) {
return new SeoulStationPepperoniPizza();
} else if (type.eqeuals("clam")) {
return new SeoulStationClamPizza();
} else {
return null;
}
}
}
// 부산역 피자집
public class BusanStationPizzaStore extends PizzaStore {
Pizza makePizza(String type) {
if(type.equals("cheese")) {
return new BusanStationCheesePizza();
} else if (type.equals("pepperoni")) {
return new BusanStationPepperoniPizza();
} else if (type.eqeuals("clam")) {
return new BusanStationClamPizza();
} else {
return null;
}
}
}
이런 식으로 만들게 되면 서울역 피자집과 부산역 피자집 분점을 낼 수 있게 된다. 피자 클래스도 만들어보자.
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare() {
System.out.println(name + " 준비 중...");
System.out.println(dough + " 굽는 중...");
System.out.println(sauce + " 뿌리는 중...");
toppings.stream().forEach(t -> System.out.println(t + " 추가 중..."));
}
void bake() {
// 피자 오븐에서 굽기
}
void cut() {
// 구워진 피자 자르기
}
void box() {
// 피자 박싱
}
// Getter and Setter...
}
public class SeoulCheesePizza extends Pizza {
public SeoulCheesePizza() {
name = "서울 스타일 소스와 치즈 피자";
dough = "씬 크러스트 도우";
sauce = "마리나라 소스":
toppings.add("레지아노 치즈");
}
}
public class BusanCheesePizza extends Pizza {
public BusanCheesePizza() {
name = "부산 스타일 소스와 치즈 피자";
dough = "치즈 크러스트 도우";
sauce = "토마토 소스":
toppings.add("모짜렐라 치즈");
}
}
위와 같이 작성함으로써 분점 지역의 특색에 맞게 피자를 만들어낼 수 있게 됐다.
팩토리 메서드 패턴
- 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하게 하는 패턴이다.
- 팩토리 메서드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브 클래스에게 맡기는 것이다.
- 새로운 분점이나 피자 종류를 추가해도 소스의 수정 없이 추상 클래스를 상속받아 구현하기만 하면 된다.
'Computer Sciences > Design Patterns' 카테고리의 다른 글
6. Command Pattern (0) | 2021.10.14 |
---|---|
5. Singleton Pattern (0) | 2021.09.15 |
4. Abstract Factory Method (0) | 2021.09.15 |
2. Decorator Pattern (0) | 2021.09.06 |
1. Observer Pattern (0) | 2021.09.06 |