위임자라는 말 그대로 프로그램에서 한 개체가 다른 개체를 대신하거나 연계하여 작동하는 패턴이다.
이 디자인 패턴은 어떤 개체의 대신 해줄 기능들을 적어놓은 프로토콜을 정의해서 채택하는 위임자를 변수에 할당해서 사용하는 방식으로 사용한다.
무조건 다른 개체를 만들어서 위임을 한 후 사용해야 하는 것은 아니다.
자신에게 자신을 위임할 수도 있는데 예를 들어UITextFieldDelegate
를 사용하는 경우, 자신(ViewController
)에게 UITextFieldDelegate
를 채택시켜 메서드를 구현하면 자신에게 자신의 일을 위임한 형태가 되기 때문에 기능 확장으로 보일 수 있다.
공식문서의 Delegation 파트의 예제 코드 이다.
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate: AnyObject {
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = Array(repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
weak var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
예제에서는 뱀사다리 게임클래스에서 게임시작
, 게임진행
, 게임종료
기능들을 위임자로 분리한 후 사용을 했다.
이 처럼 결국은 일의 기능을 분리해서 유지보수가 편하게 하고 책임을 낮게 가져가서 좀 더 가벼운 코드를 위해 사용하는 것이라고 생각한다.
프로토콜을 이용하지않고 상속을 이용한 확장은 클래스가 불필요하게 비대해질 수 있다.
예를들어 게임
과 심판
클래스를 두고 게임 승부 판단을 위해 심판을 상속한다면 승부판단을 위한 기능만이 아닌 심판의 다른 프로퍼티나 메서드 들도 따라 오게된다.
그래서 그 기능만을 가지고 있다고 보증하는 프로토콜을 사용해서 사용하는 것이 좋다.
다른 예를 들어서 내가 피자를 시켜먹는 경우다. 만약 내가 피자를 시켜먹는데 직접가서 만들고 포장을 해온다면 어떨까? 나는 동기처리를 한 것이다.
내가 피자를 주문을 해서 그 시간에 다른 일을 할 수 있다면 훨씬 효율적이지 않을까?
delegate도 마찬가지이다. 자신의 일을 delegate에 위임함으로써 위임자가 대신 일을 처리하고 그 동안 다른 일을 처리할 수 있어서 훨씬 효율적이다.
// 피자 주문을 하는 기능을 가진 사람 클래스
class Person {
weak var deliver: DeliveryDelegate?
init(deliver: DeliveryDelegate) {
self.deliver = deliver
}
func orderPizza() {
deliver?.deliver()
}
}
// 배달 프로토콜
protocol DeliveryDelegate: AnyObject {
func deliver()
}
// 배달 프로토콜을 채택한 배달원 클래스
class PizzaDeliver: DeliveryDelegate {
func deliver() {
print("피자를 배달합니다")
}
}
let pizzaDeliver = PizzaDeliver()
let hungryMan = Person(deliver: pizzaDeliver)
hungryMan.orderPizza() // print "피자를 배달합니다"
간단한 예제를 만들어보았다
피자배달원을 생성하고 배고픈 사람에게 배달원을 초기화 시킨 후
배고픈 사람이 주문을 한다.
배고픈 사람이 주문을 한 순간 배달을 위임한 배달원이 피자를 배달하고 "피자를 배달합니다"문구를 출력한다.
근본적인 이해가 중요하다 근본적인 이해가....