17

いくつかのコードを書き直していて、クラスを再作成する方法を決定しました。シートの数は固定されているため、列挙型として作成しています。これは、ビルダーパターンと伸縮コンストラクターの読みやすさに基づいた決定です。

私が取得しているコードは、いくつかの.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");
}

これは、列挙型にビルダーパターンを実装する唯一の方法ですか?別のメソッドを実行する必要がありますが、それほど面倒ではありません。しかし、それは私がパターンを破っているような気がします(これがうまくいけば、それほど悪いことではないと思います)。

NB私は、SOやウェブ上の他の場所で、他の誰かがこの質問をしたかどうかをよく調べました。私が見つけた最も近いものは、ここで列挙型とファクトリに関する質問でしたが、それは私の質問に完全には答えていません。また、新しい列挙型を作成するbuild()メソッドを受け入れる別のクラスがないため、これがビルダーパターンではないことも認識しています。これが私の最初の設計の問題の根本だと思いますが、私はJavaに比較的慣れていません。

では、Java列挙型でビルダーパターンを使用するためのより良い方法はありますか?それとも、私が持っているものは「十分に近い」ですか?

4

3 に答える 3

27

ビルダー パターンに厳密に準拠しているわけではありませんが、短い答えはイエスです。並べ替え。

build .build()() はnew. しかし、ビルダー パターンの利点のかなりの部分を得ることができます。それに直面しましょう。静的ファクトリ メソッドは使用できず、列挙型定数のインライン サブクラス化は奇妙です。

Country 列挙を使用した例を次に示します。

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

定数を構築する一般的な方法がある場合は、ビルダーに静的ファクトリ メソッドを配置することもできます。

したがって、Bloch のビルダーとはまったく異なりますが、かなり近いものです。

于 2013-05-29T18:56:58.023 に答える
6

インスタンス ブロック ( 「二重ブレース初期化子」と誤って呼ばれることが多い) を使用して、任意のコードで構築をカスタマイズできます。

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この構文を採用すると、ビルダー/流暢なパターンの簡潔さ、便利さ、および柔軟性を維持しながら、によって課される制限を回避できます。

于 2013-03-14T12:07:04.913 に答える
1

mySheet1, mySheet2などは、セクション8.9.1で定義されている JLS 構文に従う列挙定数です。

EnumConstant: Annotationsopt 識別子 Argumentsopt ClassBodyopt

したがって、列挙型定数の後に引数リスト (コンストラクターに渡すパラメーター) を続けることはできますが、宣言中に列挙型定数のメソッドを呼び出すことはできません。せいぜい、クラス本体を追加できます。

これに加えて、列挙型インスタンスを構築するためのビルダー パターンの使用法には疑問があります。一般に、少数のインスタンスに使用される列挙型の概念とは対照的に、多数のインスタンス (フィールド値の組み合わせ) がある場合にビルダー パターンが使用されるためです。

于 2013-03-14T10:49:08.277 に答える