通过它们实现的接口来不同地处理数组元素

时间:2012-09-25 03:58:25

标签: java

假设有一个ArrayList包含许多不同类型为“Element”的元素。所有这些都将被无限循环地吸引到屏幕上。

这些“元素”可以实现0..n接口,如“Movable”,“Selectable”等。

到目前为止我尝试的是迭代所有元素并检查如下界面:

ArrayList<Element> allElements;
...

for (Element element : allElements) {
  if (element instanceof Movable) {
    ((Movable)element).move();
  }
  if (element instanceof Selectable) {
    ...
  }

  element.draw();
}
但是,我对这种方法并不满意,因为它违反了开放/封闭原则(也可能违反了其他数千项原则)。当然,我可以重新设计它,以便每个元素根据它实现的接口做出响应:

for (Element element : allElements) {
  element.move(); // element checks itself if it can move, and if true moves
  ...
}

缺点是Element类必须为每个可能的行为提供签名,即它必须提供每个接口的方法,并且可以在子类中覆盖它们。这也不是我想要的,因为它会使Element类膨胀。

我也尝试过按界面选择Elements:

getElementsByInterface(Movable, allElements) { ... }

(Movable是接口,allElements是ArrayList)

但它没有编译。看来Java在运行时不再知道接口了。

长话短说(抱歉我可能过于冗长的帖子): 什么是让阵列元素根据其能力(/接口)做出反应的最佳设计解决方案?

4 个答案:

答案 0 :(得分:2)

我认为这种做法没有任何内在错误。

这种情况基本上有two approaches

  1. 使用多态和Visitor pattern。由于LSP的(接口方面),这引入了每个处理程序必须实现所有操作的“缺点”。

  2. 使用基于本地类型的切换(这在ScalaADTs等语言中更常见,其中此方法为acceptable/common; Java语法有点笨拙in comparssion)。

  3. 这里没有违反(多态)open/closed 因为正在使用接口并且“动作”仍然留在实现中(它仍然支持多态,因此“打开” );只有行动的选择不是。

答案 1 :(得分:2)

我建议正确的答案取决于您的代码库中出现多少次或类似的switch语句。

如果它恰好出现在一个地方,那么将其保留原样可能很好。

如果它出现在一堆地方,那么使用访问者模式(或者可能是命令和复合模式,根据您期望的变化的性质)重构它可能是一个好主意。

答案 2 :(得分:1)

此处Visitor pattern可能很有用。

答案 3 :(得分:0)

为什么你不能使用反射?

我可以建议你一种方式。

创建一个新的界面,说CanDoAction:

public interface CanDoAction{
    String getAction();
}

然后从此扩展接口,对于getAction方法,返回要为相应接口调用的函数的名称。例如,在类实现Movable的情况下,为getAction返回“move”。

然后循环:

for (Element element : allElements) {
    String actionName = ((CanDoAction)element).getAction();

    try {
        element.getClass().getDeclaredMethod(actionName, null)
                .invoke(element, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}