2013-03-14 9 views
13

コードを書き直しています。クラスを再作成する方法を決めました。固定数のシートがあるので、enumとして作成しています。 。これはビルダー・パターンと伸縮可能なコンストラクターの可読性に基づいた決定です。Java Enumでbuilderパターンを使用することはできますか

コードいくつかの.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私は、他の誰かがこの質問をしているかどうかを見ていました。またはウェブの他の場所に置くことができます。私が見つけた最も近いのはEnumsとFactoryについての質問ですが、それは私の質問にはあまり答えません。また、新しい列挙型を作成するbuild()メソッドを受け入れる独立したクラスを持たないので、これはかなりビルダーパターンではないことを認識しています。私はこれが私の最初のデザインの問題の根源だと思うが、私は比較的Javaに新しい。

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

答えて

14

ビルダーパターンに厳密には準拠していませんが、短い答えは「はい」です。並べ替え

ビルド()がnewを使用できないため、不足している部分が.build()を呼び出して列挙型定数をインスタンス化できません。しかし、あなたはビルダーパターンの利点のかなりを得ることができます。静的なファクトリメソッドを使用することはできません。列挙型定数のインラインサブクラス化は変です。

ここでは、国の列挙を使用した例です。

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

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

だから、それはかなりブロッホのビルダーではありませんが、かなり近いです。

+0

これはあなたがこれまでに使ってきたものですか、それともこの新しいコードですか? – Pureferret

+3

両方のビット。私はビルダーを使いたいと思っていましたが、いくつかの答えを見つけましたが、好きではありませんでした。それから、誰かを助けた場合に備えて、私はこの質問に私の解決策を分かち合うと思いました。 –

+1

ありがとう!これにより、私のenumクラスはずっと読みやすくなりました。 –

1

mySheet1, mySheet2は、あなたが引数リストによって列挙型定数をたどることができ、8.9.1

EnumConstant: Annotationsopt Identifier Argumentsopt ClassBodyopt

そうセクションで定義されJLSの構文を次のenum定数です(パラメータは、コンストラクタに渡す)ができますそれを宣言している間、enum定数のメソッドを呼び出すことはできません。ほとんどの場合、クラス本体を追加することができます。

さらに、enumインスタンスを構築するためのBuilderパターンの使用は疑問です。一般的に、Builderで使用されるenumの概念とは対照的に、多数のインスタンス(フィールド値の組み合わせ)がある場合はBuilderパターンが使用されます。いくつかのインスタンス。

+0

あなたの答えdcernahoschi、特に私に定義を指摘してくれてありがとう。実際には、実際には、ヘッダー、サブシートなどの微妙に異なる組み合わせを持つ20〜25のenumインスタンスが実際に存在します。ビルダーを正当化するのに十分な数、「少数」が列挙型?より多くの開発では、これは変更される可能性があります(このクラスで後でハードコーディングされたヘッダーはheader.xlsシートなどに変換される可能性があります)。 – Pureferret

4

あなたは、任意のコードで建設をカスタマイズする(「ダブルブレースの初期化子」と呼ばれることが多い誤っ)インスタンス・ブロックを使用することができます。この構文を採用

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

+0

これは非常に巧妙な回避策です!私の唯一の質問は次のとおりです。メソッドを変更する必要がありますか、メソッドを記述するときに明示的に好むので、インスタンスブロックの内容を 'this.methodName(...);'と書くことができます。 – Pureferret

+1

メソッドを変更する必要はありませんが、流暢なスタイル( 'this'を返す)を気にせず、あなたのクラスの外に見えるようにする必要がある場合を除いて' private'を作成します。 'this'は書くことができますが、' this''のメソッドは決して適格化する必要がないので、冗長であり、スタイルの観点から推薦されません。フィールドのみが修飾を必要とし、パラメータ名と衝突し、インスタンスブロックへのパラメータがない場合のみです。 – Bohemian

+0

ボヘミアン、私はインスタンスブロックを使用しようとしましたが、列挙型が内部にあるクラスは静的で、それらを適切な場所に置くことはエラーです。私は彼らを今のままにしておきますが、時間があれば私はそれを回避しようとします。今のところ、メソッドがどのように見えるべきかを知っていれば、答えにポップすることができますか? – Pureferret

関連する問題