2

简要参考:http ://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html

为了澄清我的问题:

互联网上有太多的文章谈论使用多态而不是继承。如果这是真的,那么'if-elses'和'switches'不应该出现在大多数java代码中。

他们这样做的事实意味着:

  1. 在某些情况下,条件不能转换为多态性。

  2. 每个带有 if else 的代码都有可能被重构,但没有完成。

现在我的问题。

  1. 以上哪个选项是正确的?

  2. 如果选项 1 为真,那么“如何确定 if-else 是否可以被多态性替代”?

4

3 回答 3

3

这个想法是避免测试对象类型的条件,例如

public void doThing() {
    if (myObject instanceof ClassA)
        doSomething();
    else if (myObject instanceof ClassB);
        doSomethingElse();
}

我们想要避免此类测试的原因是因为它们是我们代码中未来错误的来源。当一个新类被添加到我们的系统中时,所有这些测试都必须进行审查并可能进行更改。迟早,我们人类会犯错误,然后我们就会有错误的代码。此外,它使我们的代码更复杂,而且通常更慢。在这个简单的例子中,这并不明显,但如果我们以不同的方式测试一堆类型,它会是。

在这种情况下,myObject 是一个既继承ClassAClassB继承的类的实例;让我们调用那个父类ClassP。所以我们可以避免像这样添加一个方法来ClassP调用doSomething

class ClassP {
    // lots of ClassP code

    public void doSomething() {
        // basic implementation of method
    }
}

让我们假设这段代码适用于ClassA,所以对于 ClassA 实例,我们可以只编写代码

myObject.doSomething();

但是因为ClassB我们需要不同的行为,所以我们编码

class ClassB extends ClassP {
    // lots of ClassB code

    public void doSomething() {
        // different implementation of method
    }
}

所以现在我们可以做

myObject.doSomething();

例如ClassB也。我们不再需要那个条件。

强大的是,此代码还将处理将来添加到继承层次结构中的新类型,而无需更改。所以如果我们现在添加一个类

class ClassC extends ClassP {
    // lots of ClassC code

    public void doSomething() {
        // totally different implementation of method
    }
}

然后

myObject.doSomething();

myObject仍然有效,并且在为实例时调用新类中的方法ClassC,甚至不需要重新编译!

有几种多态,参见维基百科:多态(计算机科学)面向对象编程中的多态。正如您在这些页面上看到的那样,这是一个有争议的领域。

我们在这里使用的类型称为子类型多态,并且由于它可以实现的方式也称为动态绑定

于 2013-08-18T23:08:37.453 回答
1

您链接到的文章很好地解释了它。

基本上,它的条件依赖于非常适合转换为多态性的变量类型(例如),而不是依赖于变量的“普通”条件(例如)。if (foo instanceof Bar) ...if (foo == 42) ...

经验法则:如果您发现自己使用instanceof或与 进行比较foo.getClass(),那么您可以使用多态性来简化事情(但并非总是如此)。否则,您的条件可能很好。

请注意,基于type变量或getType()方法(或类似的东西)执行条件逻辑属于相同的规则 - 您基本上只是用手动类型推断代替 Java 的内置类型机制。

一个很好的例子可能是经典的“Square”和“Circle”类,每个类都实现/扩展了“Shape”接口/类。Squares有一个edgeLengthCircles有一个radius

想象一下你想要每个形状的面积,你可能会做以下事情:

interface Shape {

}
class Square implements Shape {
    public float edgeLength;
}
class Circle implements Shape {
    public float radius;
}
...
public static void main (String[] args) {
    Shape shape = new Square(); // or new Circle()
    if (shape instanceof Square) {
        Square square = (Square) shape;
        System.out.println("Area: " + (square.edgeLength * square.edgeLength));
    } else if (shape instanceof Circle) {
        Circle circle = (Circle) shape;
        System.out.println("Area: " + (Math.PI * circle.radius * circle.radius));
    }
}

在这里,用多态替换条件意味着实现一个area方法:

interface Shape {
    float area ( );
}
class Square implements Shape {
    private float edgeLength;

    float area ( ) {
        return edgeLength * edgeLength;
    }
}
class Circle implements Shape {
    private float radius;

    float area ( ) {
        return Math.PI * radius * radius;
    }
}
...
public static void main (String[] args) {
    Shape shape = new Square(); // or new Circle()
    System.out.println("Area: " + shape.area());
}

这个例子突出了另外两个有趣的点:首先,对基类的强制转换通常也表明可能需要对多态性进行重构。其次,请注意第二个示例中改进的封装。

于 2013-08-18T22:49:57.080 回答
0

1在某些情况下,条件不能转换为多态性。
2每个带有 if else 的代码都有可能被重构,但没有完成。

以上哪个选项是正确的?

我会假设每个条件都可以重构为某种多态解决方案,但必须在某个地方评估条件。它们不会消失。

例如基本上每个switcheg

public void doFoo(int foo) {
    switch (foo) {
        case 0:
            System.out.println("Hello");
            break;
        case 5:
            System.out.println("World");
            break;
        default:
            System.out.println("!");
            break;
    }
}

可以变成隐藏(几乎)所有条件的多态解决方案。

private final Map<Integer, Runnable> switchMap = new HashMap<Integer, Runnable>();
{
    switchMap.put(0, new Runnable() {
        public void run() {
            System.out.println("Hello");
        }
    });
    switchMap.put(5, new Runnable() {
        public void run() {
            System.out.println("World");
        }
    });
}
private final Runnable defaultCase = new Runnable() {
    public void run() {
        System.out.println("!");
    }
};

public void doFooPolyMorphed(int foo) {
    Runnable runnable = switchMap.get(foo);
    if (runnable == null)
        runnable = defaultCase;
    runnable.run();
}

条件仍在评估中,只是您的代码没有明确地评估。

于 2013-08-19T00:12:48.643 回答