假设我有一个使用其他Scenes
的应用。每个场景的布局都是由多个ScreenElement
构建的,例如将被绘制到Canvas
的矩形框。对于某些Scenes
,将使用相同的框。因此,鉴于DRY,我想使用继承。
我想要做的是将ScreenElement
存储在HashMap
(或最终是EnumMap
)中,以便我可以调用位于其他地方的ScreenElement
我的代码来更改其属性。
这就是我现在想出的...
这是基本布局:
Public class BasicLayout {
private HashMap<String, ScreenElement> screenElements;
public BasicLayout() {
screenElements = new HashMap<>();
screenElements.put("BACKGROUND", new ScreenElement(...));
screenElements.put("BOXONE", new ScreenElement(...));
screenElements.put("BOXTWO", new ScreenElement(...));
}
public HashMap<String, ScreenElement> getScreenElements() {
return screenElements;
}
public ScreenElement getScreenElement(String elementName) {
return screenElements.get(elementName);
}
public void draw(Canvas canvas) {
for (ScreenElement screenElement: screenElements) {
screenElement.draw(canvas);
}
}
}
然后另一种布局可能是这样的:
Public class OtherLayout extends BasicLayout {
private HashMap<String, ScreenElement> screenElements;
public OtherLayout() {
screenElements = new HashMap<>(super.getScreenElements);
screenElements.put("BOXTHREE", new ScreenElement(...));
screenElements.put("BOXFOUR", new ScreenElement(...));
}
@Override
public HashMap<String, ScreenElement> getScreenElements() {
return screenElements;
}
@Override
public ScreenElement getScreenElement(String elementName) {
return screenElements.get(elementName);
}
@Override
public void draw(Canvas canvas) {
for (ScreenElement screenElement: screenElements) {
screenElement.draw(canvas);
}
}
问题是,通过解决代码重复(我不再需要在OtherLayout
中添加Background,BOXONE和BOXTWO),我引入了代码重复!
我现在需要复制getScreenElements
,getScreenElement
和draw
方法。在这种情况下,您需要覆盖它们,因为例如,getScreenElements
始终会返回Background,BOXONE和BOXTWO,即使您实际上需要Background,BOXONE,BOXTWO,BOXTHREE和BOXFOUR(例如,在使用“ OtherLayout”的Scene
中。
希望这很有意义,并且有人可以提供巧妙的解决方案!
要澄清:
BasicLayout
都应该有Background,BOXONE和BOXTWO OtherLayout
的背景,BOXONE,BOXTWO,
BOXTHREE和BOXFOUR。答案 0 :(得分:1)
这应该有效:
public class BasicLayout {
protected HashMap<String, ScreenElement> screenElements = new HashMap<>();
public BasicLayout()
{
screenElements.put("BACKGROUND", new ScreenElement(...));
screenElements.put("BOXONE", new ScreenElement(...));
screenElements.put("BOXTWO", new ScreenElement(...));
}
public HashMap<String, ScreenElement> getScreenElements() {
return screenElements;
}
public ScreenElement getScreenElement(String elementName) {
return screenElements.get(elementName);
}
public void draw(Canvas canvas) {
for (ScreenElement screenElement: screenElements) {
screenElement.draw(canvas);
}
}
}
public class OtherLayout extends BasicLayout {
public OtherLayout() {
screenElements.put("BOXTHREE", new ScreenElement(...));
screenElements.put("BOXFOUR", new ScreenElement(...));
}
}
答案 1 :(得分:1)
我认为解决继承不是避免重复的好选择。您始终违反Liskov替代原则是某种解决方式(在您的情况下,不是这样解决:)。这就带来了更大的问题。
第二点继承应该仅用于共享行为,而不是数据。
我认为在您的情况下,委派/组成更合适:
public OtherLayout() {
screenElements = new HashMap<>();
screenElements.putAll(new BasicLayout().getScreenElements());
screenElements.put("BOXTHREE", new ScreenElement());
screenElements.put("BOXFOUR", new ScreenElement());
}
从外观上看,这两个类的唯一共同点是源元素映射中的元素。
还有更一般的注释。不要害怕复制和错误的抽象。现代计算机科学先驱(Edsger Dijkstra)的话:
抽象的目的不是模糊,而是创建一个新的 绝对精确的语义水平
答案 2 :(得分:0)
基于示例代码,只需要一个类即可接收将在构造函数中负责的元素。例如:
public class Layout {
private final Map<String, ScreenElement> screenElements;
public Layout(Map<String, ScreenElement> screenElements) {
this.screenElements = screenElements;
}
// other methods
}
在您的示例中,代码库中的某处会知道创建BasicLayout
或OtherLayout
。重构该代码并提取地图,然后传递给构造函数。例如:
// in some method that knows when to create a `BasicLayout` or `OtherLayout`
Map<String, ScreenElement> basicScreenElements = new HashMap<>();
basicScreenElements .put("BACKGROUND", new ScreenElement(...));
basicScreenElements .put("BOXONE", new ScreenElement(...));
basicScreenElements .put("BOXTWO", new ScreenElement(...));
Layout basicLayout = new Layout(basicScreenElements);
// ....
Map<String, ScreenElement> otherScreenElements = new HashMap<>();
otherScreenElements .put("BOXTHREE", new ScreenElement(...));
otherScreenElements .put("BOXFOUR", new ScreenElement(...));
Layout otherLayout = new Layout(otherScreenElements);