๊ธ€ ์ž‘์„ฑ์ž: ๊ฐœ๋ฐœํ•˜๋Š” ํ›ˆ์ด

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด(Decorator Pattern)

Decorator pattern attatches additional repsonsibilities to an object dynamically.

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์ด ๋ฌด์—‡์ผ๊นŒ์š”? ์ผ๋ฐ˜์ ์œผ๋กœ ์šฐ๋ฆฌ๋Š” ์–ด๋–ค ๊ฐ์ฒด์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ (๋ฉ”์„œ๋“œ ํ˜ธ์ถœ) ๊ทธ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์–ด๋–ค ๋™์ž‘์˜ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ฒŒ๋ฉ๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์€ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•ด ๋Ÿฐํƒ€์ž„์— ํ•ด๋‹น ๊ฐ์ฒด์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

๋Ÿฐํƒ€์ž„ ํƒ€์ž„์— ์ƒˆ๋กœ์šด ๋™์ž‘์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์€ ๊ฐ์ฒด๋ฅผ ํ•œ๋ฒˆ ๊ฐ์‹ธ๋Š” ๋ž˜ํผ ๊ฐ์ฒด๋ฅผ ๋‘๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ์š”! Concrete ๊ฐ์ฒด๋Š” ์›๋ž˜ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋ ค๋Š” ํƒ€์ž…์ด๊ณ , Wrapper๋Š” Concrete์™€ ๊ฐ™์€ ํƒ€์ž…์ด๋ฉด์„œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์™ธ๋ถ€์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” concrete๊ฐ€ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ด์ฃ . 

๋ฌด์Šจ ๋ง์ธ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์ฃ ..? ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์™œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์ด ์™œ ํ•„์š”ํ•œ์ง€ ๋‹ค์‹œ ๊ณ ๋ฏผํ•ด๋ด…์‹œ๋‹ค.

 

์„œ๋ธŒ์›จ์ด ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

์—ฌ๋Ÿฌ๋ถ„์ด ์ƒŒ๋“œ์œ„์น˜ ๊ฐ€๊ฒŒ์ธ ์„œ๋ธŒ์›จ์ด๋ฅผ ์ฝ”๋“œ๋กœ ์˜ฎ๊ฒจ๋ณด๋ ค๊ณ  ๋…ธ๋ ฅํ•ด๋ณธ๋‹ค๊ณ  ํ•ด๋ณผ๊ฒŒ์š”. ๊ธฐ๋ณธ ๋ฉ”๋‰ด๋„ ์žˆ๊ฒ ์ง€๋งŒ ์„œ๋ธŒ์›จ์ด์˜ ๋ฌ˜๋ฏธ๋Š” ์›ํ•˜๋Š” ์žฌ๋ฃŒ๋ฅผ ๋งˆ์Œ๋Œ€๋กœ ์„ž์–ด์„œ ๊ธˆ์•ก์„ ์ง€๋ถˆํ•˜๋Š” ๊ฒƒ์ด๊ฒ ์ฃ . ๊ทธ๋Ÿผ ๋จผ์ € ๊ธฐ๋ณธ์ ์ธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค.

import Foundation

protocol Sandwich: CustomStringConvertible {
    func cost () -> Int
}

class OriginalSandwich: Sandwich {
    var description: String {
        return "OriginalSandwich"
    }
    
    func cost() -> Int {
        return 3000
    }
}

class VeganSandwich: Sandwich {
    var description: String {
        return "VeganSandwich"
    }
    
    func cost() -> Int {
        return 2500
    }
}

Sandwich ํ”„๋กœํ† ์ฝœ์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , ๊ธฐ๋ณธ ์ƒŒ๋“œ์œ„์น˜์™€ ๋น„๊ฑด ๊ธฐ๋ณธ ์ƒŒ๋“œ์œ„์น˜ ํƒ€์ž…์„ ๋งŒ๋“ค์–ด์ฃผ์—ˆ์–ด์š”. ์ง€๊ธˆ์€ ๋‘ ์ข…๋ฅ˜์˜ ์ƒŒ๋“œ์œ„์น˜๊ฐ€ ์žˆ๋Š”๋ฐ์š”, ์„œ๋ธŒ์›จ์ด์—์„œ ํ•˜๋“ฏ์ด ์žฌ๋ฃŒ๊ฐ€ ๋” ์ถ”๊ฐ€๋˜์–ด์„œ ์ข…๋ฅ˜๊ฐ€ ๋งŽ์•„์ง€๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? 

 

