2011-02-07 9 views
9

Próbuję napisać kod Java dla zestawu klas wyliczeniowych.Jak kodować zestaw klas wyliczeniowych, które powinny obsługiwać publiczną metodę statyczną

Każdy z enumów zawiera pewne koncepcyjnie różne dane, więc nie ma sensu ich łączyć. Wyliczenia są również mapowane do wartości w bazie danych, a więc również udostępniają niektóre typowe operacje, zarówno instancje, jak i operacje statyczne, związane z ładowaniem danych z bazy danych.

Potrzebuję uogólnić zestaw klas wyliczeniowych, które mam, tak, że mogę przekazać jedną z tych wyliczeń do innej klasy, która wykonuje i buforuje wyszukiwania bazy danych odnoszące się do każdego z różnych wyliczeń.

Ponieważ klasa pamięci podręcznej/wyszukiwania będzie również zależeć od publicznych i statycznych metod zdefiniowanych w każdym wyliczeniu, w jaki sposób mogę zakodować moje rozwiązanie, aby zagwarantować, że każde wyliczenie, które może zostać przekazane do klasy, będzie miało wymagane metody ?

Normalne podejście polegałoby na zdefiniowaniu interfejsu, ale interfejsy nie pozwalają na stosowanie metod statycznych.

Alternatywnie możesz użyć klasy abstrakcyjnej do zdefiniowania interfejsu i części wspólnej implementacji, ale nie sądzę, że jest to możliwe z wyliczeniami (rozumiem, że wyliczenia muszą rozszerzyć klasę Enum i nie można ich rozszerzyć).

Jakie są moje opcje, które pozwalają mi upewnić się, że wszystkie moje wyliczenia implementują potrzebne mi metody?

Przykład wyliczenia:

public enum MyEnum{ 
    VALUE_ONE("my data"); 
    VALUE_TWO("some other data"); 

    /** 
    * Used when mapping enums to database values - if that sounds odd, 
    * it is: it's legacy stuff 
    * 
    * set via private constructor 
    */ 
    private String myValue; 
    //private constructor not shown 

    public static MyEnum lookupEnumByString(String enumValue){ 
     //find the enum that corresponds to the supplied string 
    } 

    public String getValue(){ 
     return myValue; 
    } 
} 
+1

Nie znam konkretnych wymagań, ale ** z wyjątkiem bardzo szczególnych przypadków **, gdzie wartości w bazie danych wynoszą ** stały ** i ** mały w liczbach **, odwzorowanie Obliczenia wartości bazy danych mają tendencję do być przesadą, która prowadzi do zwiększonej złożoności. Z tym, mówiąc, wdrożenie interfejsu (lub wdrożenie metody statycznej, jak w twoim przykładzie) byłoby w porządku. –

+1

Tak, zgadzam się +1. W naszym przypadku oznacza to dane statyczne przechowywane w bazie danych, dzięki czemu można je modyfikować bez zmiany kodu. Wyliczenia są również używane do definiowania akceptowalnych wartości publicznego interfejsu internetowego, więc odkryliśmy, że najłatwiej jest zamknąć mapowanie z zewnętrznych danych do naszej usługi internetowej do naszej bazy danych przy użyciu tego zestawu zmiennych. – chrisbunney

Odpowiedz

5

To wszystko jest dość skomplikowane i mogą występować błędy, ale mam nadzieję, że masz pomysł.

// I'm not sure about the right type arguments here 
public interface MyEnumInterface<E extends MyEnumInterface & Enum<E>> { 
    public static boolean aUsefulNonStaticMethod(); 
    String getValue(); 
    MyEnumInfo<E> enumInfo(); 
} 

/** contains some helper methods */ 
public class MyEnumInfo<E extends MyEnumInterface<E>> { 
    private static <E extends MyEnumInterface<E>> MyEnumInfo(Class<E> enumClass) {...} 
    // static factory method 
    public static <E extends MyEnumInterface<E>> MyEnumInfo<E> infoForClass(Class<E> enumClass) { 
     ... return a cached value 
    } 
    public static <E extends MyEnumInterface<E>> MyEnumInfo(E e) { 
     return infoForClass(e.getClass()); 
    } 
    // some helper methods replacing static methods of the enum class  
    E enumForValue(String value) {....} 
} 

public enum MyEnum implements MyEnumInterface<MyEnum> { 
    VALUE_ONE("my data"); 
    VALUE_TWO("some other data"); 

