LoginSignup
18
20

More than 5 years have passed since last update.

protocol extension でmixin的にObserverパターンを実装してみる

Last updated at Posted at 2016-03-22

勉強のためにswiftのprotocol extensionでObserverパターンを実装してみました。

  • Protocol extension でmixin的に利用できるので、クラス構造への影響を少なく利用できる
  • 監視する側をObserver, 管理される側をObservableというprotocolで定義
  • 通知するイベントをprotocolのassociated types でEventTypeとして定義
  • where節でObserverObservableEventTypeが同じであることを条件として指定

import Foundation

public protocol Observer: Equatable {
    typealias EventType
    func listen(event: EventType)
}

public protocol Observable {
    typealias ObserverType: Observer
    typealias EventType
    var observers: [ObserverType] { get set}
    mutating func addObserver(observer: ObserverType)
    mutating func removeObserver(observer: ObserverType)
    func notify(event: EventType)
}

extension Observable where EventType == ObserverType.EventType {
    mutating public func addObserver(observer: ObserverType) {
        var os = observers
        os.append(observer)
        observers = os
    }
    mutating public func removeObserver(observer: ObserverType) {
        guard let i = observers.indexOf(observer) else { return }
        observers.removeAtIndex(i)
    }
    public func notify(event: EventType) {
        for o in observers {
            o.listen(event)
        }
    }
}

使い方

定義部分

  • Observerをconformする側にEventTypeの指定
  • Observableをconformする側にEventTypeの指定に加えて、Observerの指定、observersのgetter, setterの実装
  • EventTypeNews, PublisherObservable, SubscriberObserver

public enum News {
    case Morning(String, Publisher)
    case Noon   (String, Publisher)
    case Evening(String, Publisher)
    case Extra  (String, Publisher)
}

public struct Subscriber: Observer, Equatable {
    public typealias Event = News
    public var id:   String
    public var name: String

    public func listen(event: Event) {
        switch event {
        case .Morning(let content, let publisher): read(content: content, publisher: publisher)
        case .Noon   (let content, let publisher): read(content: content, publisher: publisher)
        case .Evening(let content, let publisher): read(content: content, publisher: publisher)
        case .Extra  (let content, let publisher): read(content: content, publisher: publisher)
        }
    }

    public func read(content content: String, var publisher: Publisher) {
        print("\(name) reads morning paper: \(content)\n")
        if content.containsString(name) {
            publisher.removeObserver(self)
            print("\(name) unsubscribed\n")
        }
    }
}

public func ==(lhs: Subscriber, rhs: Subscriber) -> Bool {
    return lhs.id == rhs.id
}



public class Publisher: Observable {
    public var name: String
    public typealias ObserverType = Subscriber
    public typealias EventType    = News
    private var _observers: [ObserverType] = []
    public  var  observers: [ObserverType] {
        get { return _observers }
        set { _observers = newValue }
    }
    public init(name: String) {
        self.name = name
    }
}

利用部分


var sentence_spring = Publisher(name: "Bunshun")

var kiyo     = Subscriber(id: "kiyo"   ,  name: "Kiyo")
var kimutaku = Subscriber(id: "kimura" ,  name: "Kimutaku")
var nakai    = Subscriber(id: "nakai"  ,  name: "Nakai")
var yoshiki  = Subscriber(id: "yoshiki",  name: "yoshiki")


sentence_spring.addObserver(kimutaku)
sentence_spring.addObserver(nakai)
sentence_spring.addObserver(kiyo)
sentence_spring.addObserver(yoshiki)


sentence_spring.notify(.Morning("Becky and GESU NO KIWAMI are in inappropriate relationship!!!", sentence_spring))

sentence_spring.removeObserver(nakai)
sentence_spring.removeObserver(kimutaku)

sentence_spring.notify(.Evening("SMAP will be broken up!?"  , sentence_spring))
sentence_spring.notify(.Extra  ("Kiyo is arrested!!"        , sentence_spring))
sentence_spring.notify(.Extra  ("Sean K is pure japanease!!", sentence_spring))

感想

  • Observableconformする際に getter, setterを定義する必要があるところをどうにかしたい。
  • protocol extensionとassociated typeの組み合わせは強力なので、もっと小慣れて積極的に使っていきたい。

ここ にxcode Playgroundで実行できる全体のコードをあげています。

こちらからは以上です。

18
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
20