2

我正在尝试比较 compareCriteria。简单的,例如“between”和“inArray”或“greaterThan”。我对这些类使用多态性。他们从 compareCriteria 接口共享的一种方法是“matchCompareCriteria”。

我试图避免的是让每个类检查它们应该匹配的 compareCriteria 的类型。例如,inArray 对象将检查 matchCompareCriteria 是否传递了一个 inArray 对象,如果没有,它将返回 false,如果它知道如何比较。

也许 instanceof 在这种情况下是完全合法的(对象知道自己),但我仍在寻找可能的方法来避免它。有任何想法吗?

伪代码示例:

betweenXandY = create new between class(x, y)
greaterThanZ = create new greaterThan class(z)
greaterThanZ.matchCompareCriteria(betweenXandY)

如果 X 和 Y 大于 Z,它将返回 true。

编辑:

1) instanceof 是我目前在 matchCompareCriteria 方法中看到的。我想摆脱它

2) matchCompareCritera 检查一个 compareCriteria 是否被另一个包含。如果一个的所有可能值都包含在另一个中,则返回 true。对于 compareCriteria 的许多组合,比较它们甚至没有意义,因此它们返回 false(例如 betweenAlfa 和 betweenNum 将不兼容)。

4

5 回答 5

8

您描述的问题称为双重调度。该名称来自于您需要根据两个对象的类型(因此: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 函数将使用该数据结构来查找要调用的正确比较函数。

哪种方法是正确的取决于您的模型中最终可能使用的类型数量。

于 2009-09-12T23:05:40.030 回答
5

既然你提到instanceof了,我假设我们在这里使用 Java。这可能让您利用重载。考虑一个名为 的接口SomeInterface,它有一个方法:

public interface SomeInterface {
    public boolean test (SomeInterface s);
}

现在,我们定义了两个(巧妙命名的)类,它们实现了SomeInterface:Some1Some2. Some2很无聊:test总是返回 false。但是 Some1test在给定 a 时会覆盖该函数Some2

public class Some1 implements SomeInterface {
    public boolean test (SomeInterface s) {
        return false;
    }

    public boolean test (Some2 s) {
        return true;
    }
}

这让我们可以避免逐行 if 语句来进行类型检查。但有一个警告。考虑这段代码:

Some1 s1 = new Some1 ();
Some2 s2 = new Some2 ();
SomeInterface inter = new Some2 ();

System.out.println(s1.test(s2));     // true
System.out.println(s2.test(s1));     // false
System.out.println(s1.test(inter));  // false

看到第三个测试了吗?即使inter是 type Some2,它也被视为 a SomeInterface。重载解决方案是在 Java 的编译时确定的,这可能会使它对您完全无用。

这让您回到第一方:使用instanceof(在运行时评估)。即使你这样做,它仍然是一个糟糕的设计。您的每个班级都必须了解所有其他班级。如果您决定添加另一个,则必须返回所有现有的添加功能来处理新类。这很快就变得非常难以维护,这是设计糟糕的一个好兆头。

重新设计是有序的,但如果没有更多信息,我不能给你一个特别好的推动正确的方向。

于 2009-09-10T19:21:51.183 回答
1

如果我理解得很好,您的方法依赖于类型检查。这是很难避免的,而且多态性无法解决问题。从您的示例中, inArray需要检查参数的类型,因为方法的行为取决于此。你不能通过多态来做到这一点,这意味着你不能在你的类上放置多态方法来处理这种情况。那是因为你的 matchCompareCriteria 取决于参数的类型而不是它的行为

instanceof当您检查对象的类型以选择要具有的行为时,不使用的规则是有效的。显然,该行为属于您检查其类型的不同对象。但是在这种情况下,您的对象的行为取决于您传入的对象的类型并属于调用对象,而不是像以前那样依赖于被调用对象。这种情况类似于您覆盖时的情况equals()。您进行类型检查,以便传入的对象与对象的类型相同this,然后实现您的行为:如果测试失败,则返回 false;否则,进行相等测试。

结论:instanceof在这种情况下使用是可以的。

这是 Steve Yegge 的一篇较长的文章,我认为使用一个简单直接的示例可以更好地解释。我认为这非常适合您的问题。

记住:多态性是好的,除非它不是。:)

于 2009-09-12T22:34:41.873 回答
1

Smalltalk 方法是在层次结构中引入更多层。因此betweengreaterThan将是rangedCompareCriteria(或其他)的子类,并且rangeCompareCriteria::matchCompareCriteria在被问及自身的两个实例是否可比较时将返回true 。

说到这一点,您可能希望将“matchCompareCriteria”重命名为更好地表达意图的名称。

于 2009-09-12T23:07:18.857 回答
1

您需要创建一个名为 Criteria 的超类或接口。然后每个具体的子类都将实现 Criteria 接口。之间,大于等是标准。

Criteria 类将指定接受 Criteria 的 matchCompareCriteria 方法。实际的逻辑将驻留在子类中。

您正在寻找策略设计模式或模板设计模式。

于 2009-09-10T18:21:44.527 回答