    private String myValue; //set via private constructor 
    //private constructor not shown 

    public boolean aUsefulNonStaticMethod(){ 
     //do something useful 
    } 

    public String getValue(){ 
     return myValue; 
    } 

    // the ONLY static method in each class 
    public static MyEnumInfo<E> staticEnumInfo() { 
     return MyEnumInfo.infoForClass(MyEnumClass.class); 
    } 
    // the non-static version of the above (may be useful or not) 
    public MyEnumInfo<E> enumInfo() { 
     return MyEnumInfo.infoForClass(getClass()); 
    } 
} 

To trochę dziwne, że używasz innego String oprócz Enum.name(), nie jest to potrzebne?

Z powodu wszystkich wyliczeń rozszerzających Enum, nie możesz pozwolić im udostępniać żadnego kodu. Najlepsze, co możesz zrobić, to delegować to wszystko do statycznej metody pomocnika w klasie narzędziowej.

Nie ma sposobu, aby wymusić stosowanie metod statycznych, co jest zrozumiałe, ponieważ nie ma sposobu (z wyjątkiem refleksji) na ich wywołanie.

+0

Mapa enum do wartości w bazie danych łańcuch jest częścią klucza w tym odwzorowaniu. Parametr myEnum jest literówką z mojej strony, powinien pobrać ciąg znaków i zwrócić odpowiednie wyliczenie. Poprawię pytanie: – chrisbunney

+0

Istnieje 'Enum.valueOf (Class enumType, nazwa ciągu)', które może zrobić to, czego potrzebujesz. Co więcej, w każdym wyliczeniu znajduje się niejawnie zadeklarowana wartość "valueOf (String name)". – maaartinus

+0

Niestety, baza danych przechowuje kody, a nie nazwy znaczące, więc użycie wartości bazy danych jako nazwy wyliczeniowej, co uważam, że proponujesz, nie byłoby dobrym pomysłem. Dlatego zapisaliśmy ciąg obok stałej enum – chrisbunney

3

To jest najbliższe, o czym mogę pomyśleć.

Masz klasę zawierającą swoją wspólną funkcjonalność:

class Util{ 

    //common functionality 
    public void doSomething(){ 

    } 
} 

Każdy Enum ma ma instancję tej klasy i mogą zmienić swoje metody, jeśli to konieczne:

enum Enum1{ 
    FOO, 
    BAR; 

    private Util util = new Util(); 

    public Util getUtil() { 
     return util; 
    } 

} 

enum Enum2{ 
    ALICE, 
    BOB; 

    private Util util = new Util(){ 
     @Override 
     public void doSomething() { 
      //this one has overridden it 
     }; 
    }; 

    public Util getUtil() { 
     return util; 
    } 
} 

Przykład użycia:

Enum2.ALICE.getUtil().doSomething(); 
+0

Lepiej używać interfejsu "' SomethingDoer' "za pomocą metody" 'doSomething()' "i mieć ją zaimplementować. Następnie możesz po prostu wywołać 'ALICE.doSomething()'. –

+0

Jeśli korzystasz z interfejsu, kończy się powielanie kodu w swoich wyliczeniach. Z pewnością możesz zmusić Util do implementacji interfejsu, jeśli chcesz. Możesz mieć 5 wyrażeń i być może chcesz przesłonić Zrób coś tylko w jednym z nich. – dogbane

+0

W twoim przykładzie twoje metody 'doSomething()' robią różne rzeczy. Nic nie stoi na przeszkodzie, by kilka enum implementacji wywoływało tę samą metodę, jeśli im się podoba. Ponadto za pomocą interfejsu można podać dowolne wyliczenie dowolnej metody oczekującej "SomethingDoer". Poza tym w twoim przykładzie duplikujesz więcej kodu, definicję 'Util' i dodatkową metodę' getUtil() '. –

2

** OSTRZEŻENIE ** Poniższy kod jest pseudokodem Java i jako taki nie będzie się kompilował.

