[디자인 패턴] 추상 팩토리 메소드 패턴

Benji Android·2022년 5월 19일
0

디자인 패턴

목록 보기
2/7
post-thumbnail

정의

추상 팩토리 패턴(Abstract Facgtory Pattern)은 구상 클래스에 의존하지 않고 서로 연관되거나 의존적인 객채로 이루어진 제품군을 생산하는 인터페이스 제공
구상 클래스는 서브 클래스에서 만듬

📌  팩토리 쪽에만 집중하면 팩토리 메소드 패턴과 유사하지만, 클라이언트에서 추상 팩토리를 어떻게 사용하는지에 대한 관점으로 접근해야 목적과 처리점을 이해하기 쉽다.


구현할 추상 팩토리 메소드 클래스


Product Class

abstract class PizzaAF {

    var name: String = ""

    var dough: Dough? = null
    var sauce: Sauce? = null
    var cheese: Cheese? = null
    var clams: Clams? = null
    var veggie: Veggie? = null
    var tomato: Tomato? = null

    abstract fun prepare()

    fun bake() {
        println("25분 동안 피자를 굽습니다.")
    }

    fun cut() {
        println("피자를 8등분으로 자릅니다.")
    }

    fun box() {
        println("피자 박스에 피자를 담고 있습니다.")
    }
		
		override fun toString(): String {
			... 생략
		}
}

일반 팩토리 메소드와 생성자의 구성은 비슷합니다.

달라진 부분이라면 dough, sauce, cheese, clams, veggie, tomato 변수가 각각의 AbstractProduct로 변경되었다는 점 입니다.

해당 객체는 아래에서 자세히 구현하도록 하겠습니다.


AbstractFactory 만들기

interface PizzaIngredientFactory {
    fun createDough(): Dough
    fun createSauce(): Sauce
    fun createCheese(): Cheese
    fun createVeggie(): Veggie
    fun createClams(): Clams
    fun createTomato(): Tomato
}

추상 팩토리 클래스 또는 인터페이스를 통해 Dough, Sauce, Cheese, Veggie, Clams, Tomato 객체에 대한 서브 클래스를 리턴해 줍니다.

이는 객체지향(OOP)의 다형성을 아주 잘 활용한 방식이라 볼 수 있습니다.

자세한 구현은 ConcreteFactory 를 통해 구현해보도록 하겠습니다.


ConcreteFactory 만들기

class NYPizzaIngredientFactory : PizzaIngredientFactory {
    override fun createDough(): Dough = NYDough()

    override fun createSauce(): Sauce = NYSauce()

    override fun createCheese(): Cheese = NYCheese()

    override fun createVeggie(): Veggie = NYVeggie()

    override fun createClams(): Clams = NYClams()

    override fun createTomato(): Tomato = NYTomato()
}

NYPizzaIngredientFactory(ConcreteFactory) 클래스를 만들고 PizzaIngredientFactory(AbstractFactory) 인터페이스를 상속 받습니다.

PizzaIngredientFactory의 함수를 override 하게 되고 여기서 NYPizza 스타일에 맞는 서브 Product 클래스를 넣어 줍니다.

PizzaAF 에 있는 Dough, Sauce, Cheese, Veggie, Clams, Tomato 객체를 Factory를 통해 만들었습니다.


AbstractProduct 만들기

interface Cheese {
    override fun toString(): String
}

interface Clams {
    override fun toString(): String
}

interface Dough {
    override fun toString(): String
}

interface Sauce {
    override fun toString(): String
}

interface Tomato {
    override fun toString(): String
}

interface Veggie {
    override fun toString(): String
}

간단하게 AbstractProduct 클래스들을 만들어 보았습니다.

이제 AbstractProduct를 상속 받아 해당 객체를 구현해줄 서브 클래스들을 만들어 주어야 합니다.


Product Sub Class 만들기

class NYCheese : Cheese {
    override fun toString(): String {
        return "## 뉴욕 치즈 ##"
    }
}

class ChicagoClams : Clams {
    override fun toString(): String {
        return "## 시카고 조개 ##"
    }
}

... 등등

서브 클래스까지 모두 만들었습니다.

이제 Client를 만들어야 합니다.

