あなたが説明している問題は、ダブルディスパッチと呼ばれています。この名前は、2つのオブジェクトのタイプ(したがって、double)に基づいて、実行(ディスパッチ)するコードのビットを決定する必要があるという事実に由来しています。
通常、OOには単一のディスパッチがあります。オブジェクトに対してメソッドを呼び出すと、そのオブジェクトのメソッドの実装が実行されます。
あなたの場合、2つのオブジェクトがあり、実行される実装は両方のオブジェクトのタイプによって異なります。基本的に、これまでは標準的なOOの状況のみを扱っていた場合に「気分が悪い」という、これによって暗示される結合があります。しかし、それは実際には間違っていません。OOの基本機能が直接解決に適しているという問題領域から少し外れています。
動的言語(またはこの目的のために十分に動的であるリフレクション付きの静的型付き言語)を使用している場合は、基本クラスのディスパッチャーメソッドを使用してこれを実装できます。擬似コードの場合:
class OperatorBase
{
bool matchCompareCriteria(var other)
{
var comparisonMethod = this.GetMethod("matchCompareCriteria" + other.TypeName);
if (comparisonMethod == null)
return false;
return comparisonMethod(other);
}
}
ここでは、この言語には、呼び出されるすべてのクラスに組み込みのメソッドがGetMethod
あり、名前でメソッドを検索できることと、オブジェクトのタイプの名前を取得するすべてのオブジェクトのTypeNameプロパティがあることを想像しています。したがって、他のクラスがGreaterThan
であり、派生クラスにmatchCompareCriteriaGreaterThanというメソッドがある場合、そのメソッドを呼び出します。
class SomeOperator : Base
{
bool matchCompareCriteriaGreaterThan(var other)
{
// 'other' is definitely a GreaterThan, no need to check
}
}
したがって、正しい名前でメソッドを作成するだけで、ディスパッチが発生します。
引数の型によるメソッドのオーバーロードをサポートする静的に型付けされた言語では、連結された命名規則を発明する必要がなくなります。たとえば、ここではC#です。
class OperatorBase
{
public bool CompareWith(object other)
{
var compare = GetType().GetMethod("CompareWithType", new[] { other.GetType() });
if (compare == null)
return false;
return (bool)compare.Invoke(this, new[] { other });
}
}
class GreaterThan : OperatorBase { }
class LessThan : OperatorBase { }
class WithinRange : OperatorBase
{
// Just write whatever versions of CompareWithType you need.
public bool CompareWithType(GreaterThan gt)
{
return true;
}
public bool CompareWithType(LessThan gt)
{
return true;
}
}
class Program
{
static void Main(string[] args)
{
GreaterThan gt = new GreaterThan();
WithinRange wr = new WithinRange();
Console.WriteLine(wr.CompareWith(gt));
}
}
モデルに新しいタイプを追加する場合は、以前のすべてのタイプを調べて、何らかの方法で新しいタイプと対話する必要があるかどうかを自問する必要があります。したがって、すべての型は、他のすべての型と相互作用する方法を定義する必要があります-相互作用が本当に単純なデフォルトである場合でも(「return以外は何もしないtrue
」など)。その単純なデフォルトでさえ、あなたがしなければならない意図的な選択を表しています。これは、最も一般的なケースでコードを明示的に記述する必要がないという便利さによって偽装されています。
したがって、すべてのオブジェクトに分散させるのではなく、外部テーブル内のすべてのタイプ間の関係をキャプチャする方が理にかなっている場合があります。一元化することの価値は、タイプ間の重要な相互作用を見逃したかどうかを即座に確認できることです。
したがって、タイプを別の辞書にマップする辞書/マップ/ハッシュテーブル(言語で呼び出されるものは何でも)を持つことができます。2番目のディクショナリは、2番目のタイプをこれら2つのタイプの正しい比較関数にマップします。一般的なCompareWith関数は、そのデータ構造を使用して、呼び出す適切な比較関数を検索します。
どちらのアプローチが正しいかは、モデルに含まれる可能性のあるタイプの数によって異なります。