티스토리 뷰

목적

특정 인스턴스의 상태를 관찰(observe)하고 있는 구독자에게 변화를 발행(publish)한다.

 

관심있는 무언가를 관찰(observe)한다.

 

 

 

방법

 

  1. 상태 변화를 발행할 수 있는 Publisher 인터페이스를 만든다. (GoF 옵저버 패턴에서는 Subject)
    • subscribe : Observer를 추가한다.
    • unsubscribe : Observer를 제거한다.
    • publish : Observer에 변화된 상태를 발행한다.
  2. Publisher를 관측할 수 있는 Observer 인터페이스를 만든다. 구현에 따라 두가지 방법이 있다.
    • Publisher로부터 변화된 상태를 갱신받는다(push). 정해진 정보만 갱신 받을 수 있다는 단점이 있다.
    • update()에 publisher를 자신을 전달해서 필요한 데이터를 얻을 수 있도록 한다(pull). 원하는 정보를 받을 수 있지만 subject의 getter를 호출하기 때문에 관측하고 갱신한다는 패턴 목적에 조금 어긋난다.

 

 

 

예제

String value를 구독하고 발행할 수 있도록 push 방식의 옵저버 패턴을 구현해보자. 먼저 Publisher(Subject), Observer 인터페이스를 구현한다.

protocol StringPublisher {
    var observers: [StringObserver] { get }
    var value: String { get }
    
    func subscribe(_ observer: StringObserver)
    
    func publish()
}

protocol StringObserver: class {
    func update(_ item: String)
}

StringPublisher를 채택하여 이름 클래스를 만들어보자.

// 1
class PersonName: StringPublisher {
    var observers: [StringObserver] = []
    var value: String
    
		// 2
    var name: String {
        get {
            return self.value
        }
        set {
            self.value = newValue
            publish()
        }
    }
    
    init(_ initialValue: String) {
        self.value = initialValue
    }
    
    func subscribe(_ observer: StringObserver) {
        observers.append(observer)
    }
    
    func unsubscribe(_ observer: StringObserver) {
        observers.removeAll { $0 === observer }
    }
    
		// 3
    func publish() {
        for observer in observers {
            observer.update(value)
        }
    }
}
  1. StringPublisher protocol을 채택한다.
  2. value에 대한 getter, setter 역할을 한다. 새로운 값으로 set 되었을 때 publish()를 호출하여 변경사항을 발행한다.
  3. 자기 자신을 구독하고 있는 observer들의 update()를 호출하여 값을 발행한다.

사람의 이름은 학교에서도 사용하고, 회사에서도 사용할 수 있다. 학교와 회사는 이름을 관찰하고 있다가 이름이 바뀌면 적절히 업데이트 할 수 있어야 한다.

class School: StringObserver {
    func update(_ item: String) {
        print("school updated \(item)")
    }
}

class Company: StringObserver {
    func update(_ item: String) {
        print("company updated \(item)")
    }
}

이렇게 만든 publisher와 observer를 사용하면서 옵저버 패턴이 어떻게 사용되는지 알아보자.

let kim = PersonName("KIM")

let school = School()
let company = Company()

// 1
kim.subscribe(school)
kim.subscribe(company)

// 2
kim.name = "PARK"
// school updated PARK
// company updated PARK

// 3
kim.unsubscribe(school)

// 4
kim.name = "CHOI"
// company updated CHOI
  1. 김씨는 학교와 회사에 소속되어 있다.
  2. 어느날 김씨는 자신의 이름을 PARK으로 개명했다. 개명된 이름을 김씨가 소속되어 있던 학교와 회사 모두 개명된 이름을 전달 받았다.
  3. 김씨가 학교를 졸업하여 더이상 학교 소속이 아니게 되었다.
  4. 김씨가 자신의 이름을 CHOI로 개명하자 이번에는 회사만 전달 받았다.

push 방식으로 구현한 위 예제의 경우 이름을 표현하는 한 변수에 대한 구독을 하고 있지만, PersonName에 다른 값을 구독하고 싶다면 pull 방식을 사용할 수 있다. 구현에 따라 달라질 수 있지만 update()에 publisher 자기 자신을 전달하여 observer가 직접 원하는 값을 얻어가도록 구현할 수 있다.

'Programming > Design' 카테고리의 다른 글

책임연쇄패턴 : Chain of Responsibility  (0) 2017.01.08
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함