在这种情况下我该如何替换instanceof?

发布于 2024-08-03 23:49:30 字数 788 浏览 9 评论 0原文

我正在尝试比较compareCriteria。简单的,如 ' Between' 和 'inArray' 或 '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。对于compareCriteria 的许多组合来说,比较它们甚至没有意义,因此它们返回 false(例如 BetweenAlfa 和 BetweenNum 是不兼容的)。

I'm trying to compare compareCriteria. Simple ones like 'between' and 'inArray' or 'greaterThan'. I use polymorphism for these classes. One method they share from the compareCriteria interface is 'matchCompareCriteria'.

What I'm trying to avoid is having each class check for the type of compareCriteria they should match against. Eg the inArray object will check if matchCompareCriteria is passed an inArray object, if not it will return false, in the case it is it knows how to compare.

Maybe instanceof is perfectly legitimate in this case (the objects know about themselves) but still I'm looking at possible ways to avoid it. Any ideas?

pseudo-code example:

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

if X and Y are greater than Z it will return true.

edit:

1) instanceof is what I see, for now, as needed in the matchCompareCriteria method. I'd like to get rid of it

2) matchCompareCritera checks if a compareCriteria is contained by another. If all possible values of one is contained by the other it returns true. For many combinations of compareCriteria it doens't even make sense to compare them so they return false (like betweenAlfa and betweenNum would be incompatible).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

じее 2024-08-10 23:49:30

您描述的问题称为双重调度。该名称源自这样一个事实:您需要根据两个对象的类型(因此:double)来决定执行(分派)哪一段代码。

通常,在面向对象中,存在单次调度 - 调用对象上的方法会导致该对象的方法实现执行。

在您的情况下,您有两个对象,要执行的实现取决于两个对象的类型。从根本上来说,当您以前只处理过标准的 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 之外什么也不做”)”)。即使是简单的默认设置也代表着您必须做出的深思熟虑的选择。这是因为在最常见的情况下不必显式编写任何代码的便利性所掩盖。

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

因此,您可以拥有一个字典/映射/哈希表(无论在您的语言中如何称呼)将一种类型映射到另一个字典。第二个字典将第二类型映射到这两种类型的正确比较函数。一般的 CompareWith 函数将使用该数据结构来查找要调用的正确比较函数。

哪种方法是正确的取决于您的模型最终可能有多少类型。

The problem you are describing is called double dispatch. The name comes from the fact that you need to decide which bit of code to execute (dispatch) based on the types of two objects (hence: double).

Normally in OO there is single dispatch - calling a method on an object causes that object's implementation of the method to execute.

In your case, you have two objects, and the implementation to be executed depends on the types of both objects. Fundamentally, there is a coupling implied by this which "feels wrong" when you've previously only dealt with standard OO situations. But it's not really wrong - it's just slightly outside the problem domain of what the basic features of OO are directly suited to solving.

If you're using a dynamic language (or a static-typed language with reflection, which is dynamic enough for this purpose) you can implement this with a dispatcher method in a base class. In pseudo-code:

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

        return comparisonMethod(other);
    }
}

Here I'm imagining that the language has a built-in method in every class called GetMethod that allows me to look up a method by name, and also a TypeName property on every object that gets me the name of the type of the object. So if the other class is a GreaterThan, and the derived class has a method called matchCompareCriteriaGreaterThan, we will call that method:

class SomeOperator : Base
{
    bool matchCompareCriteriaGreaterThan(var other)
    {
        // 'other' is definitely a GreaterThan, no need to check
    }
}

So you just have to write a method with the correct name and the dispatch occurs.

In a statically typed language that supports method overloading by argument type, we can avoid having to invent a concatenated naming convention - for example, here it is in 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));
    }
}

If you were to add a new type to your model, you would need to look at every previous type and ask yourself if they need to interact with the new type in some way. Consequently every type has to define a way of interacting with every other type - even if the interaction is some really simple default (such as "do nothing except return true"). Even that simple default represents a deliberate choice you have to make. This is disguised by the convenience of not having to explicitly write any code for the most common case.

