독립된 객체의 상호작용을 캡슐화 하는 것을 목표로 하는 패턴입니다.

해당 패턴을 사용하여 개발자는 독립된 객체들의 상화작용에 다형성을 부여할 수 있습니다.

 

1️⃣ 동기

하나의 행동을 여러 객체로 분할시키는 것은 해당 부분들에 대한 유연성을 확보할 수 있고 단위 테스트가 용이해지는 장점이 분명히 있습니다.

하지만, 잘게 나뉘어진 객체들의 참조를 관리하는 추가적인 작업이 필요하고 추후 기능을 수정하기 위해 여러 클래스들에 대한 서비 클래스 혹은 새로운 타입들을 생성해야하는 작업이 필요할 수 있습니다.

중재자 객체를 활용하면 상호작용에 관련된 책임을 하나의 객체로 응집시켜 위의 문제들을 일정부분 해결할 수 있습니다.

독립된 객체들은 서로의 존재에 대해 알 필요없이 단순히 중재자와 소통을 통해 독립성을 유지합니다.

 

2️⃣ 활용성

  • 여러 객체가 독립적으로 책임을 수행하는 구조를 확립하였지만, 복잡한 상호작용을 효과적으로 관리하고 싶은 경우
  • 한 객체가 많은 객체를 참조하여 해당 객체를 재사용하기 어려운 경우
  • 여러 객체의 분산된 행위를 새로운 서브 클래스 생성 없이 수정하고 싶은 경우

 

3️⃣ 패턴 참여자

패턴 참여자 협력구조

  • Mediator: Colleague객체와 소통하는데 필요한 인터페이스를 정의합니다.
  • ConcreteMediator: Colleague객체들에 대한 생성 및 관리의 책임을 가집니다. 또한, 구체적으로 Colleague객체가 상호작용을 구현합니다.
  • Colleague: 독립적인 동작을 하는 객체 타입들로, Mediator객체를 인터페이스를 통해 참조하고 있습니다.

 

4️⃣ 패턴 참여자 협력 방법

Colleague는 Mediator에 자신이 현재 변동이 발생했음을 알립니다. Mediator는 해당 이벤트를 다른 Colleague로 전달하는등 이벤트 처리를 담당합니다.

 

5️⃣ 결과

  • 서브클래싱을 사용하지 않아도 됩니다.
    중재자 패턴은 객체들간의 상호작용을 중재자에게 응집시키기 때문에 객체들간의 독립성을 강화시킬 수 있습니다.
    따라서 기능 변동시 중재자 클래스만 서브클래싱이 필요하지 독립적인 객체들을 서브클래싱할 필요는 많이 줄어들게 됩니다.
  • 이해하기 쉬운 코드가 됩니다.
    객체간 다 대 다 통신을 일 대 다 통신으로 축약하기에 개발자 입장에서 흐름을 파악하기 쉽습니다.

 

6️⃣ Swift구현

구현시 고려할 점

  • 상호작용에 대한 변경빈도가 낮다면, Mediator를 추상화할 필요는 없습니다.
  • Colleague-Mediator는 다영한 방식으로 소통할 수 있습니다.
    • 이번 장에서는 Colleague는 Mediator인터페이스를 통해 변화를 알리고, Mediator는 구체적인 Colleague타입을 참조하기에 원하는 동작을 전달합니다.
    • 옵저버 패턴을 사용해 상호간의 의존성을 최소화할 수 있습니다.
    • Notification을 활용할 수 있습니다.

 

Director클래스는 Mediator를 의미합니다.

버튼을 표출하는 기능이 있다고 가정하겠습니다.

class Director {
    func createColleagues() { }
    func changed(sender: Colleague) { }
    func showButton() { createColleagues() }
}

 

Director를 약하게 참조하는 Colleague인터페이스와 구체타입을 구현합니다.

class Colleague {
    private(set) weak var director: Director?
    
    init(director: Director) {
        self.director = director
    }
}

class Button: Colleague {
    func onTap() {
        director?.changed(sender: self)
    }
}

class Printer: Colleague {
    func printText(_ str: String) {
        print(str)
    }
}

 

구체적인 Director타입으로 Colleague간 소통 방법을 정의합니다.

final class ConcreteDirector: Director {
    private(set) var button: Button?
    private(set) var printer: Printer?
    
    override func createColleagues() {
        self.button = .init(director: self)
        self.printer = .init(director: self)
    }
    
    override func changed(sender: Colleague) {
        if sender === button {
            
            printer?.printText("button is tapped")
            
        } else if sender === printer {
            // ..
        }
    }
}

 

버튼이 클릭될 시 원하는 텍스트가 프린트 되는 것을 확인할 수 있습니다.

fileprivate let director = ConcreteDirector()
director.showButton()

director.button?.onTap() // button is tapped

 

 

7️⃣ 느낀점

UIKit의 UIViewController에 해당 패턴이 사용되고 있음을 알 수 있었습니다.

해당 패턴의 다소 문제점인 부분은 중재자와 Colleague간의 소통 방식이 함수호출로 이루어진다는 점이 다소 불편할 것이라고 예상됩니다.

옵저버 패턴과 UIKit에서 현재 활용 중인 selector를 활용하면 좀 더 유연한 소통이 가능할 것이라고 생각됩니다.

 

복잡한 요구사항에 대한 책임을 다수의 객체로 분사시켜 개발하는 경우 책에서 언급된 대로 많은 객체들에 대한 참조를 관리하는 것이 다소 까다롭고 복잡성이 오히려 증가되는 상황도 종종 발생했습니다.

결국 하나의 객체가 여러 객체를 참조하여 상호작용을 담당하는 구조가 보통 생기지만, 이것을 설명하기는 다소 까다로웠습니다.

앞으로는 중재자 패턴을 활용해 해당 문제를 해결하였다고 명시할 수 있게다고 생각했으며, 협력을 구성하는 객체들의 독립성중재자의 다형성에 좀 더 초점을 맞출 수 있겠다고 생각했습니다.

 

 

구현 코드는 아래 저장소에서 확인할 수 있습니다.

 

GitHub - J0onYEong/GOF-design-pattern: GOF디자인 패턴 실습코드 레포지토리입니다.

GOF디자인 패턴 실습코드 레포지토리입니다. Contribute to J0onYEong/GOF-design-pattern development by creating an account on GitHub.

github.com