- 팩토리 패턴은 크게 3가지 정도가 있다.
- Simple Factory (엄밀히 말하면 패턴은 아니다)
- Factory Method Pattern
- Abstract Method Pattern
Simple Factory
엄밀히 말하면 패턴은 아니다. 프로그래밍에서 자주 쓰이는 관용구 정도의 방식이다.
상황
- 피자 가게에 주문을 받고, 피자를 만드는 시스템이 있다.
- 피자의 종류에 상관없이 피자를 굽고, 자르고, 포장하는 과정은 모두 똑같다.
- 주문에 따라 피자의 종류만 달라진다.
class PizzaStore(val factory: SimplePizzaFactory) {
fun orderPizza(type: String): Pizza {
val pizza = factory.createPizza(type)
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
}
}
class SimplePizzaFactory {
fun createPizza(type: String): Pizza {
if (type == "chicago") {
return ChicagoPizza()
} else {
return CheesePizza()
}
}
}
interface Pizza {
fun prepare()
fun bake()
fun cut()
fun box()
}
class CheesePizza : Pizza {
override fun prepare() {
// 재료준비
}
override fun bake() {
// 굽기
}
override fun cut() {
// 자르기
}
override fun box() {
// 포장하기
}
}
class ChicagoPizza : Pizza {
override fun prepare() {
// 재료준비
}
override fun bake() {
// 굽기
}
override fun cut() {
// 자르기
}
override fun box() {
// 포장하기
}
}
- 인스턴스를 생성하는 역할을 팩토리 클래스에 위임했다.
- 피자 종류가 추가되어도
PizzaStore
를 변경하지 않아도 된다.
- 피자 종류가 추가되어도
Factory Method Pattern
팩토리 메소드 패턴은 슈퍼 클래스에 객체를 생성할 때 필요한 인터페이스를 만든다. 어떤 클래스의 인스턴스를 만들지는 서브 클래스에게 맡기는 것이다.
Creator
(슈퍼 클래스)는 사용할 객체를 만드는 메소드를 가지고 있다.Product
를 반환받아 작업을 할 뿐이다.Product
가 무엇인지는 모른다.
- ex) 라인 조립을 한다고 하자. 나사들 가져와서 임팩트 드릴로 박아야한다.
- 라인 작업자가 해야할 일
- 나사를 만들어주세요.
- 나사를 조립한다.
- 라인 작업자는 나사의 재질이 무엇이고 어느 공장에서 만들어진건지 알 필요가 없다.
- 라인 작업자가 해야할 일
상황
Simple Factory 패턴에 이어서, 이제 피자 가게를 확장하기로 했다.
피자를 만드는 과정은 똑같지만, 피자에는 지역 색이 들어간다.
K-Pizza, Italian Pizza, Chicago Pizza, Newyork Pizza …
기존의 방식을 써볼까?
- 한 공장에서 너무 많은 피자를 만든다.
바꿔보자
package design_pattern
abstract class PizzaStore {
fun orderPizza(type: String): Pizza {
val pizza = createPizza(type)
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
}
abstract fun createPizza(type: String): Pizza
}
class KoreanPizzaStore : PizzaStore() {
override fun createPizza(type: String): Pizza? {
return when(type) {
"cheese" -> KoreanStyleCheesePizza()
"pepperoni" -> KoreanStylePepperoniPizza()
"combination" -> KoreanStyleCombinationPizza()
"potato" -> KoreanStylePotatoPizza()
else -> null
}
}
}
class NYPizzaStore : PizzaStore() {
override fun createPizza(type: String): Pizza? {
return when(type) {
"cheese" -> NYStyleCheesePizza()
"pepperoni" -> NYStylePepperoniPizza()
"combination" -> NYStyleCombinationPizza()
"potato" -> NYStylePotatoPizza()
else -> null
}
}
}
interface Pizza {
fun prepare()
fun bake()
fun cut()
fun box()
}
class NYStylePotatoPizza : Pizza {
}
class NYStyleCombinationPizza : Pizza {
// 생략
}
class NYStylePepperoniPizza : Pizza {
// 생략
}
class NYStyleCheesePizza : Pizza {
// 생략
}
class KoreanStylePotatoPizza : Pizza {
// 생략
}
class KoreanStyleCombinationPizza : Pizza {
// 생략
}
class KoreanStylePepperoniPizza : Pizza {
// 생략
}
class KoreanStyleCheesePizza : Pizza {
// 생략
}
- 실제로 클라이언트가 스토어 구현체에 주문하는 것은 아니지만, 무슨 피자를 만들 것인지는 구현체(서브클래스)에 정의되어 있기 때문에 이렇게 그려봤다.
- 이렇게 그리는게 맞을 거 같다.
Abstract Factory Pattern
추상 팩토리 패턴은 구현 클래스에 의존하지 않고, 서로 연관되거나 의존적인 객체로 이루어진 제품군(Family)을 생산하는 인터페이스를 제공하는 패턴이다.
- 사용자는 추상적인 Product만 알고 사용한다. 실제로 어떤 것인지는 상관없다. (팩토리 메소드 패턴도 동일)
- 팩토리를 통해 반환받은 Product만 사용한다.
- 사용하는 방법은 똑같기 때문에, Mac에서 노션을 쓰든, Windows에서 노션을 쓰든 동일한 GUI를 사용할 수 있다.
상황
피자가게가 전세계로 나아가다 보니, 서로 쓰는 재료가 달라졌다.
그러다보니 같은 나라의 지점이라도 지점 별로 다른 원산지의 재료를 쓰고 있었다.
각 나라별로 원재료를 공급하는 공장을 만들어 피자의 품질을 올리자.
interface PizzaIngredientFactory {
fun createDough(): Dough
fun createSource(): Source
fun createCheese(): Cheese
fun createPepperoni(): Pepperoni
}
class NYPizzaIngredientFactory : PizzaIngredientFactory {
override fun createDough(): Dough {
return ThinCrustDough()
}
override fun createSource(): Source {
return MarinaraSource()
}
override fun createCheese(): Cheese {
return ReggianoCheese()
}
override fun createPepperoni(): Pepperoni {
return SlicedPepperoni()
}
}
- 재료 공장을 만들어준다.
interface Pizza {
var dough: Dough
var source: Source
var cheese: Cheese
var pepperoni: Pepperroni
fun prepare()
fun bake()
fun cut()
fun box()
}
class PepperoniPizza(private val factory: PizzaIngredientFactory) : Pizza {
override fun prepare() {
dough = factory.createDough()
source = factory.createSource()
cheese = factory.createCheese()
pepperoni = factory.createPepperoni()
}
override fun bake() {
TODO("Not yet implemented")
}
override fun cut() {
TODO("Not yet implemented")
}
override fun box() {
TODO("Not yet implemented")
}
}
- 피자는 재료공장에서 재료를 받아 만들어진다.
- 어떤 공장에서 오는지는 몰라도 된다.
class NYPizzaStore : PizzaStore() {
override fun createPizza(type: String): Pizza? {
val factory: PizzaIngredientFactory = NYPizzaIngredientFactory()
return when(type) {
"cheese" -> CheesePizza(factory)
"pepperoni" -> PepperoniPizza(factory)
"combination" -> CombinationPizza(factory)
"potato" -> PotatoPizza(factory)
else -> null
}
}
}
출처
https://refactoring.guru/ko/design-patterns/factory-method
https://refactoring.guru/ko/design-patterns/abstract-factory
https://ko.wikipedia.org/wiki/추상_팩토리_패턴
Uploaded by N2T
'Programming > 디자인패턴' 카테고리의 다른 글
[Kotlin] Command Pattern 예시코드 - 코틀린으로 쓴 디자인 패턴 (0) | 2023.04.17 |
---|---|
[Kotlin] Singleton Pattern 예시코드 - 코틀린으로 쓴 디자인 패턴 (0) | 2023.04.17 |
[Kotlin] Decorator Pattern 예시코드 - 코틀린으로 쓴 디자인 패턴 (0) | 2023.04.06 |
[Kotlin] Observer Pattern 예시코드 - 코틀린으로 쓴 디자인 패턴 (0) | 2023.04.05 |
[Kotlin] Strategy Pattern 예시코드 - 코틀린으로 쓴 디자인 패턴 (0) | 2023.04.05 |