protocol Sandwich: CustomStringConvertible {
    func cost () -> Int
}

class OriginalSandwich: Sandwich {
    var description: String {
        return "OriginalSandwich"
    }
    
    func cost() -> Int {
        return 3000
    }
}

class VeganSandwich: Sandwich {
    var description: String {
        return "VeganSandwich"
    }
    
    func cost() -> Int {
        return 2500
    }
}

class meatballSandwich: Sandwich {
    var description: String {
        return "meatballSandwich"
    }
    
    func cost() -> Int {
        return 3500
    }
}

class avocadoSandwich: Sandwich {
    var description: String {
        return "avocadoSandwich"
    }
    
    func cost() -> Int {
        return 4500
    }
}

๋‹จ์ผ ์ฑ…์ž„ ์›์น™์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด์„œ ์ด๋ ‡๊ฒŒ ์ž˜๊ฒŒ ์„ธ๋ถ€ ํƒ€์ž…์„ ๋” ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ฒ ์ง€๋งŒ, ์ข…๋ฅ˜๊ฐ€ ๋งŽ์œผ๋ฉด ๋งŽ์„์ˆ˜๋ก ํด๋ž˜์Šค์˜ ๊ฐœ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์กฐํ•ฉ์— ๋Œ€ํ•ด์„œ ์ƒˆ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜๋Š” ์—†์œผ๋‹ˆ๊นŒ์š”. ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์•„๋‹Œ ๊ฒƒ ๊ฐ™์ฃ ? 

๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๋ชจ๋“  ์žฌ๋ฃŒ๋ฅผ ์ •์˜ํ•ด๋‘๊ณ  Boolean์œผ๋กœ ์‚ฌ์šฉํ•  ์žฌ๋ฃŒ๋ฅผ ๋ฐ”๊ฟ”์ค„ ์ˆ˜๋„ ์žˆ๊ฒ ์ฃ .

import Foundation

protocol Sandwich: CustomStringConvertible {
    var hasAvocado: Bool { get set }
    var hasMeatBall: Bool { get set }
    func cost () -> Int
}

extension Sandwich {
    mutating func toggleAvocado() {
        self.hasAvocado.toggle()
    }
    
    mutating func toggleMeatBall() {
        self.hasMeatBall.toggle()
    }
}

class OriginalSandwich: Sandwich {
    var hasAvocado: Bool
    var hasMeatBall: Bool
    
    var description: String {
        return "OriginalSandwich"
    }
    
    init(hasAvocado: Bool, hasMeatBall: Bool) {
        self.hasAvocado = hasAvocado
        self.hasMeatBall = hasMeatBall
    }
    
    func cost() -> Int {
        return 3000
    }
}

class VeganSandwich: Sandwich {
    var hasAvocado: Bool
    var hasMeatBall: Bool
    
    var description: String {
        return "VeganSandwich"
    }
    
    init(hasAvocado: Bool, hasMeatBall: Bool) {
        self.hasAvocado = hasAvocado
        self.hasMeatBall = hasMeatBall
    }
    
    func cost() -> Int {
        return 2500
    }
}

ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ• ์—ญ์‹œ ์กฐํ•ฉํ•ด์•ผํ•  ์žฌ๋ฃŒ๊ฐ€ ์ˆ˜์‹ญ๊ฐ€์ง€๊ฐ€ ๋œ๋‹ค๋ฉด ์ƒ์„ฑ์ž๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•ด์ง€๊ณ , ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•  ๋•Œ ํ•„์—ฐ์ ์œผ๋กœ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋˜, ๋น„๊ฑด ์ƒŒ๋“œ์œ„์น˜์˜ ๊ฒฝ์šฐ์—๋Š” hasMeatBall์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋ฐ๋„, ์ธํ„ฐํŽ˜์ด์Šค์— ๋ฏธํŠธ๋ณผ์ด ์žˆ๋‹ค๋Š” ์ด์œ ๋กœ hasMeatBall์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™(ISP)์„ ์œ„๋ฐฐํ•˜๊ฒŒ ๋˜์ฃ .  ๊ฒฐ์ •์ ์œผ๋กœ ์ƒˆ๋กœ์šด ์žฌ๋ฃŒ๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ๊ตฌ์ฒด ํƒ€์ž…๊ณผ ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ๋ชจ๋‘ ์ˆ˜์ •ํ•ด์ฃผ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐฉ ํ์‡„ ์›์น™(OCP)๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๋Š” ๊ตฌ์กฐ์˜ ์ฝ”๋“œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. 

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์€ ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ์œ ์—ฐํ•˜๊ฒŒ ์ƒˆ๋กœ์šด ์žฌ๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ๋Ÿฐํƒ€์ž„์—๋„ ๋™์ ์œผ๋กœ ์žฌ๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ๊ธฐ์กด ๊ฐ์ฒด์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š์€ ์ฑ„๋กœ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด๋‚˜ ์ •๋ณด๋ฅผ ๊ฐ์ฒด์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. 

 

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด ์ ์šฉํ•˜๊ธฐ

