私は Objective C に比較的慣れておらず、既存のデリゲートの周りにカスタム デリゲートを作成することに関するアーキテクチャの質問があります。おそらく、疑似コードで最もよく説明されています。長々と申し訳ありませんが、もっと簡潔にする方法を判断できませんでした。
全体的な望ましいアーキテクチャの概要
Web サービスを呼び出すライブラリに、Web サービスが非同期的に返されるときにコールバックのデリゲートを取るクラスがあります。この低レベル ライブラリは、ファサード (シングルトン) にラップされて、クライアントがそれを認識できないようにします。ファサード メソッドは、低レベル ライブラリのデリゲートを効果的にラップするデリゲートを取得できるため、クライアントは、ファサードが(それ自体がデリゲート) は、Web サービス呼び出しからデータを取得します)。
言い換えると、クライアント インスタンス A はファサード [f makeCallWithDelegate:self] を呼び出し、ファサードはその内部動作を隠しているため、A はファサードのデリゲートのプロトコルを実装するだけで済みます。
疑似コード
低レベルのクラス:
// The low-level class that calls a web service
// LowLevelClass.h
@protocol LowLevelClassDelegate
- (void)success:(LowLevelClass*) c;
@end
@interface LowLevelClass
- (void)makeAsynchronousCallWithDelegate:(id <LowLevelClassDelegate>) d;
@end
// LowLevelClass.m omitted
低レベル クラスをラップするファサード。この問題は、makeCallWithDelegate セレクターと成功デリゲート コールバックに記載されています。
// partial Facade.h
@protocol FacadeDelegate
- (void)success;
@end
@interface Facade <LowLevelClassDelegate> // the facade handles the delegate calls of the LowLevelClass
...
- (void)makeCallWithDelegate:(id <FacadeDelegate>) d;
...
@end
// Facade.m (pseudocode)
@implementation Facade
- (void)makeCallWithDelegate:(id <FacadeDelegate>) the_delegate {
LowLevelClass * llc = ... // get instance
[llc makeAsynchronousCallWithDelegate:self]; // delegate to self, catch events
// Issue: have to somehow pass the_delegate to the "success" method below,
// or have it available
}
// LowLevelClassDelegate implementation, hands result to the FacadeDelegate
- (void)success:(LowLevelClass*) c {
// Issue: need the_delegate to get down here somehow, or be reachable.
[the_delegate success]
}
@end
クライアント:
// Client.m fragment, client implements the FacadeDelegate protocol
...
- (void)makeFacadeGetData {
[facade makeCallWithDelegate:self];
}
- (void)success {
NSLog(@"Hooray");
}
...
要約
Client インスタンスはファサードを呼び出し、自身をデリゲートとして渡します。次に、ファサードは低レベルのクラス インスタンスを呼び出し、ファサード自体をデリゲートとして渡します。ファサードは低レベルのクラス インスタンスからの応答を受け取り、より適切な結果を Client インスタンスに返します。私が取り組んでいる問題は、低レベルのデリゲートの応答をラップするときに正しいファサード デリゲートが確実に使用されるようにする方法です。
インスタンスを使用して上記を繰り返します。 クライアント インスタンス A は、ファサード [f makeCallWithDelegate:self] を呼び出します。クライアント インスタンス B もファサード [f makeCallWithDelegate:self] を呼び出します。クライアント A がクライアント A の呼び出しのデリゲートとして使用され、クライアント B がクライアント B の呼び出しのデリゲートとして使用されていることを確認する必要があります。
設計上の注意事項
LowLevelClass は変更できません。可能であれば、「ユーザーデータ」フィールドなどとして呼び出すデリゲートを渡すことになるでしょう...とにかく、そのアプローチのファンではありません。これは、上位層の知識で下位層クラスを汚染するためです。クラス。
いくつかの理由から、ファサードはシングルトンです。おそらくこれは変更される可能性があり、複数のファサード インスタンスを作成できます。
可能な解決策
A. ファサードが一度に 1 つのクライアントによって使用されることを保証できれば、the_delegate を Facade メンバー変数に格納し、それを Facade の success: セレクターで呼び出すことができます。ただし、それを保証することはできません。いくつかの異なるクライアント インスタンスがファサードを呼び出すことができ、各クライアントの呼び出しのデリゲートが他のクライアントではなく、クライアント自体であることを確認する必要があります (上記のインスタンスの例を使用すると、A は A の呼び出しを処理する必要があり、B は B の呼び出しを処理する必要があります)。 )。
B. ファサードを非シングルトン クラスにして、メンバー変数に the_delegate を格納するだけでよい。たぶんそれが最善の解決策です...しかし、何かがおかしいと感じています。
C. makeCallWithDelegate への各呼び出しに一意のキーを割り当て、ファサード シングルトン インスタンスに呼び出しキーの辞書とその呼び出しに渡されたデリゲートを格納できるのではないかと考えていました。(注: 私にとって意味のある唯一のキーは、LowLevelClass ポインターの実際の値です。結局のところ、ランダムな文字列 ID を生成した場合、問題を先延ばししているだけです。しかし、これはどういうわけか不安定に感じます。) 「成功」メソッドは辞書にアクセスできるため、正しいデリゲートを呼び出します。
// Facade.m (pseudocode)
@implementation Facade
- (void)makeCallWithDelegate:(id <FacadeDelegate>) the_delegate
{
LowLevelClass * llc = ... // get instance
NSString* uniqueKey = makeUniqueKeyFromPointerValue(llc);
[self.delegateDictionary setObject:the_delegate forKey:uniqueKey];
[llc makeAsynchronousCallWithDelegate:self]; // delegate to self, catch events
}
// LowLevelClassDelegate implementation, hands result to the FacadeDelegate
- (void)success:(LowLevelClass*) c
{
NSString* uniqueKey = makeUniqueKeyFromPointerValue(c);
id<FacadeDelegate> del = [self.delegateDictionary objectForKey:uniqueKey];
[del success]
}
@end
上記が十分に詳細で、十分に明確であることを願っています。結局のところ、私は自分自身が混乱しているように感じます (これも、Objective C は初めてです)。
ここまでやってくれたなら、感謝します (そしておめでとう)。この問題に適した設計について何か提案を聞きたいと思います。
ありがとうございました。