Swift/Design_Pattern

[Swift 디자인 패턴] Observer Pattern (옵저버) - 디자인 패턴 공부 20

Dev_Pingu 2021. 6. 28. 00:24
반응형

안녕하세요 Pingu입니다!🐧

 

지난 글에서는 행동 패턴 중 하나인 Memento Pattern(메멘토)에 대해 알아봤었는데요, 이번 글에서는 또 다른 행동 패턴 중 하나인 Observer Pattern(옵저버)에 대해 알아보도록 하겠습니다.

옵저버 패턴이란?

Observer Pattern(옵저버)이란 관찰 중인 객체에서 발생하는 이벤트를 여러 다른 객체에 알리는 메커니즘을 정의할 수 있는 디자인 패턴입니다. iOS에서는 Swift 5.1 버전부터 Combine 프레임워크에 Publisher가 추가되어 이를 사용할 수 있고 NotificationCenter도 비슷하게 사용할 수 있습니다.

  • Subject (Publisher)
    • Observer들을 가지고 있으며 개수는 제한이 없습니다.
    • Observer들을 추가, 제거하는 인터페이스를 제공합니다.
  • Concrete Subject (Publisher)
    • Concrete Observer 객체의 상태를 저장합니다.
    • 상태가 변경되면 Observer(Subscriber)에게 알립니다.
  • Observer (Subscriber)
    • 객체의 변경 사항을 알려야하는 객체에 대한 Update 인터페이스를 제공합니다.
    • 상태가 변경되면 
  • Concrete Observer (Subscriber)
    • Concrete Subject (Publisher) 객체에 대한 참조를 유지합니다.
    • Subject(Publisher)의 상태와 일관성을 유지합니다.
    • 객체의 상태와 일관성을 유지하기 위해 update 인터페이스를 구현합니다.

옵저버 패턴은 언제 쓰나요?

다른 객체의 상태가 변경될 때마다 어떤 행동을 하고 싶다면 옵저버 패턴을 사용하면 됩니다. 이러한 패턴은 iOS에서는 ViewController에 Observer(Subscriber)가 있고, Model에 Subject(Publisher)가 있는 MVC 패턴에서 사용할 수 있습니다. 이를 통해 Model은 ViewController의 타입에 대해 알 필요 없이 상태가 변경될 때마다 이를 ViewController에 전달할 수 있습니다. 따라서 여러 개의 ViewController가 하나의 Model의 변경사항을 사용할 수 있게 됩니다.

 

현실에서 옵저버 패턴을 사용하는 상황을 이해해보자면, 애플 매장에 아이폰이 매진 상태라고 해보겠습니다. 그래서 아이폰이 입고될 때 DB의 모든 고객에게 아이폰 입고 알림을 주게 되면 누군가에게는 좋은 알림이지만 누군가에게는 스팸이 될 수 있겠죠? 따라서 아이폰이 입고될 때 알림을 받고 싶은 고객에 대해서만 해당 알림을 주는 등의 방법을 사용하면 좋을 것 같아요. 이럴 때 사용할 수 있는 방법이 옵저버 패턴입니다.

옵저버 패턴의 결과?

장점

  • Open / Close 원칙을 지킬 수 있습니다. Subject(Publisher)의 코드를 수정하지 않고 새로운 Observer(Subscriber) 클래스를 추가할 수 있습니다. 물론 그 반대도 가능합니다.
  • 런타임에서 객체간 관계를 설정할 수 있습니다.

단점

  • Observer(Subscriber)에게 알림이 가는 순서는 보장하지 않습니다.
  • Observer, Subject의 관계가 잘 정의되지 않으면 원하지 않는 동작이 발생할 수도 있습니다.

예제

그럼 Observer Pattern을 Swift 언어로 간단하게 구현해보겠습니다.

Combine 프레임워크나 NotificationCenter로 구현 할 수도 있지만, 그냥 직접 간단하게 한 번 구현해볼게요.

아까 애플 매장에 아이폰이 매진된 상황을 구현해보면 재밌을것 같으니 그 상황을 구현해보겠습니다.

 

먼저 Subject(Publisher) 인터페이스를 정의합니다.

// Subject(Publisher) Interface
protocol Publisher {
    var observers: [Observer] { get set }
    func subscribe(observer: Observer)
    func unSubscribe(observer: Observer)
    func notify(message: String)
}

옵저버를 추가하는 subscribe, 옵저버를 제거하는 unSubscribe, 추가된 옵저버들에게 알림을 보내는 notify 메서드를 가져야 하는 프로토콜을 정의합니다. 그런 뒤 여기서 사용하는 Observer 프로토콜을 정의해줍니다.

// Observer(Subscriber) Interface
protocol Observer {
    var id: String { get set }
    func update(message: String)
}

 

그럼 이제 Publisher 역할을 하는 Apple Store 클래스를 정의합니다.

// Concrete Subject(Publisher)
class AppleStore: Publisher {
    var observers: [Observer]
    
    init(observers: [Observer]) {
        self.observers = observers
    }
    
    func subscribe(observer: Observer) {
        self.observers.append(observer)
    }
    
    func unSubscribe(observer: Observer) {
        if let index = self.observers.firstIndex(where: { $0.id == observer.id }) {
            self.observers.remove(at: index)
        }
    }
    
    func notify(message: String) {
        for observer in observers {
            observer.update(message: message)
        }
    }
}

그리고 AppleStore의 알람을 수신할 Customer 클래스도 정의해줍니다.

// Concrete Observer(Subscriber)
class Customer: Observer {
    var id: String
    init(id: String) {
        self.id = id
    }
    func update(message: String) {
        print("\(id)님 \(message)수신\n")
    }
}

 

이렇게 하면 간단하게 Observer Pattern을 모두 구현 하게 됩니다.

그럼 잘 작동하는지 실행해보겠습니다!

위의 코드를 실행하게 되면 위와 같은 결과를 보여줍니다.

appleStore의 알림을 받는 Customer 객체인 pingu, roby를 추가해주고 notify 메서드를 호출하면 2명의 Customer 객체에 잘 전달되는 것을 볼 수 있습니다. 그런 뒤 roby를 unSubscribe로 제거해준 뒤 notify 메서드를 호출하면 Pingu 객체에만 전달되는 것을 볼 수 있습니다.

 

이렇게 Observer Pattern을 알아보고 간단하게 구현도 해봤습니다.

자주 사용하던 패턴인데 실제로 구현해보니 재밌었던 것 같아요!

 

전체 코드는 여기에서 볼 수 있습니다.

 

감사합니다.

반응형