๊ทธ๋Ÿผ ์œ„ ์˜ˆ์ œ์— ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์ ์šฉํ•ด์„œ ์ข€ ๋” ๋‚˜์€ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.

๋จผ์ € ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์ •๋ฆฌํ•ด๋ณด์ฃ . ๊ฐ€์žฅ ์ตœ์ƒ์œ„์— ์žˆ๋Š” Component๋Š” ์šฐ๋ฆฌ๊ฐ€ ์žฌ๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•  ๊ฐ์ฒด์˜ ์ถ”์ƒํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ์˜ˆ์ œ์—์„œ๋Š” Sandwich๊ฐ€ ๋˜๊ฒ ์ฃ ? ๊ทธ๋ฆฌ๊ณ  ์ด ์ถ”์ƒํ™”๋œ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌํ˜„์ฒด๊ฐ€ ConcreteComponent, ์ฆ‰ ์šฐ๋ฆฌ์˜ ์˜ˆ์ œ์—์„œ๋Š” OriginalSandwich์™€ VeganSandwich๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. 

์šฐ๋ฆฌ๋Š” ์ด ๊ตฌ์ฒด ํƒ€์ž…์— Decorator๋ฅผ ํ†ตํ•ด ์žฌ๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ ค๊ณ  ํ•ด์š”. ์ด ์žฌ๋ฃŒ๊ฐ€ ๋  ํƒ€์ž…๋“ค์˜ ์ถ”์ƒ ํด๋ž˜์Šค๊ฐ€ Decorator์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆผ์—์„œ Decorator๊ฐ€ Component๋ฅผ ์ƒ์†ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์‹œ์— Component๋ฅผ ์†Œ์œ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ์š”, ์ด์œ ์— ๋Œ€ํ•ด์„œ ๊ณ ๋ฏผํ•ด๋ด…์‹œ๋‹ค. 

๋จผ์ €, ์šฐ๋ฆฌ๊ฐ€ ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด cost ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๊ณ  ํ•ด๋ณผ๊ฒŒ์š”, ๊ทธ๋Ÿผ ๊ธฐ๋ณธ ํƒ€์ž…(OriginalSandwich, VeganSandwich)์— Decorator๋ฅผ ํ†ตํ•ด ์ถ”๊ฐ€ํ•œ ์žฌ๋ฃŒ๋“ค์˜ ๊ฐ€๊ฒฉ์ด ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ ์šฐ๋ฆฌ๋Š” Sandwich ํƒ€์ž…์— ๋”ฐ๋กœ ์žฌ๋ฃŒ์— ๋Œ€ํ•œ ๋ชฉ๋ก์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋Š” ์•Š์•„์š”. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™๊ณผ ๊ฐœ๋ฐฉ ํ์‡„ ์›์น™์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด์„œ์š”. 

๊ทธ๋ž˜์„œ Decorator์˜ ๊ฐ ํƒ€์ž…๋“ค์€ Sandwich ํƒ€์ž…์„ ์†Œ์œ ํ•˜๊ณ , ๋™์‹œ์— Sandwich์—์„œ ๊ฐ•์ œํ•˜๋Š” ๋ฉ”์„œ๋“œ๋“ค์„ ๊ตฌํ˜„ํ•ด์„œ ์žฌ๊ท€์ ์œผ๋กœ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ธˆ์•ก์„ ์–ป๊ธฐ ์œ„ํ•œ ๋กœ์ง์€ ๋”ฐ๋ผ์„œ ์ด๋ ‡๊ฒŒ ๋˜๊ฒ ์ฃ . ๋ฉ”์„œ๋“œ ์š”์ฒญ์ด ๋“ค์–ด๊ฐ€๋ฉด ๊ฐ€์žฅ ์ค‘์‹ฌ์— ์žˆ๋Š” ConcreteComponent๊นŒ์ง€ ๋„๋‹ฌํ•˜๊ณ , ์ด๋ฅผ base case๋กœ ์‚ผ์•„์„œ ๊ฐ ์žฌ๋ฃŒ์˜ ๊ฐ€๊ฒฉ๋“ค์„ ๋”ํ•ด ์ตœ์ข…์ ์ธ ๊ธˆ์•ก์„ ๊ตฌํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•œ๋‹ค๋ฉด ๊ฐ ์žฌ๋ฃŒ๋“ค์€ ๋‹ค๋ฅธ ์žฌ๋ฃŒ๋“ค์˜ ์ƒํ™ฉ์— ์‹ ๊ฒฝ์“ธ ํ•„์š”๋„ ์—†๊ณ , Concrete Component๋„ ์ž์‹ ์—๊ฒŒ ์–ด๋–ค ์žฌ๋ฃŒ๊ฐ€ ์ถ”๊ฐ€๋˜๋Š”์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†์–ด์ง‘๋‹ˆ๋‹ค. 

