iOS開発者向けのデザインパターン。 オブザーバーパートI

序文の代わりに


デザインパターン専用のGang of Fourの伝説的な本がリリースされてから17年が経ちました。 このような堅実な期間にもかかわらず、そこに記載されている方法の関連性に挑戦することは困難です。 デザインパターンは生き続け、進化します。 それらは使用され、議論され、scられ、賞賛されます。 残念ながら、多くの人にとって、それらは依然として不必要な抽象化のままです。

人生とさまざまなリソースの両方で同僚とさまざまなプログラミングの問題を議論するとき、特定のパターンの重要性を説明することがしばしば必要です。 そのため、具体的な例を使用して、プログラマーの生活をどれだけ楽にできるかを示すためにアイデアが生まれました。 iOSのようなプラットフォームに関してもです。


オブザーバーパターン


人生の例

学生Vasyaはパーティーに行くのが大好きです。 もっと正確に言うと、彼らは彼が人生の重要な部分を占めていたので、彼は研究所から追放されました。 仕事を探し始めたヴァシャは、自分がほとんど何も知らず、できなかったことに気付きました。 しかし、待ってください... Vasyaにはパンを食べない美しい少女がたくさんいます-彼らを招待なしでクールなパーティーに参加させてください。 Vasyaは、彼の都市のすべての関連機関でも知られています。 最後に、Vasyaは次のことを理解しています。パーティーが成功する(裕福な訪問者が多額のお金を費やした)ために、美しい女の子(このお金を使う人)で満たすのは良いことです。

それで、ヴァシャはポン引きになり、彼自身のビジネスを開きました。 施設の所有者は、流行のプライベートイベントに関するニュースでVasya(Vasyaを知らない?!)に頼ります。 Vasyaは、彼女たちをそこに連れて行くことができると彼女たちの友人に知らせます。 彼はどこから女の子を得るのですか? それは簡単です:光を取る。 彼女は先週、クラブでヴァシャに会った。 スヴェタは長い間エリートパーティーを訪れることを夢見ていたので、ヴァシャに電話番号を書き留めるように頼みました。 彼女はきれいな女の子で、服を着ているので、ヴァシャは同意しました。 スヴェタはヴァシーナのサービスをフォローし始め、 オブザーバーになりました。 しかし、ヴァシャは時が来たら電話をかけると警告した。

1年が経ち、スヴェタは研究所を卒業し、仕事を見つけ、そして最も重要なことに、結婚しました! 彼女はもはやパーティーに行きたくないので、昨日彼女はヴァシャに電話し、彼女のばかげたメッセージで彼女をもう困らせないように頼んだ。 そのため、彼女 Vasyaの(非常に疑わしい) 件名から退会しました。 しかし、彼女はいつでもサインアップできることを知っています(彼女はまだきれいで、若く、格好いいです)。

一方、Vasyaは静止していませんでしたが、事業を拡大しました。 最近、彼はパーティーでリラックスするのが好きで、ノンアルコールカクテルでかわいい女の子を扱う準備ができている非常に裕福なサッカー選手に会いました。 当然、彼らはさまざまなイベントに招待することもできます(主催者は喜んで、Vasyaにもっとお金を払います)。 事業拡大は見過ごされていました。実際、誰が誰に電話するか気にしますか? 女の子もサッカー選手も電話を持っています。 時々、Vasyaは新しいイベントを報告して、自分が話している相手を混乱させることさえあります。

より短い例

Vasyaのビジネスに密接に従った人は、すぐにニュースレターを思い出すことができました。 確かに、私たちはあなたのメールアドレスをお気に入りのリソースに残し、対応するウェブページに毎日アクセスしなくても、重要で興味深いニュースをスパムで受信できます 。 Svetaの場合のように、私たちは常に(実際、運がよければ)ニュースレターの購読を解除できます。

定義

ギャングオブフォー



コメント

そのため、オブザーバーパターンは1対多の関係を定義します。 さらに、変更について報告するオブジェクトはサブジェクトと呼ばれ、変更について報告するオブジェクトはobserversと呼ばれます。

観察者パターンの重要な特徴に注目する価値があります。被験者は観察者についてほとんど何も知らないかもしれません。 そのため、ヴァシャは女の子と選手の間に違いは見ませんでした。

構造

画像

iOS開発者向けオブザーバー


プログラミングに移りましょう。 最初に、特定の例を使用してオブザーバーパターンの独自の実装を分析し、次にCocoaで実装された通知メカニズムを分析します。

独自の実装

App Storeを爆破する完全に新しいゲームを開発しているとしましょう。 次のレベルを通過した後、次の2つのことを行う必要があります。
  1. お祝い画面を表示します。
  2. 新しいレベルへのオープンアクセス。

最初のアクションと2番目のアクションは互いに関連していないことに注意してください。 ランダムな順序で実行できます。 また、近い将来、そのようなアクションの数を増やす必要があると思われます(たとえば、サーバーにデータを送信したり、ユーザーがGame Centerで実績を獲得したかどうかを確認したりする)。

調査したオブザーバーパターンを適用します。 オブザーバープロトコルから始めましょう。

@protocol GameStateObserver <NSObject>

- void completedLevel Level * level withScore NSUInteger スコア。

@end


ここではすべてが透明です。オブザーバーはGameStateObserverプロトコルを実装します。 私たちはこの問題に対処します。

@protocol GameStateSubject <NSObject>

- void addObserver id <GameStateObserver> オブザーバー;
- void removeObserver id <GameStateObserver> オブザーバー;
- void notifyObservers;

@end


興味のあるクラスに移りましょう。 現在のゲームの状態をGameStateクラスのオブジェクトに保存します。 次に、その定義は次のようになります。

