您描述的问题称为双重调度。该名称来自于您需要根据两个对象的类型(因此:double)来决定执行(调度)哪位代码的事实。
通常在 OO 中只有一次分派——在对象上调用方法会导致该对象的方法实现被执行。
在您的情况下,您有两个对象,要执行的实现取决于两个对象的类型。从根本上说,当您以前只处理标准 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 属性,它可以让我获得对象类型的名称。因此,如果另一个类是 a 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));
}
}
如果要向模型中添加新类型,则需要查看以前的每个类型并问自己是否需要以某种方式与新类型交互。因此,每种类型都必须定义一种与其他类型交互的方式——即使交互是一些非常简单的默认值(例如“除了返回什么都不做true
”)。即使是那个简单的默认值也代表了你必须做出的深思熟虑的选择。这是因为不必为最常见的情况显式编写任何代码的便利性。
因此,捕获外部表中所有类型之间的关系可能更有意义,而不是将其分散在所有对象周围。集中它的价值在于您可以立即查看是否错过了类型之间的任何重要交互。
所以你可以有一个字典/地图/哈希表(无论它在你的语言中叫什么)将一个类型映射到另一个字典。第二个字典将第二种类型映射到这两种类型的正确比较函数。一般的 CompareWith 函数将使用该数据结构来查找要调用的正确比较函数。
哪种方法是正确的取决于您的模型中最终可能使用的类型数量。