Więc chcesz dołączyć logikę do poszczególnych wyliczeń. Może to wymagać pewnych wyliczeń, aby podzielić się tą samą logiką, mając inne wyliczenia z własną, specyficzną logiką.Również chcesz skojarzyć klucze ciąg, który nie może być taka sama jak nazwa enum jest (co zazwyczaj zwracany przez Enum.name().

Poniższy oparte na Javie pseudokod pokazuje jeden sposób (jeden z wielu), w którym mógłbyś Nie jest to jedyny i nie twierdzę, że jest najlepszy.Jednakże byłby to sposób, który bym użył w takim przypadku:

To znaczy, chciałbym iść z kompozycją obiektu za pośrednictwem interfejsów (trochę rodzaju szablonów strategii i szablonów).

// at package-level visibility 

interface EnumHandler 
{ 
    SomeRetVal doSomething(DBEnum dbEnum); 
} 

final class DefaultHandler implements EnumHandler 
{ 
    static DefaultHandler _handler = new DefaultHandler(); 

    SomeRetVal doSomething(DBEnum dbEnum) 
    { 
     return ping; 
    } 
} 

// at public visibility 

public interface Actionable 
{ 
    // meh, you might need such an interface, or maybe not, just added here 
    // for illustration purposes. you'll have to make a decision if you need it 
    // or not. 
    SomeRetVal doSomething(); 
} 

// have the enum implement the interface if you determine you need 
// such an interface 
public Enum DBEnum implements Actionable 
{ 
    // ONE and THREE share the same logic. TWO has its own. 

    ONE("db-key-one"), 
    TWO("db-key-two, new EnumHandler(){ 
      SomeRetVal doSomething(DBEnum dbEnum){ return pong; } }), 
    THREE("db-key-three"); 

    // this guy keeps track of enums by key 
    static private java.util.Map<String,DBEnum> _MAP = 
    java.util.Collections.unmodifiableMap(
     new java.util.HashMap<String,DBEnum>()); 

    final private String _key; 
    final private EnumHandler _handler; 

    // allows construction of customized handler 
    DBEnum(final String key, final EnumHandler handler) 
    { 
     this._key = key; 
     this._handler = handler; 

     this._MAP.put(key, this) 
    } 

    // construct using default handler 
    DBEnum(final String key) 
    { 
     this(key, DefaultHandler._handler); 
    } 

    // have toString() return the key instead of this.name() 
    public String toString() 
    { 
     return this._key; 
    } 

    // implementing Actionable interface (if you choose to use such an interface) 
    public SomeRetVal doSomething() 
    { 
     return this._handler.doSomething(this); 
    } 

    // get enum by key 
    public static DBEnum getByDbKey(final String key) 
    { 
    DBEnum retVal = this._MAP.get(key); 
    if(retVal == null){ throw new IllegalArgumentException("thingie not found"); 

    return retVal; 
    } 

    public static Iterator<String> dbKeys() 
    { 
    return _map.keySet().iterator(); 
    } 
} 

// somewhere else 
public static void main(String[] args) 
{ 
    DBEnum.ONE.doSomething(); 

    DBEnum.geByDBKey(DBEnum.TWO.toString()).doSomething(); 

    for(String dbKey : DBEnum.dbKeys()) 
    { 
     DBEnum.getByDbKey(dbKey).doSomething(); 
    } 

    // the following will kaput with an IllegalArgumentException 
    DBEnum.getDbByKey("key-that-is-not-there").doSomething(); 
} 

Zasadniczo można teoretycznie wyciągnąć rzeczywiste klucze db z pliku zasobów, gdy Enum jest ładowany przez moduł ładujący klasy. Zawartość (i zmiany w zawartości) pliku zasobów może być elementem wdrażania. Mogą to być znaczące korzyści - zmiana klucza bazy danych nie wymagałaby ponownej kompilacji. ALE takie podejście sprawi, że rzeczy nieco bardziej skomplikowane. Byłoby coś, co zaimplementowałbym po wszystko inne jest zrobione dobrze.

+1

Nie do końca to, czego szukałem, ponieważ nie • Potrzebuję dołączyć różne logiki do różnych wartości wyliczeniowych, a raczej próbuję zdefiniować wspólny interfejs (w sensie ogólnym, a nie specyficzny sens interfejsu Java '), który zapewnia zestaw klas wyłudzeniowych, wszystkie zapewniają określone metody, w tym niektóre metody, które powinny być statyczne. Jednak twoje podejście może zdefiniować publiczną metodę, która wywoła metodę statycznej procedury obsługi w implementacji. Jednak ciekawa odpowiedź! – chrisbunney

+0

I powinienem też dodać: +1 – chrisbunney

+0

Ahhh, teraz widzę, co zamierzacie. Interesujące wyzwanie, ponieważ sama Java nie zapewnia dobrych sposobów, aby to zrobić. Założę się, że coś takiego można zrobić w mgnieniu oka z Ruby. –

Powiązane problemy