[iOS/Swift] 앱 개발의 실무적 접근으로 배울 디자인 패턴 ~Mediator~

이 기사 시리즈는, iOS/Swift 엔지니어인 집필자 개인이,
매우 일반적인 iOS 앱 개발의 일반적인 상황
Swift의 핵심 라이브러리 및 프레임 워크에서 사용되는 패턴
주목하여 디자인 패턴을 다시 배워 본 기록입니다.

관련 기사 목록
[iOS/Swift] 앱 개발의 실무적 접근 방식으로 배우는 디자인 패턴

Mediator 패턴 개요


  • Mediator란 「중개자」라는 의미입니다.
  • 여러 객체 간에 직접 상호 작용하지 않고 Mediator를 통해 상호 작용합니다.
  • 각 오브젝트가 의존하는 상대를 Mediator만으로 하는 것으로, 오브젝트끼리가 느슨하게 결합되어, 관련 오브젝트가 많은 경우에는 보수성을 향상할 수 있습니다.
  • GoF 디자인 패턴은 동작과 관련된 패턴으로 분류됩니다.

  • 사용소



    실용적인 예로는 UIPageViewController 아래에있는 하위 ViewController 간의 알림이 있습니다.
    아래 예제 코드는 빨간색 배경의 ViewController가 숨겨질 때 파란색 배경의 ViewController에 알림을 제공하는 예제입니다.



    UIPageViewController가 Mediator의 역할을 담당합니다.
    이점은 새로운 Receiver가 되는 아이 ViewController가 늘어났을 때, 기존의 아이 ViewController는 변경하지 않아도 된다는 점입니다.

    샘플 코드



    Xcode 11.3에서 단일 페이지 응용 프로그램을 새로 만들고 ViewController.swift에 다음 코드를 복사하면 작동합니다.
    // MARK: - プロトコル
    protocol Receiver {
        func receive(message: String)
    }
    
    protocol Sender {
        func send(message: String)
    }
    
    protocol Mediator: class {
        var recipients: [Receiver] { get }
        func send(message: String)
    }
    
    // MARK: - PageViewControllerの子ViewController
    // 送り手のViewController
    final class SenderViewController: UIViewController {
        // Message送信をMediatorに委譲する
        weak var messageDelegate: Mediator?
    
        override func viewDidDisappear(_ animated: Bool) {
            super.viewDidDisappear(animated)
            // 表示されなくなった時にMessageを送信(Mediatorに委譲)
            messageDelegate?.send(message: "SenderViewController.viewDidDisappear()")
        }
    }
    
    // 受け手のViewController ※Receiverプロトコルに準拠
    final class RecieverViewController: UIViewController, Receiver {
        func receive(message: String) {
            print("\(message)を受信しました")
        }
    }
    
    // MARK: - PageViewController
    final class ViewController: UIPageViewController {
        // 送り手のViewController
        let senderViewController = SenderViewController()
        // 受け手のViewController
        let receiverViewController = RecieverViewController()
        // 子ViewController配列
        var controllers = [UIViewController]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // PageViewControllerの子ViewControllerを設定
            senderViewController.view.backgroundColor = .red
            controllers.append(senderViewController)
    
            receiverViewController.view.backgroundColor = .blue
            controllers.append(receiverViewController)
    
            setViewControllers([controllers[0]], direction: .forward, animated: false, completion: nil)
            dataSource = self
    
            // SenderViewControllerの委譲先に自分を設定する
            senderViewController.messageDelegate = self
        }
    }
    
    // PageViewControllerをMediatorプロトコルに準拠
    extension ViewController: Mediator {
        var recipients: [Receiver] {
            // 子ViewControllerの中でReceiverプロトコルに準拠しているものを返す
            return controllers.filter { $0 is Receiver } as! [Receiver]
        }
    
        func send(message: String) {
            for recipient in recipients {
                recipient.receive(message: message)
            }
        }
    }
    

    ※설명에는 무관합니다만 움직일 때에 코피페가 필요한 코드
    // UIPageViewControllerDataSource
    extension ViewController: UIPageViewControllerDataSource {
        // 右にスワイプ(戻る)
        func pageViewController(_ pageViewController: UIPageViewController,
                                viewControllerBefore viewController: UIViewController) -> UIViewController? {
            guard
                let index = controllers.firstIndex(of: viewController),
                index > 0
                else {
                    return nil
            }
            return controllers[index - 1]
        }
    
        // 左にスワイプ(進む)
        func pageViewController(_ pageViewController: UIPageViewController,
                                viewControllerAfter viewController: UIViewController) -> UIViewController? {
            guard
                let index = controllers.firstIndex(of: viewController),
                index < controllers.count - 1
                else {
                    return nil
            }
            return controllers[index + 1]
        }
    
        func presentationCount(for pageViewController: UIPageViewController) -> Int {
            return controllers.count
        }
    }
    

    좋은 웹페이지 즐겨찾기