Therefore, it may make more sense to capture the relationships between all the types in an external table, instead of scattering it around all the objects. The value of centralising it will be that you can instantly see whether you have missed any important interactions between the types.

So you could have a dictionary/map/hashtable (whatever it's called in your language) that maps a type to another dictionary. The second dictionary maps a second type to the right comparison function for those two types. The general CompareWith function would use that data structure to look up the right comparison function to call.

Which approach is right will depend on how many types you're likely to end up with in your model.

傲鸠 2024-08-10 23:49:30

由于您引用了 instanceof,我假设我们在这里使用 Java 进行工作。这可能会让您利用重载。考虑一个名为 SomeInterface 的接口,它只有一个方法:

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

现在,我们定义两个(巧妙命名的)类来实现 SomeInterfaceSome1 和 <代码>Some2。 Some2 很无聊:test 总是返回 false。但是,当给定 Some2 时,Some1 会覆盖 test 函数:

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 的类型为 Some2,它也会被视为 SomeInterface。 Java 中的重载解析是在编译时确定的,这可能会使其对您完全无用。

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

重新设计是有序的,但如果没有更多的信息,我无法给你一个特别好的推动,让你朝着正确的方向前进。

Since you reference instanceof, I'm assuming we're working in Java here. This might let you make use of overloading. Consider an interface called SomeInterface, which has a single method:

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

Now, we define two (cleverly named) classes that implement SomeInterface: Some1 and Some2. Some2 is boring: test always returns false. But Some1 overrides the test function when given a Some2:

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

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

This lets us avoid having line after line of if statements to do type checking. But there is a caveat. Consider this code:

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

See that third test? Even though inter is of type Some2, it is treated as a SomeInterface instead. Overload resolution is determined at compile-time in Java, which could make it completely useless for you.

That puts you back at square one: using instanceof (which is evaluated at run-time). Even if you do it that way, it's still a bad design. Each of your classes has to know about all of the others. Should you decide to add another, you have to go back to all existing ones to add in functionality to handle the new class. This gets horribly unmaintainable in a hurry, which is a good sign that the design is bad.

A redesign is in order, but without a lot more information, I can't give you a particluarly good push in the right direction.

初心 2024-08-10 23:49:30

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

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

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

You need to create a super class or interface called Criteria. Then each concrete sub class will implement the Criteria interface. between, greaterthan etc are criterias.

the Criteria class will specify the matchCompareCriteria method which accepts a Criteria. The actual logic will reside in the sub classes.

You are looking for either the Strategy Design pattern or the Template Design PAttern.

俏︾媚 2024-08-10 23:49:30

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

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

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

这是 Steve Yegge 撰写的一篇较长的文章,我认为它解释得更好,使用一个简单明了的例子。我认为这很适合你的问题。

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

If I understand well, your method relies on type checking. That is quite hard to avoid, and polymorphism fails at solving the problem. From your example, inArray needs to check the type of the parameter because the behaviour of the method depends on this. You can't do that by polymorphism, meaning you can't put a polymorphic method on your classes to handle this case. That is because your matchCompareCriteria depends on the type of the parameter rather than on its behaviour.

The rule of not using instanceof is valid when you check the type of an object to chose what behaviour to have. Clearly, that behaviour belongs on the different objects whose type you check. But in this case, the behaviour of your object depends on the type of the object you are passed in and belongs on the calling object, not on the called ones as before. The case is similar to when you override equals(). You do a type check so that the object passed in is the same type as this object and then implement your behaviour: if the test fails, return false; otherwise, do the equality tests.

Conclusion: using instanceof is ok in this case.

Here is a longer article from Steve Yegge, which explains better, I think, using a simple and straightforward example. I think this maps great to your problem.

Remember: Polymorphism is good, except when it's not. :)

给不了的爱 2024-08-10 23:49:30

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

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

The Smalltalk approach would be to introduce more layers to the hierarchy. So between and greaterThan would be subclasses of rangedCompareCriteria (or something), and rangeCompareCriteria::matchCompareCriteria would return true when asked if two instances of itself were comparable.

Speaking of which, you probably want to rename "matchCompareCriteria" to something that expresses the intent a bit better.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文