• 팩토리 패턴은 크게 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) 라인 조립을 한다고 하자. 나사들 가져와서 임팩트 드릴로 박아야한다.
      • 라인 작업자가 해야할 일
        1. 나사를 만들어주세요.
        1. 나사를 조립한다.
      • 라인 작업자는 나사의 재질이 무엇이고 어느 공장에서 만들어진건지 알 필요가 없다.

상황

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

+ Recent posts