[디자인 패턴] 데코레이터 패턴

Benji Android·2023년 3월 29일
0

디자인 패턴

목록 보기
6/7
post-thumbnail

정의

주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때, 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다.
데코레이터(Decortor) 뜻 그대로 원하는 클래스를 장식한다.
즉, Wrapping 한다 라고 생각할 수 있다.


구현할 클래스 목록

Beverage 를 사용하여 커피를 만듭니다.


Component (Beverage) 구현

interface Beverage {

    fun getDescription(): String {
        return "Unknown Beverage"
    }

    fun cost(): Double
}

Interface 를 활용하면 실제 구현체를 서브 클래스로 위임할 수 있게 된다.

구현은 ConcreateComponent 에서 override 를 활용하여 구현하게 된다.


ConcreateComponent

다이어 그램과 같이 Beverage implements 하여 구현체를 작성하는 ConcreateComponent 만든다.

class Espresso : Beverage {

    override fun getDescription(): String {
        return "Espresso"
    }

    override fun cost(): Double {
        return 1.99
    }
}

class Decaf : Beverage {

    override fun getDescription(): String {
        return "Decaf Coffee"
    }
    override fun cost(): Double {
        return 1.05
    }
}

Beverage 의 구현체 클래스를 만들어 보았습니다.

Beverage는 인터페이스기 때문에 Client 에서 사용하기 위해 구현체 클래스를 만들어 주어야 합니다.

Client 에서 사용하기 위해 Espresso , Decaf 클래스를 만들었습니다.


Decorator & ConcreateDecorator

// 비슷한 카테고리를 한번에 처리하기 위해 abstract class 만들어 사용할 수 있다.
abstract class CondimentDecorator : Beverage {
    abstract val beverage: Beverage
}

// 기본 Beverage 를 생성자로 받아서 행동을 추가하는 class 이다.
class Milk(override val beverage: Beverage) : CondimentDecorator() {

    override fun getDescription(): String {
        return beverage.getDescription() + ", Milk"
    }
    override fun cost(): Double {
        return .10 + beverage.cost()
    }
}

기본적인 Beverage 클래스를 Decorator 할 클래스를 만듭니다.

abstract class 로 만들어 Beverage 에서 사용하는 class 를 override 할 수 있도록 하고

Beverage 를 서브 클래스에서 사용하도록 강제합니다.

서브클래스에서는 기본 Beverage 의 값에 추가로 장식할 수 있도록 함수를 재정의합니다.

Decorator class 만들어 사용하게 되면, 원래 데이터가 Wrapping 되는 효과를 얻을 수 있습니다.


사용해보기

class AppCafe {
    init {
        val bw = System.out.bufferedWriter()
        var beverage1: Beverage = Decaf()
        bw.appendLine("1번 커피 주문")
        bw.appendLine("${beverage1.getDescription()} $${beverage1.cost()}")

        var beverage2: Beverage = HouseBlend()
        beverage2 = Mocha(beverage2)
        beverage2 = Mocha(beverage2)
        beverage2 = Whip(beverage2)
        bw.appendLine("2번 커피 주문")
        bw.appendLine("${beverage2.getDescription()} $${beverage2.cost()}")

        var beverage3: Beverage = Espresso()
        beverage3 = Milk(beverage3)
        beverage3 = Soy(beverage3)
        beverage3 = Mocha(beverage3)
        bw.appendLine("3번 커피 주문")
        bw.appendLine("${beverage3.getDescription()} $${beverage3.cost()}")

        bw.flush()
    }
}

private fun main() {
    AppCafe()
}

모든 데코레이터를 만들고 커피를 주문해보겠습니다.

1번 커피의 경우 디카페인 커피에 아무것도추가하지 않습니다.

2번 커피는 기본 블랜딩 + 2개의 모카 + 휘핑 으로 주문합니다.

3번 커피는 에스프레소 + 우유 + 두유 + 모카 로 주문합니다.

기본 Beverage 에 Decorator class 덮어 Wrapping 하면 Beverage 에 Decorator에서 추가한 작업이 (+) 되어 return 됩니다.


마무리

Decorator 하고 싶은 Target을 Interface로 정의하고 구현체 Class 추가로 만듭니다.

생성자로 Target Interface 를 받아 장식하고 싶은 부분을 override 를 사용하여 추가합니다.

Decortor 를 지난 Target 은 Wrapping 되어 내가 추가한 값이 저장됩니다.

장점

  • 새로운 클래스를 만들지 않고 기존 기능을 조합할 수 있다.(이미 생성된 Decortor class 조합하면 다양한 경우의 수를 조합할 수 있습니다.)
  • 컴파일 타임이 아닌 런타임에 동적으로 기능을 변경할 수 있다.

단점

  • 테코레어터를 조합하는 코드가 복잡할 수 있다.

참고

profile
Android 주니어 개발자

0개의 댓글