객체의 인스턴스 생성 작업을 외부에 위임하여 슈퍼클래스의 불필요한 의존성을 없앤다.
orderPizza()
: 고객이 피자를 주문할 수 있어야 합니다.createPizza()
: 피자가게는 주문받은 피자를 만들 수 있어야 합니다.Pizza
: 주문과 생성 과정후 고객에게 전달될 최종 결과물인 피자가 필요합니다.public class PizzaStore {
func orderPizza(type: String) -> Pizza {
var pizza: Pizza
switch(type) {
case .cheese :
pizza = CheesePizza()
case .greek:
pizza = GreekPizza()
}
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
}
}
orderPizza()
메서드의 문제 의존성 문제PizzaStore 클래스 안에 N개의 피자 구상클래스를 의존하고 있습니다.
- 의존성 문제는 조금 있다가 다시 살펴보도록 하겠습니다.
바뀌는 부분 존재
피자를 추가할 경우 매번 switch(type) 블록 안에 피자 구상클래스 생성하는 코드를 추가해줘야 합니다.
orderPizza() 메서드에서는 상황에 따라 필요한 인스턴스를 만들 구상 클래스가 변동될 수 있기 때문에그럴때마다 코드 추가 및 수정작업이 매번 이루어져야 할 것입니다.
그렇다면 어떤 부분이 바뀌는지를 확인했으니 캡슐화를 해보도록 하겠습니다.
SimplePizzaFactory
클래스를 생성합니다.orderPizza() 메소드에선 더이상 어떤 피자를 만들어야 하는지 고민하지 않아도 됩니다.
이로써 orderPizza() 메소드 내의 바뀌는 부분을 분리 및 캡슐화 처리하게 되었습니다.
public class PizzaStore {
let pizzaFactory: SimplePizzaFactory
init(pizzaFactory: SimplePizzaFactory) {
self.pizzaFactory = pizzaFactory
}
func orderPizza(type: String) -> Pizza {
let pizza = pizzaFactory.createPizza(type)
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
}
}
public class SimplePizzaFactory {
public createPizza(type: String) {
var pizza: Pizza
switch(type) {
case .cheese :
pizza = CheesePizza()
case .greek:
pizza = GreekPizza()
}
return pizza
}
}
import Foundation
enum PizzaType: String {
case Cheese = "Cheese"
case Bakon = "Bakon"
case Photato = "Photato"
}
protocol Pizza {
var name: String {get set}
var dough: String {get set}
var sauce: String {get set}
var toppings: [String] {get set}
func prepare()
func bake()
func cut()
func box()
func getName() -> String
}
extension Pizza {
func prepare() {
print("Preparing " + name)
print("Tossing dough...")
print("Adding Sauce...")
print("Adding toppings: ")
for i in 0..<toppings.count {
print(" " + toppings[i])
}
}
func bake() { print("Bake for 25 minutes at 350") }
func cut() { print("Cutting the pizza into diagonal slices") }
func box() { print("Place pizza in official pizzastore box") }
func getName() -> String { name }
}
public class NYStyleCheesePizza: Pizza {
var name: String
var dough: String
var sauce: String
var toppings: [String] = []
init() {
name = "NY Style Sauce and Cheese Pizza"
dough = "Thin Crust Dough"
sauce = "Mariana Sauce"
toppings.append("Grated REggiano Cheese")
}
}
protocol PizzaStore {
//각 분점의 특성에 맞는 피자 생성은 PizzaStore를 구현하는 클래스에서 담당하게 됩니다.
**func createPizza(type: PizzaType) -> Pizza**
}
extension PizzaStore {
func orderPizza(type: PizzaType) -> Pizza {
let pizza = createPizza(type: type)
//피자 만드는 활동(준비, 굽기, 자르기, 포장)
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
}
}
class NYPizzaStore: PizzaStore {
func createPizza(type: PizzaType) -> Pizza {
switch(type) {
case .Cheese :
return NYStyleCheesePizza()
case .Bakon :
return NYStyleBakonPizza()
case .Photato :
return NYStylePhotatoPizza()
}
}
}
**//Main**
let nyStore = NYPizzaStore()
let pizza = nyStore.orderPizza(type: PizzaType.Cheese)
print("Ethan ordered a \(pizza.getName())")
**//result**
Preparing NY Style Sauce and Cheese Pizza
Tossing dough...
Adding Sauce...
Adding toppings:
Grated REggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official pizzastore box
Ethan ordered a NY Style Sauce and Cheese Pizza
SimplePizzaFactory
에 있던 createPizza() 메서드를 PizzaStore
프로토콜에서 선언만 합니다.createPizza()
메서드는 PizzaStore 구현 클래스에서 각 지역마다 고유의 스타일에 맞게 만들도록 할 것입니다.팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 한다.
팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것이다.
추상화된 것에 의존하도록 만들어라.
구상 클래스에 의존하도록 만들지 않도록 한다.
추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있습니다.