假设有一个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在运行时不再知道接口了。
长话短说(抱歉我可能过于冗长的帖子): 什么是让阵列元素根据其能力(/接口)做出反应的最佳设计解决方案?
答案 0 :(得分:2)
我认为这种做法没有任何内在错误。
这种情况基本上有two approaches:
使用多态和Visitor pattern。由于LSP的(接口方面),这引入了每个处理程序必须实现所有操作的“缺点”。
使用基于本地类型的切换(这在Scala和ADTs等语言中更常见,其中此方法为acceptable/common; Java语法有点笨拙in comparssion)。
这里没有违反(多态)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();
}
}