0

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

这些“元素”可以实现 0..n 个接口,如“可移动”、“可选”等。

到目前为止,我尝试的是遍历所有元素并检查以下接口:

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 类膨胀。

我还尝试通过这样的界面选择元素:

getElementsByInterface(Movable, allElements) { ... }

(Movable是接口,allElements是ArrayList)

但它不编译。似乎 Java 在运行时不再知道接口。

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

4

4 回答 4

2

我认为这种方法本质上没有任何问题。

这种情况基本上有两种方法

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

  2. 使用基于本地类型的切换(这在像Scala这样的带有ADT的语言中更常见,这种方法是可以接受的/常见的;Java 语法在比较中有点笨拙)。

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

于 2012-09-25T04:31:13.453 回答
2

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

如果它恰好发生在一个地方,那么保持原样可能没问题。

如果它出现在一堆地方,那么使用访问者模式(或者可能同时使用命令和复合模式,具体取决于您期望的更改的性质)重构它可能是一个好主意。

于 2012-09-25T04:54:47.847 回答
1

访问者模式在这里可能很有用。

于 2012-09-25T04:01:55.593 回答
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();
    }
}
于 2012-09-25T04:20:02.190 回答