Как решить проблему дублирования кода в этом примере, где я ввожу наследование, чтобы фактически решить проблему дублирования кода - PullRequest
0 голосов
/ 16 ноября 2018

Предположим, у меня есть приложение с другим Scenes.Каждая сцена имеет макет, который состоит из ряда ScreenElement с, например, прямоугольных прямоугольников, которые будут нарисованы в Canvas.Для некоторых Scenes будут использоваться те же поля.Поэтому, ввиду DRY, я хочу использовать наследование.

Я хочу сохранить ScreenElement в HashMap (или, в конечном итоге, EnumMap), чтобы я мог вызватьна ScreenElement s в другом месте моего кода, чтобы изменить их атрибуты.

Вот то, что я придумал сейчас ...

Вот базовый макет:

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

Так что дело в том, что, решая дублирование кода (мне больше не нужно добавлять BACKGROUND, BOXONE и BOXTWO в мои OtherLayout)Я ввожу дублирование кода!

Теперь мне нужно продублировать методы getScreenElements, getScreenElement и draw.В этом случае вам необходимо переопределить их, потому что, если вы этого не сделаете, getScreenElements, например, всегда возвращайте BACKGROUND, BOXONE и BOXTWO, даже если вы действительно хотите BACKGROUND, BOXONE, BOXTWO, BOXTHREE и BOXFOUR (например, вScene, который использует `OtherLayout '.

Надеюсь, что это имеет смысл, и у кого-то есть оригинальное решение!

Чтобы уточнить:

  • В любое время BasicLayout должен иметь BACKGROUND, BOXONE и BOXTWO
  • В любое время OtherLayout должен иметь BACKGROUND, BOXONE, BOXTWO, BOXTHREE и BOXFOUR.

Ответы [ 3 ]

0 голосов
/ 16 ноября 2018

Исходя из примера кода, необходим только один класс, который принимает элементы, за которые он будет отвечать в конструкторе. Например:

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);
0 голосов
/ 16 ноября 2018

Я не думаю, что решение наследования - это хороший выбор, чтобы избежать дублирования. Вы всегда нарушаете принцип подстановки Лискова каким-то обоснованным способом (в вашем случае не так устраивает :)). И это приводит к гораздо большим проблемам в будущем.

И второй пункт наследования должен использоваться только для обмена поведением, а не данными.

Я думаю, что в вашем случае делегирование / состав лучше подходят:

public OtherLayout() {
    screenElements = new HashMap<>();
    screenElements.putAll(new BasicLayout().getScreenElements());
    screenElements.put("BOXTHREE", new ScreenElement());
    screenElements.put("BOXFOUR", new ScreenElement());
}

Потому что, судя по всему, единственное, что является общим для двух классов, это элементы в карте исходных элементов.

И более общее примечание. Не бойтесь дублирования как неправильной абстракции. Цитата пионера современной информатики (Эдсгер Дейкстра):

Цель абстракции не в том, чтобы быть расплывчатым, а в создании нового семантический уровень, на котором можно быть абсолютно точным

0 голосов
/ 16 ноября 2018

Это должно работать:

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(...));
    }
}
...