如何确定要保留哪些条件以及哪些条件可以溶解成多态性

时间:2013-08-18 22:25:07

标签: java oop

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

澄清我的问题:

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

他们这两方面的事实意味着:

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

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

  3. 现在我的问题。

    1. 以上哪个选项属实?

    2. 如果选项1为真,那么'如何判断if-else是否可以被多态替换'?

3 个答案:

答案 0 :(得分: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();

仍然有效,并在myObjectClassC实例时调用新类中的方法,甚至无需重新编译!

有多种多态性,请参阅维基百科:Polymorphism (computer science)Polymorphism in object-oriented programming。正如你在这些页面上看到的那样,这是一个有争议的领域。

我们在这里使用的那种叫做子类型多态,由于它的实现方式也称为动态绑定

答案 1 :(得分:1)

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

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

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

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

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

想象一下,如果想要每个形状的区域,可以执行以下操作:

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());
}

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

答案 2 :(得分:0)

  

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

     

以上哪个选项属实?

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

例如基本上每个switch例如

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();
}

条件仍在评估中,而不是由您的代码明确评估。