多态性如何替换循环中的 if-else 语句?

How can Polymorphism replace an if-else statement inside of a loop?

提问人:WolfmanDragon 提问时间:2/6/2009 最后编辑:CommunityWolfmanDragon 更新时间:11/1/2015 访问量:15307

问:

多态性如何替换循环中的 if-else 语句或 Switch?特别是,它总是可以取代if-else吗?我在循环中使用的大多数 if-thens 都是算术比较。这个问题就是从这个问题中衍生出来的。

int x;
int y;
int z;

while (x > y)
{
     if (x < z)
     {
         x = z;
     }
}

这将如何与多态性一起工作?
注意:我是用 Java 写的,但对任何 OOL 都感兴趣。

与语言无关的 OOP 循环多 态性 IF-语句

评论

0赞 WolfmanDragon 2/6/2009
@ken 谢谢你发现错别字。我经常这样做。

答:

3赞 Mitch Wheat 2/6/2009 #1

多态性在您提供的示例中并不真正适用。

请参阅此 SO 答案

评论

0赞 Pete Kirkham 2/6/2009
如果是 Smalltalk 而不是 java,那么这个例子就已经是多态的了。
0赞 Pete Kirkham 2/8/2009
这个问题与语言无关。将代码直接翻译成 smalltalk 将是多态的。
27赞 Jon Skeet 2/6/2009 #2

当每个情况对应于不同的类型时,多态性通常会替换 switch 语句。因此,与其拥有:

public class Operator
{
    string operation;

    public int Execute(int x, int y)
    {
         switch(operation)
         {
             case "Add":
                 return x + y;
             case "Subtract":
                 return x - y;
             case "Multiply":
                 return x * y;
             case "Divide":
                 return x / y;
             default:
                 throw new InvalidOperationException("Unsupported operation");
         }
    }
}

您将拥有:

public abstract class Operator
{
    public abstract int Execute(int x, int y);
}

public class Add : Operator
{
    public override int Execute(int x, int y)
    {
        return x + y;
    }
}

// etc

但是,对于您提供的比较类型的决策,多态性确实无济于事。

评论

0赞 Isa 9/18/2021
创建这四个类如何决定使用哪个类?看来您可能仍然需要一个开关,或者如果其他。
0赞 Jon Skeet 9/18/2021
@Isa:因为调用代码只需要使用Operator,正确的代码在执行时会根据具体类型自动执行。“对象是什么类型”决定了使用什么代码。您仍然需要一些东西来创建正确类型的实例,但这在代码中的单独位置(并且很可能已经有效地具有单独的路径)。
0赞 Rsc Rsc 8/29/2022
Jon,那么我理解您需要使用开关或 if-then 才能创建正确的类是否正确?
0赞 Jon Skeet 8/29/2022
@RscRsc:不 - 我回答的第一部分是 switch 语句;我回答的第二部分是取代它的基于继承的方法。
0赞 Rsc Rsc 8/31/2022
哦,我错过了一些东西。假设有一个方法在答案的第二部分调用基于类的代码。 那么你写的基于类的代码怎么会知道要实例化“Add”类呢?CalculateMe("Add", 5, 2);
3赞 mP. 2/6/2009 #3

多态性只能取代 if 测试,当 if 测试基本上根据对象的“类型”分配给各种方法时。例如,如果对象是 X 类型,则调用 foo,如果它是 Y 调用栏,等等。在这个人为的例子中,我们将使用方法 bad() 定义一个接口 DoSonething。X 和 Y 都将实现 Baz,并让它们各自的 baz() 为 X 调用 foo(),为 Y 调用 bar()。只需调用 baz() 即可消除对 if 测试的需要。

3赞 Rafał Dowgird 2/6/2009 #4

在 Smalltalk 中,“if”实际上是布尔值中的一种多态方法。在以下示例中:

[ x>y ] whileTrue:  
  [   
    ( x<z ) ifTrue: [ x:=z ]        
  ]

该消息以 “execute this block” 和 “ignore this block” 的形式实现,因此根据计算结果,将调用任何一个实现。ifTrue:aBlockTrueFalse(x<z)

因此,在 Smalltalk 中,多态性默认替换了所有 if-else 构造:)

评论

0赞 WolfmanDragon 2/10/2009
披露:我从未使用过smalltalk。这里不是还有一个布尔值吗?如果是这样,它是否只是一个包裹在多态性的花哨的 IF-Then-Else 语句?我只是要求澄清。
0赞 Rafał Dowgird 2/10/2009
是的,有一个布尔值,它是通过多态实现的 if-then-else 语句。我知道这种多态性发生在与问题不同的水平上,但我认为这种机制仍然足够有趣,足以证明这个答案是合理的。
1赞 Pete Kirkham 2/6/2009 #5

一种模式是具有表示测试结果的对象和表示要执行的块的对象。结果对象具有覆盖的选择函数,因此如果 Bool 有一个 choose(T positive, T negative),则 Bool.TRUE 将返回 positive 参数,Bool.FALSE 将返回负参数。smalltalk家族语言的朴素实现就是这样工作的。

要以这种形式对 while 循环进行编码,需要对比较 x 和 y 的结果调用 choose 方法,以确定是否为 while 循环内部调用该块,并且该块还使用 compare 和 choose 来设置 x 的值。更直白的翻译是选择将 x 设置为 z 的块或什么都不做的块;相反,它只是使用 Choose 将 X 设置回相同的值。

显然,对于这个简单的案例来说,这是矫枉过正且效率低下的。

public class WantonPolymorphism {

    static class Int32 {
        final int value;
        Int32 ( final int value ) { this.value = value; }

        Compare compare ( Int32 other ) {
            // Java runs out of turtles at this point unless you use
            // an enum for every value
            if ( this.value < other.value ) return Compare.LESS;
            if ( this.value > other.value ) return Compare.GREATER;
            return Compare.EQUAL;
        }
    }

    enum Compare {
        LESS {
            <T> T choose (T less, T equal, T greater) { return less; }
        },
        EQUAL {
            <T> T choose (T less, T equal, T greater) { return equal; }
        },
        GREATER {
            <T> T choose (T less, T equal, T greater) { return greater; }
        };

        abstract <T> T choose (T less, T equal, T greater) ;
    }

    interface Block { Block execute () ; }


    /**
     * Main entry point for application.
     * @param args The command line arguments.
     */
    public static void main (String...args) {
        Block block =  new Block() {
            Int32 x = new Int32(4);
            Int32 y = new Int32(3);
            Int32 z = new Int32(2);

            public Block execute () {
                System.out.printf("x = %d, y = %d, z = %d\n", x.value, y.value, z.value);

                return x.compare(y).choose(done, done, new Block () {
                    public Block execute () {
                        x = x.compare(z).choose(x,x,z);

                        return x.compare(y).choose(done, done, this);
                    }
                });
            }

            Block done = new Block () {
                public Block execute () {
                    System.out.printf("x = %d, y = %d, z = %d\n", x.value, y.value, z.value);
                    System.exit(0);
                    return this;
                }
            };
        };

        for(;;) 
            block = block.execute();
    }
}