ReactiveCocoa勉強会関西にてObserverパターンについてお話ししましたので、以下にその内容をまとめます。
Observerパターンは、GoFの23のデザインパターンのうちの一つで、モデルが状態の変化をしビューに通知するパターンです。GUIアプリケーションの開発で多用されます。もちろんスマートフォンアプリの開発においても大変役に立つので、いくつかの例を挙げて見ていきます。
static void * Context = &Context;
- (void)anything
{
[object addObserver:self
forKeyPath:NSStringFromSelector(@selector(property))
options:NSKeyValueObservingOptionNew
context:Context];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == Context) {
doSomething();
}
}
上記は監視する側の例です。NSKeyValueObserving
プロトコルのaddObserver:forKeyPath:options:context
を呼び出し、変化があったときobserveValueForKeyPath:ofObject:change:context:
が呼ばれるというものです。NSStringFromSelector()
やcontext
を利用するのは良い習慣です。
self.property = object;
NSMutableArray *arrayProxy =
[array mutableArrayValueForKeyPath:
NSStringFromSelector(@selector(property))];
[proxy addObject:object];
KVOの通知を発火させるためには、Key-Value Codingに準拠するようにプロパティへの代入を行うか、あるいはコレクションのKVOを発火させるためにmutableArrayValueForKeyPath:
が返すプロキシオブジェクトを利用します。
このようにObjective-Cでは、KVOという仕組みによってObserverパターンを簡単に利用できるようになっています。
AndroidにおけるObserverパターンの実現ためには、素朴にObserverパターンを実装するほか、java.beans
パッケージのPropertyChangeSupport
クラスを利用する方法があります。
public class Bean {
private final PropertyChangeSupport mChanges;
private String mName;
public Bean() {
mChanges = new PropertyChangeSupport(this);
}
public void addListener(PropertyChangeListener listener) {
mChanges.addPropertyChangeListener(listener);
}
public void deleteListener(PropertyChangeListener listener) {
mChanges.removePropertyChangeListener(listener);
}
public String getName() { return mName; }
public void setName(String name) {
String oldName = mName;
mName = name;
mChanges.firePropertyChange("name", oldName, mName);
}
}
PropertyChangeSupport
のインスタンスを保持し、PropertyChangeListener
インターフェースのオブジェクトを受け付けます。そして監視させたいフィールドの変更があったとき、firePropertyChange()
を呼び出すことで、PropertyChangeListener
にPropertyChangeEvent
が送られます。
java.beans
パッケージの中で、このPropertyChangeSupport
関連の機能だけがAndroidにも存在しています。KVOと似たような形で利用できるかもしれません。
またjava.util.Observable
を継承するという方法もありますが、あまり使い勝手が良くないかもしれません。
Swiftについてはどうでしょうか。SwiftからもNSObject
のKVOは利用できます。しかし純粋なSwiftのオブジェクトについては、これまでのKVOは利用できません。
Appleのドキュメント“Using Swift with Cocoa and Objective-C”の“Adopting Cocoa Design Patterns”を参照すると、今日現在で
Information forthcoming.
となっています。正式なリリースまでに何か明らかになるとよいのですが。
ここからは与太話ですが、もしSwiftにKVOライクな仕組みが導入されるとしたら、Objective-Cのようなランタイムで動的に何かするのではなく、コンパイルタイムで解決できるような仕掛けになるのではないでしょうか。そのほうがSwift的で、より安全かつ最適化しやすいようにも思います。
以上となりますが、今後もライフワークとしてObserverパターンについて考えていきたいと思います。