2012-01-10 18 views
5

Tengo una pregunta sobre el mejor patrón de diseño para la reutilización de código cuando se trata de enumeraciones de Java. Básicamente, lo que intento lograr es poder definir varias enumeraciones que modelan colecciones de negocios estáticas (conjuntos de constantes), pero también me gustaría compartir el comportamiento entre ellas, con una codificación mínima.Ampliación de Java Enums

Esto es trivial con las clases abstractas pero, como las enumeraciones Java no se pueden extender (solo pueden implementar interfaces), este tipo de trabajo es tedioso e implica mucho trabajo de copiar/pegar propenso a errores (copiar el código de enum a enum). Los ejemplos de "lógica de negocios" que deben compartirse entre todas las enumeraciones incluyen la conversión de/a cadenas, comparación de instancias y lógica, etc.

Mi mejor oportunidad ahora es utilizar clases de ayuda junto con las interfaces de negocios, pero esto solo va hasta ahora en la reducción de la complejidad del código (ya que todas las enumeraciones aún tienen que declarar e invocar las clases de ayuda). Ver ejemplo (sólo para aclarar):

public enum MyEnum { 
    A, B, C; 

    // Just about any method fits the description - equals() is a mere example 
    public boolean equals(MyEnum that) { 
     ObjectUtils.equals(this, that); 
    } 
} 

Cómo hacer StackOverflowers acuerdo con esta "característica del lenguaje"?

+0

podría tratar de Lombok y '@ Delegate', nunca he lo intenté con una enumeración. –

+0

En Java 6, las enumeraciones son una clase final; no puedes extenderlos. Creo que Java 7 es lo mismo. – DwB

+1

Las enumeraciones no están hechas para eso: usan clases (o clases con enumeraciones) o separan la lógica; podría usar un patrón de fábrica para un caso como este (si el comportamiento es lo suficientemente diferente de enum a enum). – Viruzzo

Respuesta

2

Haría lo mismo, o combinaría los Enum en una super-enumeración.

Con Java 8 esto será más fácil. Podrá definir una implementación default para los métodos de interfaz y hacer que el enum amplíe la interfaz.

+0

¿Qué quieres decir con combinar el 'enum's en un super-'enum'? ¿No sería eso inseguro ya que los valores A y B podrían ser comparados incluso si no tenían sentido? – lsoliveira

+0

Puede ser inseguro, más bien depende del contexto. –

+0

Al final, todas las demás soluciones agregan más complejidad sin violar el principio DRY, por lo que esta parece ser la solución. Cruzando mis dedos las clases de extensión llegan a Java, algún día ... – lsoliveira

2

Rara vez encuentro enums útil, excepto para representar estados finitos en cuyo caso no necesitan un comportamiento.

Sugeriría refactorizar enums que necesita un comportamiento en classes con un Factory.

+0

las enumeraciones son mucho más seguras que usar constantes para pasar los valores y se prestan mejor a la manipulación (EnumSet <...>, 'switch', etc.). Estoy de acuerdo en que son mucho menos poderosos que su homólogo de C#, pero aún tienen su lugar (especialmente cuando solo estás refabricando un código y no puedes hacer una reescritura completa :)). – lsoliveira

+0

Estoy de acuerdo en que 'enums' es mejor que el anterior' static estático int CONSTANT = 1' y que el azúcar sintáctico es agradable, pero cada vez que puse el comportamiento en un 'enum' lo he lamentado. –

+0

@Isoliveira Espera ** MENOS ** poderosa que las enumeraciones C# que son básicamente azúcar sintáctica alrededor de un int? ¿Cómo llegaste a esa conclusión? De todos modos estoy de acuerdo con Garett hasta cierto punto: los mensajes están ahí para representar estados finitos, en cuyo caso no estoy seguro de por qué tendríamos que ampliarlos. Pero tener un estado simple (!) En las enumeraciones suele ser útil. Si se trata de métodos más que triviales, debería ser una clase. Pero, p. un método 'getComplement()' estaría bien en mi libro. – Voo

3

Puede mover la lógica reutilizable a clases dedicadas (sin enum) y luego hacer que las enums deleguen esas clases. Aquí hay un ejemplo:

[Nota al margen: no se recomienda la herencia de PlusTwo extends PlusOne (b/c PlusTwo no es PlusOne). Es aquí sólo para ilustrar el punto de ser capaz de extender una lógica existente.]

public interface Logic { 
    public int calc(int n); 
} 

public static class PlusOne implements Logic { 
    public int calc(int n) { return n + 1; } 
} 

public static class PlusTwo extends PlusOne { 
    @Override 
    public int calc(int n) { return super.calc(n) + 1; } 
} 

public static enum X { 
    X1, X2; 
    public Logic logic; 

    public int doSomething() { 
    return logic.calc(10); 
    } 
} 

public static enum Y { 
    Y1, Y2; 
    public Logic logic; 

    public String doSomethingElse() { 
    return "Your result is '" + logic.calc(10) + "'"; 
    } 
} 

public static void main(String[] args) { 
    // One time setup of your logic: 
    X.X1.logic = new PlusOne(); 
    X.X2.logic = new PlusTwo(); 
    Y.Y1.logic = new PlusOne(); 
    Y.Y2.logic = new PlusTwo(); 

    System.out.println(X.X1.doSomething()); 
    System.out.println(X.X2.doSomething()); 
    System.out.println(Y.Y1.doSomethingElse()); 
    System.out.println(Y.Y2.doSomethingElse()); 
} 
+0

Al final, eso es solo una variación en mi propuesta inicial. Simplemente desacopla la lógica de implementación. También usaría una fábrica para 'Lógica' y no la expondría así (como estoy seguro que tú también lo harías, si no fuera porque solo es un ejemplo). – lsoliveira

0

Esto puede parecer un poco feo, pero por lo general le puede ofrecer la funcionalidad requerida.

Usted puede tener interfaz

public interface MyEnumInterface<T extends Enum<T>> { 

    String getBusinessName(); 

    T getEnum(); 

} 

Implementación

public enum OneOfMyEnums implements MyEnumInterface<OneOfMyEnums>{ 

    X, Y, Z; 

    @Override 
    public String getBusinessName() { 
     return "[OneOfMyEnums]" + name(); 
    } 

    @Override 
    public OneOfMyEnums getEnum() { 
     return this; 
    } 

} 

y utilidad de clase en lugar de su clase padre

public class MyEnumUtils { 

    public static <T extends Enum<T>> String doSomething(MyEnumInterface<T> e){ 
     e.getBusinessName(); // can use MyEnumInterface methods 
     e.getEnum().name(); // can use Enum methods as well 
     return null; 
    } 

} 
+0

Este enfoque es interesante, pero invierte el contrato al hacer que el cliente confíe en las clases de utilidad en lugar del 'enum's (el método' equals() 'entra en' MyEnumUtils'). – lsoliveira