代码之家  ›  专栏  ›  技术社区  ›  koen

在这种情况下,如何替换InstanceOf?

  •  2
  • koen  · 技术社区  · 15 年前

    我想比较比较标准。简单的,如“介于”和“Inraray”或“GreaterThan”。我对这些类使用多态性。他们从CompareCriteria接口共享的一个方法是“MatchCompareCriteria”。

    我试图避免的是让每个类检查它们应该匹配的比较标准的类型。例如,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。对于许多比较标准的组合,甚至比较它们都没有意义,因此它们返回错误(如betweenalfa和betweennum是不兼容的)。

    5 回复  |  直到 15 年前
        1
  •  8
  •   Daniel Earwicker    15 年前

    你描述的问题叫做 double dispatch . 名称来自这样一个事实,即您需要根据两个对象的类型(因此:double)来决定要执行(分派)哪一位代码。

    在OO中通常只有一个分派-对对象调用一个方法会导致该对象执行该方法的实现。

    在您的例子中,您有两个对象,要执行的实现取决于这两个对象的类型。从根本上讲,当您以前只处理过标准的OO情况时,这意味着一个耦合“感觉错误”。但这并不是真的错——它只是稍微超出了OO的基本特性直接适合解决的问题领域。

    如果您使用的是动态语言(或具有反射功能的静态类型语言,这对于实现此目的来说是足够动态的),那么可以使用基类中的Dispatcher方法来实现这一点。在伪代码中:

    class OperatorBase
    {
        bool matchCompareCriteria(var other)
        {
            var comparisonMethod = this.GetMethod("matchCompareCriteria" + other.TypeName);
            if (comparisonMethod == null)
                return false;
    
            return comparisonMethod(other);
        }
    }
    

    在这里,我想象语言在每个类中都有一个内置的方法 GetMethod 它允许我按名称查找一个方法,并在每个对象上查找一个type name属性,以获取对象类型的名称。所以如果另一个班是 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 “”。即使是这种简单的违约也代表了你必须做出的慎重选择。为了不必为最常见的情况显式地编写任何代码,这一点被掩盖了。

    因此,捕获外部表中所有类型之间的关系可能更有意义,而不是将其分散在所有对象周围。集中化的价值在于,您可以立即看到是否遗漏了类型之间的任何重要交互。

    所以您可以有一个字典/map/hashtable(在您的语言中,无论它是什么)将一个类型映射到另一个字典。第二个字典将第二个类型映射到这两个类型的右比较函数。常规compareWith函数将使用该数据结构查找要调用的正确比较函数。

    哪种方法是正确的将取决于您可能在模型中使用多少类型。

        2
  •  5
  •   Pesto    15 年前

    因为你提到 instanceof 我假设我们在Java中工作。这可能会让您利用重载。考虑调用的接口 SomeInterface ,它只有一种方法:

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

    现在,我们定义了两个实现 某些接口 : Some1 Some2 . 大约2 很无聊: test 总是返回false。但有些人会忽略 测试 当给定一个 某某 :

    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 属于类型 某某 ,它被视为 某些接口 相反。在Java中编译时确定超载分辨率,这会使它对你完全没有用。

    这让你回到了正题一:使用 运算符 (在运行时评估)。即使你这样做,它仍然是一个糟糕的设计。你的每个班级都必须了解其他所有的班级。如果决定添加另一个类,则必须返回到所有现有的类,才能使用插件功能来处理新类。这是一个可怕的无法弥补的匆忙,这是一个很好的迹象,设计是坏的。

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

        3
  •  1
  •   Vincent Ramdhanie    15 年前

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

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

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

        4
  •  1
  •   Andrei Vajna II    15 年前

    如果我理解的很好,您的方法依赖于类型检查。这是很难避免的,多态性无法解决问题。从你的例子来看,伊纳瑞 需要 检查参数类型,因为方法的行为 取决于 对此。你不能通过多态性来做到这一点,这意味着你不能在类上放置多态方法来处理这种情况。那是因为你的比赛标准取决于 类型 参数而不是其上的 行为 .

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

    结论:使用 运算符 在这种情况下可以。

    这是一个较长的 article 我认为,这是史蒂夫耶格的一个简单而直截了当的例子。我认为这很适合你的问题。

    记得: 多态性是好的,除非它不是 . :)

        5
  •  1
  •   Mark Bessey    15 年前

    smalltalk方法将为层次结构引入更多层。所以 之间 比大 将是 范围比较标准 (或其他东西),以及 范围比较标准::匹配比较标准 会回来 当被问到是否两个例子本身是可比的。

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