2011-02-07 13 views
9

Estoy tratando de escribir algún código Java para un conjunto de clases enum.Cómo codificar un conjunto de clases enum que todas deberían soportar un método público estático

Cada una de las enumeraciones encapsula algunos datos conceptualmente distintos, por lo que no tiene sentido combinarlos. Las enumeraciones también se asignan a valores en una base de datos, y también comparten algunas operaciones comunes, tanto de instancia como estáticas, relacionadas con la carga de datos de la base de datos.

Necesito generalizar el conjunto de clases enum que tengo, de modo que pueda pasar cualquiera de estas enumeraciones en una clase diferente que realiza y cachés de búsquedas de bases de datos relacionadas con cada una de las diferentes enumeraciones.

Dado que la clase de búsqueda/caché también dependerá de los métodos públicos y estáticos definidos en cada enumeración, ¿cómo puedo codificar mi solución para garantizar que cualquier enumeración que pueda pasar a la clase tenga los métodos necesarios? ?

El enfoque normal sería definir una interfaz, pero las interfaces no permiten métodos estáticos.

Alternativamente, puede usar una clase abstracta para definir la interfaz y parte de la implementación común, pero no creo que eso sea posible con enums (entiendo que las enumeraciones deben extender la clase Enum y no pueden extenderse).

¿Cuáles son mis opciones que tengo que me permitan asegurar que todas mis enumeraciones implementen los métodos que necesito?

Ejemplo enumeración:

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

No conozco los requisitos específicos que tiene, pero ** excepto en casos muy específicos ** donde los valores de la base de datos son ** constantes ** y ** pequeños en números **, los enumerados en los valores de la base de datos tienden a ser un exceso que conduce a una mayor complejidad. Dicho eso, implementar una interfaz (o implementar un método estático como en tu ejemplo) estaría bien. –

+1

Sí, estoy de acuerdo +1. En nuestro caso, esto representa datos estáticos almacenados en la base de datos para que puedan modificarse sin alterar el código. Las enumeraciones también se utilizan para definir valores aceptables para una interfaz web pública, por lo que hemos encontrado que es más fácil encapsular la asignación de datos externos a nuestro servicio web a nuestra base de datos utilizando este conjunto de enums – chrisbunney

Respuesta

5

Todo es bastante complicada y puede haber errores, pero espero que se entiende la idea.

// 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()); 
    } 
} 

Es un poco extraño, que está utilizando otra cadena, además de Enum.name(), qué lo necesita?

Debido a todas las enumeraciones que se extienden Enum, no puede permitirles compartir ningún código. Lo mejor que puede hacer es delegar todo en un método estático de ayuda en una clase de utilidad.

No hay forma de obligar a las clases a implementar un método estático, lo que es comprensible, ya que no hay forma (excepto por la reflexión) de llamarlas.

+0

Las enumeraciones asignan valores a la base de datos , la cadena es parte de la clave en ese mapeo. El parámetro myEnum es un error tipográfico de mi parte, debería tomar una cadena y devolver la enumeración correspondiente. Voy a corregir la pregunta – chrisbunney

+0

Hay 'Enum.valueOf (Class enumType, String name)' que puede hacer lo que necesites. Además, hay un 'valueOf (String name) 'implícitamente declarado en cada enumeración. – maaartinus

+0

Lamentablemente, la base de datos almacena códigos, nombres no significativos, por lo que utilizar el valor de la base de datos como nombre de la enumeración, que es lo que creo que usted propone, no sería realmente una buena idea. Es por eso que almacenamos la cadena junto con la constante de enumeración – chrisbunney

3

Esto es lo más cercano que se me ocurre.

tiene una clase que contiene la funcionalidad común:

class Util{ 

    //common functionality 
    public void doSomething(){ 

    } 
} 

Cada Enum tiene tiene una instancia de esta clase y puede invalidar sus métodos si es necesario:

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

Ejemplo de Uso:

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

Es mejor utilizar una interfaz "' SomethingDoer' "con el método" 'doSomething()' "y hacer que las enum lo implementen. Entonces puedes simplemente llamar a 'ALICE.doSomething()'. –

+0

Si usa una interfaz, termina duplicando el código en sus enums. Ciertamente puede hacer que Util implemente una interfaz si lo desea. Podrías tener 5 enumeraciones y quizás quieras anular doSomething en solo una de ellas. – dogbane

+0

En su ejemplo, sus métodos 'doSomething()' hacen cosas diferentes. No hay nada que impida que varias implementaciones enum invoquen el mismo método si lo desean. Además, al usar una interfaz, puedes suministrar cualquier enumeración a cualquier método esperando un 'SomethingDoer'. Además, en su ejemplo está duplicando más código, la definición de 'Util' y el método' getUtil() 'adicional. –

2

** ADVERTENCIA ** el siguiente es un pseudocódigo de Java, y como tal, no se compilará.

Así que desea adjuntar lógica a enumeraciones individuales. Esto posiblemente podría requerir que algunas enumeraciones compartan la misma lógica mientras tienen otras enumeraciones con su propia lógica específica.También, que desea asociar claves de cadena que podría no ser el mismo que el nombre de la enumeración (lo que normalmente devuelto por Enum.name().

La siguiente pseudocódigo muestra basada en Java de una manera (uno de los muchos) en la que se podía hacer eso. No es el único, y no pretendo que sea el mejor. Sin embargo, este sería el enfoque que usaría en tal caso.

Es decir, me gustaría ir con la composición del objeto a través de interfaces (un poco tipo de estrategia + patrones de plantilla.)

// 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(); 
} 

Uno podría, en teoría, extraer las claves db reales de un archivo de recursos cuando el cargador de clases cargue Enum. El contenido (y los cambios en el contenido) del archivo de recursos podría ser un elemento de implementación. Puede haber ventajas significativas para esto: un cambio en la clave db no requeriría una recompilación. PERO tal enfoque hará las cosas un poco más complejas. Sería algo que implementaría después de todo lo demás se hace bien.

+1

No es exactamente lo que estaba buscando, ya que no Necesito unir lógica diferente a diferentes valores de enumeración, más bien estoy tratando de definir una interfaz común (en el sentido general, no el sentido de 'interfaz' Java específico) que asegura que un conjunto de clases enum proporcione ciertos métodos, incluyendo algunos métodos que deberían ser estáticos Sin embargo, su enfoque podría definir un método público que llame a un método de controlador estático en la implementación. ¡Una respuesta interesante, sin embargo! – chrisbunney

+0

Y también debería agregar: +1 – chrisbunney

+0

Ahhh, ahora veo lo que está buscando. De hecho, es un desafío interesante ya que Java no proporciona buenos medios para hacerlo. Apuesto a que algo como esto podría hacerse en un instante con Ruby. –

Cuestiones relacionadas