我可以在Java枚举上使用生成器模式吗

我可以在Java枚举上使用生成器模式吗,java,enums,builder,Java,Enums,Builder,我正在编写一些代码,我已经决定了重新创建类的方法,因为有固定数量的工作表,我将它们创建为枚举。这是一个基于构造器模式与伸缩构造器的可读性的决定 我正在编写的代码获取了一些.xls文件,添加了一些标题(并从其他.xls文件中读取了一些),也许还有一些子表。然后,它以特定的方式将这些工作表合并在一起,在主excel工作簿上创建选项卡。我的问题是,一些工作簿选项卡采用不同数量的工作表作为参数。我正在尝试应用生成器模式。这就是我试图编写的代码: public enum workBookSheet {

我正在编写一些代码,我已经决定了重新创建类的方法,因为有固定数量的工作表,我将它们创建为枚举。这是一个基于构造器模式与伸缩构造器的可读性的决定

我正在编写的代码获取了一些.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希望我的枚举声明(顶部以逗号分隔的“枚举构造函数”列表)中只包含构造函数,而不包含其他方法。我可以毫无怨言地将这些方法移到下面的“构建器”方法

public void buildSheets(){
    mySheet1.addSubSheet("pathToSubSheet1.xls");
    mySheet2.addHeaderSheet("pathToHeaders.xls").addSubSheet("pathtoSubSheet2.xls");
}
这是在枚举上实现生成器模式的唯一方法吗?它确实需要我运行一个单独的方法,这并不太麻烦。不过,我确实觉得我打破了这个模式(我想,如果这行得通的话,这并不是一件坏事。)

注意:我已经仔细查看了一下,看看是否有其他人在网上或其他地方问过这个问题。我发现的最接近的问题是关于枚举和工厂的问题,但这并不能完全回答我的问题。另外,我知道这并不是构建器模式,因为我没有一个单独的类来接受创建新枚举的build()方法。我想这是我最初设计中的问题的根源,但我对Java相对来说比较陌生


那么,有没有更好的方法在Java枚举上使用构建器模式呢?或者我所拥有的“足够接近”了吗?

mySheet1、mySheet2等是遵循第节中定义的JLS语法的枚举常量

枚举常数: 注释sopt标识符参数sopt ClassBodyopt

因此,可以通过参数列表(传递给构造函数的参数)跟随枚举常量,但不能在声明枚举常量时调用方法。最多可以为它添加一个类主体


除此之外,使用生成器模式构建枚举实例是值得怀疑的,因为通常情况下,当您有大量实例(字段值的组合)时,会使用生成器模式,而很少有实例使用枚举的概念。

您可以使用实例块(通常错误地调用)“双大括号初始值设定项”),可使用任意代码自定义结构:

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
所施加的限制,但仍然具有构建器/流畅模式的简洁、方便和灵活性。

尽管它不严格符合构建器模式,但简短的回答是肯定的。有点像

缺少的部分是无法调用
.build()
来实例化枚举常量,因为build()不能使用
new
。但是您可以从构建器模式中获得很多好处。面对它,您不能使用静态工厂方法,枚举常量的内联子类化很奇怪

下面是一个使用国家/地区枚举的示例

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;
    }
}
package应用程序;
导入org.apache.commons.lang.StringUtils;
导入javax.annotation.Nullable;
导入java.util.EnumSet;
导入java.util.Set;
导入静态app.Language.*;
导入静态com.google.common.base.Premissions.*;
枚举语言{
意大利人
英语,
马耳他语
}
公共枚举国家{
意大利(新建筑商(1,“意大利”).addLanguage(意大利语)),
马耳他(新建筑商(2,“马耳他”)。增加语言(马耳他语、英语、意大利语)。人口(450000);
最终私有int id;
最后一个私有字符串名;
最终私有整数总体;
最终专用集语言;
私有静态类生成器{
私有int-id;
私有字符串名称;
私人整数人口;
私有集合语言=EnumSet.noneOf(Language.class);
公共生成器(int-id,字符串名称){
checkArgument(!StringUtils.isBlank(name));
this.id=id;
this.name=名称;
}
公共建筑商集合人口(整数人口){
检查参数(总体>0);
这个。人口=人口;
归还这个;
}
公共生成器添加语言(语言){
checkNotNull(语言);
this.languages.add(语言);
归还这个;
}
公共语言(语言…语言){
checkNotNull(语言);
this.languages.addAll(语言);
归还这个;
}
}
私人国家(建筑商){
this.id=builder.id;
this.name=builder.name;
this.population=builder.population;
this.languages=builder.languages;
checkState(!this.languages.isEmpty());
}
公共int getId(){
返回id;
}
公共字符串getName(){
返回名称;
}
@可空
公共整数getPopulation(){
返回人口;
}
公共集getLanguages(){
返回语言;
}
}
如果您有构建常量的常用方法,甚至可以在生成器中放置静态工厂方法


所以它不是Bloch的构建器,但非常接近。

谢谢你的回答dcernahoschi,特别是你给我指出了定义。事实上,我实际上有20-25个枚举实例在使用,每个实例的标题、子表等组合都有细微的不同。有多少个“大”到足以证明一个构建器是正确的,有多少个“少”就足够了对于枚举?随着开发的深入,这可能会发生变化(类中稍后硬编码的头可能会转换为header.xls表等),因此这种模式确实适合我。这是一种非常巧妙的解决方法!我唯一的问题是:我必须修改方法吗