今まで経験したことのない奇妙な設計状況があります... Objective-C を使用していた場合は、カテゴリで解決しますが、C# 2.0 を使用する必要があります。
まず、いくつかの背景。このクラス ライブラリには 2 つの抽象化レイヤーがあります。最下層は、コンテンツをスキャンするコンポーネントのプラグイン アーキテクチャを実装します (申し訳ありませんが、それ以上具体的にはできません)。各プラグインは独自の方法でスキャンを行いますが、受け入れるコンテンツの種類によってプラグインが異なる場合もあります。この議論とは関係のないさまざまな理由から、プラグイン インターフェイスを介して Generics を公開したくありませんでした。そのため、コンテンツ タイプごとに IScanner インターフェイスと派生インターフェイスを作成しました。
最上層は、さまざまな部分を含む複合コンテンツ形式を受け入れる便利なラッパーです。スキャナーが異なれば、関心のあるコンテンツ タイプに応じて、コンポジットのさまざまな部分が必要になります。したがって、必要な関連部分を探して、コンポジット コンテンツを解析する IScanner 派生インターフェイスごとに固有のロジックを用意する必要があります。
これを解決する 1 つの方法は、単純に別のメソッドを IScanner に追加して、各プラグインに実装することです。ただし、2 層設計の要点は、プラグイン自体が複合フォーマットについて知る必要がないようにすることです。これを解決するための強引な方法は、上位層に型テストとダウンキャストを配置することですが、将来的に新しいコンテンツ タイプのサポートが追加されるため、これらは慎重に維持する必要があります。実際には Visitor は 1 つしかないため、Visitor パターンもこの状況では扱いにくいですが、さまざまな Visitable タイプの数は時間の経過とともに増加するだけです (つまり、これらは Visitor が適している反対の条件です)。さらに、IScanner のシングル ディスパッチをハイジャックしたいだけなのに、ダブル ディスパッチはやり過ぎのように感じます。
Objective-C を使用している場合は、IScanner から派生した各インターフェイスでカテゴリを定義し、そこに parseContent メソッドを追加します。カテゴリは上位層で定義されるため、プラグインを変更する必要はなく、同時に型テストの必要もありません。残念ながら、C# 拡張メソッドは基本的に静的であるため機能しません (つまり、呼び出しサイトで使用される参照のコンパイル時の型に関連付けられており、Obj-C カテゴリのような動的ディスパッチにフックされていません)。言うまでもなく、C# 2.0 を使用する必要があるため、拡張メソッドを使用することさえできません。:-P
この問題を C# で解決するためのクリーンでシンプルな方法はありますか。
編集:現在の設計の構造を明確にするのに役立ついくつかの擬似コード:
interface IScanner
{ // Nothing to see here...
}
interface IContentTypeAScanner : IScanner
{
void ScanTypeA(TypeA content);
}
interface IContentTypeBScanner : IScanner
{
void ScanTypeB(TypeB content);
}
class CompositeScanner
{
private readonly IScanner realScanner;
// C-tor omitted for brevity... It takes an IScanner that was created
// from an assembly-qualified type name using dynamic type loading.
// NOTE: Composite is defined outside my code and completely outside my control.
public void ScanComposite(Composite c)
{
// Solution I would like (imaginary syntax borrowed from Obj-C):
// [realScanner parseAndScanContentFrom: c];
// where parseAndScanContentFrom: is defined in a category for each
// interface derived from IScanner.
// Solution I am stuck with for now:
if (realScanner is IContentTypeAScanner)
{
(realScanner as IContentTypeAScanner).ScanTypeA(this.parseTypeA(c));
}
else if (realScanner is IContentTypeBScanner)
{
(realScanner as IContentTypeBScanner).ScanTypeB(this.parseTypeB(c));
}
else
{
throw new SomeKindOfException();
}
}
// Private parsing methods omitted for brevity...
}
編集: 明確にするために、私はこのデザインについてすでに多くのことを考えてきました。私には多くの理由がありますが、そのほとんどを共有することはできません。興味深いのですが、元の質問をかわしているため、まだ回答を受け入れていません。
実際、Obj-C では、この問題をシンプルかつエレガントに解決できました。問題は、C# で同じ手法を使用できるかということです。代替案を探すのは構いませんが、公平を期すために、それは私が尋ねた質問ではありません。:)