2013-03-14 24 views
13

Ponownie piszę kod i zdecydowałem, jak odtworzyć klasę, ponieważ jest ustalona liczba arkuszy, a ja tworzę je jako wyrazy . Jest to decyzja oparta na czytelności tupotu budowniczego względem konstruktora teleskopowego.Czy mogę użyć wzorca budowniczego na Java Enum

Kod Przechwytywam niektóre pliki .xls, dodaje nagłówki (i czyta niektóre z innych plików .xls) i może niektóre pod-arkusze. Następnie łączy on wiele różnych arkuszy w określony sposób, tworząc tabulatory w głównym skoroszycie programu Excel. Mój problem polega na tym, że niektóre zakładki skoroszytu przyjmują różną liczbę arkuszy jako argumenty. Próbuję zastosować wzór konstruktora. Jest to rodzaj kodu Próbuję napisać:

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

} 

błędu, że java daje mi zdaje się mówić, że Java oczekuje moją deklarację enum (przecinek listę „konstruktorów enum” rozdzielany na górze) mieć tylko konstruktora, a nie dodatkowe metody. Mogę przenieść te metody do metody "konstruktora" poniżej, bez reklamacji.

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

Czy to jedyny sposób na wdrożenie wzorca budowniczego w wyliczeniu? To wymaga ode mnie osobnej metody, która nie jest zbyt trudna. Czuję, że łamie ten schemat (chyba nie tak źle, jeśli to działa).

NB Dobrze się rozejrzałem, aby sprawdzić, czy ktoś zadał to pytanie lub w innym miejscu w Internecie. Najbliższe, jakie znalazłem, dotyczyło tutaj kwestii związanych z Enumami i Fabrykami, ale to nie jest odpowiedź na moje pytanie. Ponadto jestem świadomy, że nie jest to całkiem wzorzec konstruktora, ponieważ nie mam oddzielnej klasy, która akceptuje metodę build(), która tworzy nowe wyliczenie. Domyślam się, że to jest główny problem w moim początkowym projekcie, ale jestem stosunkowo nowy w Javie.

Czy istnieje lepszy sposób używania wzorca konstruktora w wyliczaniu Java? Czy jest to, co mam "wystarczająco blisko"?

Odpowiedz

14

Chociaż nie jest ściśle zgodny z wzorcem konstruktora, krótka odpowiedź brzmi "tak". Raczej.

Brakujący element nie może wywołać .build(), aby utworzyć stałą wyliczenia, ponieważ funkcja build() nie może użyć new. Ale możesz uzyskać całkiem sporo korzyści ze wzoru budowniczego.I spójrzmy prawdzie w oczy, nie można użyć statycznych metod fabrycznych, a wbudowana podrzędność stałych enum jest dziwna.

Oto przykład użycia wyliczenia 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; 
    } 
} 

Można nawet umieścić statyczne metody fabryki w budowniczego jeśli mają wspólne sposoby tworzenia stałą.

Więc to nie jest całkiem budowniczy Blocha, ale jest całkiem blisko.

+0

Czy jest to coś, z czego już korzystałeś wcześniej, czy jest to nowy kod, który napisałeś jako przykład? – Pureferret

+3

Trochę z obu. Chciałem użyć budowniczego, znalazłem kilka odpowiedzi, ale ich nie lubiłem i wymyśliłem to w ten sposób. Wtedy pomyślałem, że podzielę się moim rozwiązaniem w tym pytaniu na wypadek, gdyby pomógł komuś. –

+1

Dziękuję bardzo! Dzięki temu moja klasa wyliczeń była znacznie bardziej czytelna. –

1

mySheet1, mySheet2, itd. Są stałymi enum co wynika składnię JLS zdefiniowany w sekcji 8.9.1

EnumConstant: Annotationsopt Identifier Argumentsopt ClassBodyopt

więc można śledzić wyliczenia stałej przez listę argumentów (parametry przekazać do konstruktora), ale nie może wywołać metody na stałej wyliczeniowej podczas deklarowania. Co najwyżej możesz dodać do niego ciało klasowe.

Poza tym użycie wzoru budowniczego do budowania instancji wyliczeniowych jest wątpliwe, ponieważ generalnie wzorzec budowniczego jest używany, gdy występuje duża liczba wystąpień (kombinacji wartości pól) w przeciwieństwie do koncepcji wyliczeń używanych do kilka przykładów.

+0

Dziękuję za odpowiedź dcernahoschi, szczególnie za wskazanie mi definicji. W rzeczywistości mam 20-25 instancji w grze, każda z subtelnie inną kombinacją nagłówków, pod-arkuszy itd. Ile jest "wystarczająco dużych", by uzasadnić budowniczego, jak mało jest mało wyliczać? Przy większej liczbie zmian może się to zmienić (twarde kodowane nagłówki później w klasie mogą zostać przekonwertowane na arkusze header.xls itp.), Więc ten wzór jest dla mnie odpowiedni. – Pureferret

4

można użyć bloków Instancji (często niepoprawny zwane „podwójne inicjalizatory karczkiem”), aby dostosować konstrukcję z dowolnego kodu:

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

Zatrudnianie tej składni pozwala na obejście ograniczeń nałożonych przez enum ale nadal mają zwięzłość, wygodę i elastyczność budowniczego/płynnego wzoru.

+0

To bardzo sprytne obejście! Moje jedyne pytania to: Czy muszę zmodyfikować metody i czy mogę napisać zawartość bloku instancji jako 'this.methodName (...);' jak wolę być wyraźna podczas pisania metod. – Pureferret

+1

Nie musisz * modyfikować metod, jednak nie zawracałbym sobie głowy płynnym stylem (zwracając "to") i robił bym "prywatny", chyba że * potrzebujesz *, aby były widoczne poza twoją klasą. Możesz napisać "to", ale jest zbędne i nie jest zalecane z punktu widzenia stylu, zwłaszcza, że ​​metody "tego" * nigdy * nie wymagają kwalifikacji; tylko pola wymagają kwalifikowania i tylko wtedy, gdy kolidują z nazwami parametrów i nie ma parametrów dla bloków instancji. – Bohemian

+0

Czeski, próbowałem użyć bloków instancji, ale klasa, w której wyliczenia są wewnątrz, jest statyczna, a umieszczenie ich na miejscu oznacza, że ​​są błędne. Zostawiam ich jak na razie, ale jeśli znajdę czas, spróbuję to obejść. Na razie, jeśli wiesz, jak powinny wyglądać metody, czy możesz pop w odpowiedzi? – Pureferret

Powiązane problemy