@interface GameState NSObject <GameStateSubject> {
...
NSMutableSet * observerCollection;
}

@property readonly レベル*レベル。
@property readonly NSUIntegerスコア。

...
- void updateState;

@end


同時に、ゲームに大きな変更が発生するたびにupdateStateメソッドupdateState呼び出されると考えていupdateStateGameState実装の一部を次に示します。

@implementation GameState

...
- void addObserver id <GameStateObserver> オブザーバー{
[ observerCollection addObject observer ] ;
}

- void removeObserver id <GameStateObserver> オブザーバー{
[ observerCollection removeObject observer ] ;
}

- void notifyObservers {
for id <GameStateObserver> observerCollectionのオブザーバー {
[オブザーバーcompletedLevel self.level withScore self.score ] ;
}
}

- void updateState {
...
if levelCompleted {
[ self notifyObservers ] ;
}
}

@end


これで、レベルの正常な完了を知る必要があるオブジェクトについては、 GameStateObserverプロトコルを実装し、正常な完了の通知をサブスクライブするだけで十分です。 適切なコードは次のようになります。

GameState * gameState = [ [ GameState alloc ] init ] ;
[ gameState addObserver levelManager ] ;
[ gameState addObserver levelViewController ] ;


ここで、 levelViewControllerはゲームプロセスのインターフェイスを担当するコントローラーであり、 levelManagerはレベルに応答するモデルオブジェクトです。

議論

かなり原始的な例を調べましたが、それはどこにでもあります。 ソリューションが非常に柔軟であることはすぐに明らかです。 通知では、一部のデータをパラメーターとして転送することに決定したことに注意してください。 このような実装には長所と短所があります。 GameStateObserverプロトコルの次のバージョンを使用すると便利な場合があります。

@protocol GameStateObserver <NSObject>

- void levelCompleted id <GameStateSubject> 件名;

@end


GameStateSubjectの対応するバリアントは次のようになります。

@protocol GameStateSubject <NSObject>

- void addObserver id <GameStateObserver> オブザーバー;
- void removeObserver id <GameStateObserver> オブザーバー;
- void notifyObservers;

@property readonly レベル*レベル。
@property readonly NSUIntegerスコア。

@end


Cocoaのオブザーバー:通知

Cocoaには、オブザーバーパターンを実装するメカニズムがあることがわかります。 完全に正確に言うと、このようなメカニズムが2つあります。 現時点では、通知メカニズムに焦点を当て、2つ目は将来のために残します。 さらに、すべての微妙な点については説明しませんが、基本的な機能のみを説明します。

非公式には、通知メカニズムにより、通知のサブスクライブ/サブスクライブ解除、およびすべてのサブスクライバーへの通知の送信という2つのことができます。 アラートはNSNotificationクラスのインスタンスです。 アラートはnameタイプNSString文字列名nameによって設定されます。 名前に加えて、アラートにはサブジェクトエンティティとNSDictionaryタイプの追加のuserInfoデータも含まれます。

NSNotificationCenterは、アラートのサブスクライブと配信を担当します。 ほとんどの場合、それにアクセスするには、 defaultCenterクラスメソッドを呼び出します。

メッセージをサブスクライブするために、通知センターにはaddObserver:selector:name:object:メソッドがあります。 最初のパラメーターはオブザーバーであり、2番目はセレクターであり、通知時に呼び出されます。 署名- (void)methodName:(NSNotification *)です。 3番目のパラメーターはアラートの名前、4番目はサブジェクトです。 件名としてnilを渡すと、任意の送信者から通知が配信されます(名前が一致する場合)。 アラートを使用すると、サブスクリプションコードは次のようになります。

GameState * gameState = [ [ GameState alloc ] init ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver levelManager
セレクター @selector levelCompleted :)
名前 @ "LevelCompletedNotification"オブジェクト gameState ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver levelViewController
セレクター @selector levelCompleted :)
名前 @ "LevelCompletedNotification"オブジェクト gameState ] ;


levelCompleted:メソッドの例levelCompleted:

- void levelCompleted NSNotification * 通知{
id <GameStateSubject> subject = [通知オブジェクト] ;
レベル*レベル= subject.level;
NSUIntegerスコア= subject.score;
}


一般的に、 GameStateSubjectプロトコルをGameState直接使用できます。

アラートの登録を解除するには、 removeObserver:またはremoveObserver:name:object:メソッドのいずれかを呼び出す必要があります。

アラートの送信はさらに簡単なプロセスです。 そのために、メソッドpostNotificationName:object:およびpostNotificationName:object:userInfo:ます。 最初はuserInfo nil設定します。 notifyObserversメソッドの新しい実装は次のとおりです。

- void notifyObservers {
[ [ NSNotificationCenter defaultCenter ]
postNotificationName @ "LevelCompletedNotification"オブジェクト self ] ;
}


コメント

アラートを送信するメカニズムは、説明した以上のことができます。 たとえば、上記のコードはすべて同期的です。 アラートを非同期的に送信するには、 NSNotificationQueueアラートNSNotificationQueue使用する必要がありNSNotificationQueue

また、説明したすべてがCocoaとCocoa Touchの両方で機能することにも注意してください。 将来的には、iOSプラットフォームの一部の機能を引き続き使用します。

あとがきの代わりに


私たちが検討し、学んだことを要約しましょう。 だから私たち:

第2部では、オブザーバパターンも実装するKey-Value Observingメカニズムについて学習します。 これからさらに多くのパターンがあります!

有用なソース


Source: https://habr.com/ru/post/J126968/


All Articles