在此示例中,我如何介绍继承来实际解决代码重复问题,如何解决代码重复问题

时间:2018-11-16 13:08:00

标签: java android inheritance dry

假设我有一个使用其他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),我引入了代码重复!

我现在需要复制getScreenElementsgetScreenElementdraw方法。在这种情况下,您需要覆盖它们,因为例如,getScreenElements始终会返回Background,BOXONE和BOXTWO,即使您实际上需要Background,BOXONE,BOXTWO,BOXTHREE和BOXFOUR(例如,在使用“ OtherLayout”的Scene中。

希望这很有意义,并且有人可以提供巧妙的解决方案!

要澄清:

  • 任何时候BasicLayout都应该有Background,BOXONE和BOXTWO
  • 任何时候OtherLayout的背景,BOXONE,BOXTWO, BOXTHREE和BOXFOUR。

3 个答案:

答案 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
}

在您的示例中,代码库中的某处会知道创建BasicLayoutOtherLayout。重构该代码并提取地图,然后传递给构造函数。例如:

 // 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);