제가 구현한 프로젝트에서 Client는 PizzaStore 클래스를 상속 받고 있는 클래스들 입니다.

해당 클래스의 상세 구현으로 넘어가 보겠습니다.


Client Class 만들기

abstract class PizzaStoreAF {
		// 팩토리 메소드와 같은 함수 입니다.
    abstract fun createPizza(item: PizzaTypeAF): PizzaAF

    fun orderPizza(type: PizzaTypeAF): PizzaAF {
        val pizza: PizzaAF = createPizza(type)
        println("--- Making a ${pizza.name} ---")
        return pizza.apply {
            prepare()
            bake()
            cut()
            box()
        }
    }
}
class NYPizzaStore : PizzaStoreAF() {

    override fun createPizza(item: PizzaTypeAF): PizzaAF {
        val ingredientFactory = NYPizzaIngredientFactory()

        return when(item) {
            PizzaTypeAF.CHEESE -> {
                CheesePizza(ingredientFactory).apply {
                    name = "뉴욕 치즈 피자"
                }
            }
            PizzaTypeAF.CLAM -> {
                ClamsPizza(ingredientFactory).apply {
                    name = "뉴욕 조개 피자"
                }
            }
            PizzaTypeAF.TOMATO -> {
                TomatoPizza(ingredientFactory).apply {
                    name = "뉴욕 토마토 피자"
                }
            }
            PizzaTypeAF.VEGGIE -> {
                VeggiePizza(ingredientFactory).apply {
                    name = "뉴욕 야채 피자"
                }
            }
        }
    }
}

class ChicagoPizzaStore : PizzaStoreAF() {
		override fun createPizza(item: PizzaTypeAF): PizzaAF {
				...
		}
}

Client를 자세히 보면 제일 처음 다이어그램에서 처럼 AbstractFacotryAbstractProduct 를 가지고 각각의 Pizza를 만들어 주는 것을 볼 수 있습니다.

Client 클래스인 PizzaStore도 모두 만들었습니다.

직접 사용할 수 있도록 코드를 작성해 봅시다.


결과

fun main() {
    val nyStore = NYPizzaStore()
    val chicagoStore = ChicagoPizzaStore()

    var pizzaAF = nyStore.orderPizza(PizzaTypeAF.CHEESE)
    println("A 주문 피자: $pizzaAF")

    pizzaAF = chicagoStore.orderPizza(PizzaTypeAF.TOMATO)
    println("B 주문 피자: $pizzaAF")

    pizzaAF = chicagoStore.orderPizza(PizzaTypeAF.VEGGIE)
    println("C 주문 피자: $pizzaAF")

    pizzaAF = nyStore.orderPizza(PizzaTypeAF.CLAM)
    println("D 주문 피자: $pizzaAF")
}

내가 원하는 Client 에서 원하는 Type의 피자를 주문하면 뉴욕 or 시카고 스타일의 피자를 만들어 줍니다.

사용하는 부분(직접 구현하는 부분)에서 if-else 또는 when 문을 사용하지 않고 쉽게 원하는 피자를 생성이 가능하게 되었습니다.

추가로 다른 종류의 피자를 추가한다면 추상 클래스 or 인터페이스를 추가하면 간단하게 확장이 가능하며, 기존 코드는 변경되지 않습니다.(OCP)


특징

구현(Implement)보다 인터페이스(Interface)를 위한 코드 접근법을 제공.
sub class를 확장이 쉽다.
클라이언트 코드에서 구체적인 클래스의 의존성을 줄여주어 느슨한 결함이 가능함.

팩토리 매소드 & 추상 팩토리 매소드 패턴 차이점

  • 관점이 다르다.
    • 팩토리 패턴은 “팩토리를 구현하는 방법 (inheritance)”에 초점을 둔다.
    • 추상 팩토리 패턴은 “팩토리를 사용하는 방법 (composition)”에 초점을 둔다.
  • 목정이 조금 다르다.
    • 팩토리 패턴은 구체적인 객체 생성 과정을 하위 또는 구체적은 클래스로 옮기는 것이 목적
    • 추장 팩토리 패턴은 관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 해주는 것이 목적

참고

profile
Android 주니어 개발자

0개의 댓글