Подтвердить что ты не робот

Могу ли я использовать шаблон построителя в Java Enum

Я переписываю код, и я решил, как воссоздать класс, поскольку существует фиксированное количество листов, я создаю их как перечисления. Это решение основано на удобочитаемости паттерна строителя и конструктора телескопа.

Код, который я захватил некоторые .xls файлы, добавляет заголовки (и читает некоторые из других .xls файлов) и, возможно, некоторые под-листы. Затем он объединяет различные листы вместе определенным образом, чтобы делать вкладки в основной книге Excel. Моя проблема заключается в том, что некоторые вкладки рабочей книги занимают различное количество листов - это аргументы. Я пытаюсь применить шаблон построителя. Это код, который я пытаюсь написать:

public enum workBookSheet {
    mySheet1("Name1","mainSheet1.xls",true,1).addSubSheet("pathToSubSheet1.xls"),
    mySheet2("Name2","mainSheet2.xls",true,2).addHeaderSheet("pathToHeaders.xls").addSubsheet("pathtoSubSheet2.xls");

    private String tabName;
    private String mainSheetName;
    private Boolean available;
    private Integer order;
    private String subSheetName;
    private String headerSheetName;

    private workBookSheet(String tabName, String mainSheetName, Boolean available, Integer order){
        this.tabName = tabName;
        this.mainSheetName = mainSheetName;
        this.available = available;
        this.order = order;
    }
    public workBookSheet addSubSheet(String subSheetName){
        this.subSheetName = subSheetName;
        return this;
    }
    public workBookSheet addHeaderSheet(String headerSheetName){
        this.headerSheetName = headerSheetName;
        return this;
    }

}

Ошибка, которую java дает мне, кажется, говорит о том, что Java ожидает моего объявления enum (список с разделителями-запятыми "конструкторы enum" вверху), чтобы иметь только конструктор, а не дополнительные методы. Я могу переместить эти методы в метод "строитель" ниже, без жалоб.

public void buildSheets(){
    mySheet1.addSubSheet("pathToSubSheet1.xls");
    mySheet2.addHeaderSheet("pathToHeaders.xls").addSubSheet("pathtoSubSheet2.xls");
}

Является ли это единственным способом реализации шаблона построителя для перечисления? Это требует, чтобы я запускал отдельный метод, который не слишком много хлопот. Мне кажется, что я нарушаю шаблон, хотя (я думаю, это не так уж плохо, если это работает.)

N.B Я хорошо осмотрелся, чтобы узнать, задал ли кто-нибудь еще этот вопрос, на SO или в другом месте в Интернете. Самое близкое, что я нашел, было вопросом здесь на Enums и Factories, но это не совсем отвечает на мой вопрос. Также я знаю, что это не совсем шаблон построения, так как у меня нет отдельного класса, который затем принимает метод build(), который создает новое перечисление. Я предполагаю, что это корень проблемы в моем первоначальном дизайне, но я относительно новичок в Java.

Итак, есть ли лучший способ использовать шаблон построителя для перечисления Java? Или это то, что у меня "достаточно близко"?

4b9b3361

Ответ 1

Хотя он не строго соответствует шаблону строителя, короткий ответ - да. Сортировка.

Пропущенный элемент не может вызвать .build() для создания константы перечисления, потому что build() не может использовать new. Но вы можете получить немало преимуществ шаблона строителя. И пусть сталкиваются с этим, вы не можете использовать статические методы factory, а встроенное подклассирование констант перечисления странно.

Вот пример использования перечисления страны.

package app;

import org.apache.commons.lang.StringUtils;
import javax.annotation.Nullable;
import java.util.EnumSet;
import java.util.Set;
import static app.Language.*;
import static com.google.common.base.Preconditions.*;

enum Language {
    ITALIAN,
    ENGLISH,
    MALTESE
}

public enum Country {

    ITALY(new Builder(1, "Italy").addLanguage(ITALIAN)),
    MALTA(new Builder(2, "Malta").addLanguages(MALTESE, ENGLISH, ITALIAN).setPopulation(450_000));

    final private int id;
    final private String name;
    final private Integer population;
    final private Set<Language> languages;

    private static class Builder {

        private int id;
        private String name;
        private Integer population;
        private Set<Language> languages = EnumSet.noneOf(Language.class);

        public Builder(int id, String name) {
            checkArgument(!StringUtils.isBlank(name));

            this.id = id;
            this.name = name;
        }

        public Builder setPopulation(int population) {
            checkArgument(population > 0);

            this.population = population;
            return this;
        }

        public Builder addLanguage(Language language) {
            checkNotNull(language);

            this.languages.add(language);
            return this;
        }

        public Builder addLanguages(Language... language) {
            checkNotNull(language);

            this.languages.addAll(languages);
            return this;
        }
    }

    private Country(Builder builder) {

        this.id = builder.id;
        this.name = builder.name;
        this.population = builder.population;
        this.languages = builder.languages;

        checkState(!this.languages.isEmpty());
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Nullable
    public Integer getPopulation() {
        return population;
    }

    public Set<Language> getLanguages() {
        return languages;
    }
}

Вы можете даже статически ставить методы factory в построителе, если у вас есть общие способы построения константы.

Так что это не совсем блоховский строитель, но он довольно близко.

Ответ 2

Вы можете использовать блоки экземпляров (часто неправильно называемые "инициализаторы двойной скобки" ) для настройки конструкции с произвольным кодом:

public enum workBookSheet {

    mySheet1("Name1", "mainSheet1.xls", true, 1) {{
        addSubSheet("pathToSubSheet1.xls");
    }},
    mySheet2("Name2", "mainSheet2.xls", true, 2) {{
        // you can use the fluent interface:
        addHeaderSheet("pathToHeaders.xls").addSubSheet("pathtoSubSheet2.xls");
        // but I would prefer coding separate statements:
        addHeaderSheet("pathToHeaders.xls");
        addSubSheet("pathtoSubSheet2.xls");
    }};

    // rest of your class the same...
}

Используя этот синтаксис, вы можете обойти ограничения, налагаемые enum, но при этом сохраняйте краткость, удобство и гибкость создателя/свободного шаблона.

Ответ 3

mySheet1, mySheet2 и т.д. являются перечисляемыми константами, которые следуют синтаксису JLS, определенному в разделе 8.9.1

EnumConstant: Идентификатор Annotationsopt Argumentsopt ClassBodyopt

Итак, вы можете следовать константе enum по списку аргументов (параметры, передаваемые конструктору), но вы не можете вызывать метод в константе перечисления при объявлении его. В большинстве случаев вы можете добавить для него класс.

Кроме того, использование шаблона builder для создания экземпляров enum сомнительно, так как в целом шаблон построителя используется, когда у вас есть большое количество экземпляров (комбинации значений полей), в отличие от концепции перечислений, используемой для нескольких экземпляры.