๊ทธ๋Ÿผ ๊ตฌํ˜„ํ•ด๋ณผ๊นŒ์š”?

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด ๊ตฌํ˜„ํ•˜๊ธฐ

๋จผ์ € ๊ธฐ๋Šฅ๋“ค์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค.

protocol Decorating: Sandwich {
    var sandwich: Sandwich { get set }
}

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ”„๋กœํ† ์ฝœ์„ ๋งŒ๋“ค์—ˆ๊ณ , ๋‚ด๋ถ€์—๋Š” Sandwich ํƒ€์ž…์˜ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง€๋„๋ก ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์„ฑํ–ˆ์–ด์š”. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ”„๋กœํ† ์ฝœ์€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ ์šฉํ•  ๊ตฌ์ฒด์ ์ธ ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ์ƒ์†ํ•˜๊ฑฐ๋‚˜ ์ฑ„ํƒํ•ด์•ผํ•˜๊ณ , ๋™์‹œ์— ๊ฐ™์€ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฉค๋ฒ„๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ์ด ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” ์žฌ๋ฃŒ์˜ ์—ญํ• ์„ ํ•  Concrete Decorator๋Š” ์•„๋ณด์นด๋„์™€ ๋ฏธํŠธ๋ณผ์ด ๋˜๊ฒ ์ฃ .

class Avocado: Decorating {
    var sandwich: Sandwich
    var description: String {
        return self.sandwich.description + " + Avocado"
    }
    
    init(sandwich: Sandwich) {
        self.sandwich = sandwich
    }
    
    func cost() -> Int {
        return self.sandwich.cost() + 1000
    }
}

class MeatBall: Decorating {
    var sandwich: Sandwich
    var description: String {
        return self.sandwich.description + " + MeatBall"
    }
    
    init(sandwich: Sandwich) {
        self.sandwich = sandwich
    }
    
    func cost() -> Int {
        return self.sandwich.cost() + 1500
    }
}

๊ฐ ์žฌ๋ฃŒ๋“ค์€ ์ƒ์„ฑ์ž๋กœ Sandwich ํƒ€์ž…์„ ๋ฐ›์•„์š”. ํ•˜์ง€๋งŒ ์ด ํƒ€์ž…์€ ์žฌ๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•  ๋Œ€์ƒ์ธ Concrete Component ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, Decorator๋„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Decorator๋ฅผ ์ƒ์„ฑ์ž๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜์ฃ . ์™œ ์ด๋ ‡๊ฒŒ ํ•˜๋Š”์ง€ ์ž˜ ์ดํ•ด๊ฐ€ ์•ˆ๋˜์–ด๋„ ๊ดœ์ฐฎ์•„์š”..! ์ž ์‹œ ํ›„์— ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋ฉด ๋ช…์พŒํ•ด์ง‘๋‹ˆ๋‹ค!

๊ฐ ์žฌ๋ฃŒ๋“ค์ด ๊ตฌํ˜„ํ•˜๋Š” ๋ฉ”์„œ๋“œ์™€ ์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋Š” ์ƒ์„ฑ์ž๋กœ ๋ฐ›์€ Sandwich ํƒ€์ž…์ด ๊ฐ€์ง„ ๋™๋ช…์˜ ๋ฉ”์„œ๋“œ/์—ฐ์‚ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ทธ ๋ฐ˜ํ™˜ ๊ฐ’์— ์ž์‹ ์˜ ์ƒํƒœ ๊ฐ’์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. 

๊ฐ™์€ ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๊ฐ€์žฅ ๊ธฐ๋ณธ ํƒ€์ž…์ด ๋  Concrete Component๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ๊ตฌํ˜„์„ ํ•ฉ๋‹ˆ๋‹ค.

class OriginalSandwich: Sandwich {
    var description: String {
        return "OriginalSandwich"
    }
    
    func cost() -> Int {
        return 3000
    }
}

class VeganSandwich: Sandwich {
    var description: String {
        return "VeganSandwich"
    }
    
    func cost() -> Int {
        return 2500
    }
}

์žฌ๊ท€๋ฅผ ์ข…๋ฃŒํ•  base case๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์—์š”. Concrete Component์˜ ๊ฒฝ์šฐ์—๋Š” ์ž์‹ ์˜ ๊ฐ’๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์žฌ๋ฃŒ๋“ค์ด ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ์—์„œ ๊ฐ ์žฌ๋ฃŒ๋“ค์˜ ๊ฐ’์ด ์ถ”๊ฐ€๋˜์–ด์„œ ์ฒ˜๋ฆฌ๋˜๋Š” ๊ฒƒ์ด์ฃ . 

์ด์ œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค!

let avocadoMeatBallSandwich = MeatBall(sandwich: Avocado(sandwich: OriginalSandwich()))

print(avocadoMeatBallSandwich.cost())
print(avocadoMeatBallSandwich)

์•„๊นŒ Sandwich ํƒ€์ž…์„ Decorator๊ฐ€ ๋”ฐ๋ฅด๊ณ  ์žˆ์—ˆ์ฃ ? ๊ทธ ์ด์œ ๋Š” ์ƒˆ๋กœ์šด ์žฌ๋ฃŒ๊ฐ€ ์ถ”๊ฐ€๋œ Concrete Component๋ฅผ ์ƒ์„ฑ์ž์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ธ์Šคํ„ด์Šค๋งˆ๋‹ค ๋”ฑ ํ•˜๋‚˜๋งŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์„๊ฑฐ์—์š”.

์ˆœ์„œ๋Œ€๋กœ ๋ฏธํŠธ๋ณผ, ์•„๋ณด์นด๋„ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ๋งˆ์ง€๋ง‰์— Concrete Component์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฃผ๋ฉด,

์ด ๊ทธ๋ฆผ์ด ๊ทธ๋Œ€๋กœ ๊ตฌํ˜„์ด ๋ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ๊ฒฐ๊ณผ๋„ ์šฐ๋ฆฌ๊ฐ€ ์˜๋„ํ•œ๋Œ€๋กœ์˜€์–ด์š”. 

 

์ƒˆ๋กœ์šด ์žฌ๋ฃŒ์˜ ๋“ฑ์žฅ

๋งŒ์•ฝ ๋Ÿฐํƒ€์ž„์— "ํ† ๋งˆํ† " ๋ผ๋Š” ์žฌ๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ์ƒ๊ฒผ๋‹ค๋ฉด ์–ด๋–จ๊นŒ์š”? ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ๋ฉ๋‹ˆ๋‹ค.

๋จผ์ € Decorator ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” ํ† ๋งˆํ†  ํƒ€์ž…์„ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

class Tomato: Decorating {
    var sandwich: Sandwich
    var description: String {
        return self.sandwich.description + " + Tomato"
    }
    
    init(sandwich: Sandwich) {
        self.sandwich = sandwich
    }
    
    func cost() -> Int {
        return self.sandwich.cost() + 500
    }
}

๊ฐ„๋‹จํ•˜๊ฒŒ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒ ์ฃ ? ๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด์— ๋งŒ๋“ค์—ˆ๋˜ ๋ฏธํŠธ๋ณผ ์•„๋ณด์นด๋„ ์ƒŒ๋“œ์œ„์น˜์— ํ† ๋งˆํ† ๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

let tomatoAdded = Tomato(sandwich: avocadoMeatBallSandwich)
print(tomatoAdded.cost())
print(tomatoAdded)

๊ธฐ์กด ๊ฐ์ฒด๋Š” ๊ทธ๋Œ€๋กœ ๋‘” ์ฑ„๋กœ ํ† ๋งˆํ† ๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฐ์ฒด๋ฅผ ์†์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์žฌ๋ฃŒ๊ฐ€ ์ž์ฃผ ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜, ๋™์ ์œผ๋กœ ๊ธฐ์กด ๊ฐ์ฒด์— ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด OCP๋ฅผ ๋”ฐ๋ฅด๋ฉด์„œ๋„ ์œ ์—ฐํ•˜๊ฒŒ ๋ณ€๊ฒฝ์— ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!