2011-10-24 14 views
15

He visto a menudo y se utiliza enumeraciones con atributos asociados que hacer algunas cosas básicas tales como proporcionar un nombre para mostrar o la descripción:El "Enum como objeto rico inmutable": ¿se trata de un antipatrón?

public enum Movement { 
    [DisplayName("Turned Right")] 
    TurnedRight, 
    [DisplayName("Turned Left")] 
    [Description("Execute 90 degree turn to the left")] 
    TurnedLeft, 
    // ... 
} 

y han tenido una serie de métodos de extensión para apoyar los atributos:

public static string GetDisplayName(this Movement movement) { ... } 
public static Movement GetNextTurn(this Movement movement, ...) { ... } 

Siguiendo este patrón, se podrían aplicar atributos adicionales existentes o personalizados a los campos para hacer otras cosas. Es casi como si la enumeración puede funcionar como el tipo simple valor enumerado que es y como más rica de objetos de valor inmutable con un número de campos:

public class Movement 
{ 
    public int Value { get; set; } // i.e. the type backing the enum 
    public string DisplayName { get; set; } 
    public string Description { get; set; } 
    public Movement GetNextTurn(...) { ... } 
    // ... 
} 

De esta manera, se pueden "viajar" como una simple campo durante la serialización, compararse rápidamente, etc. pero el comportamiento puede ser "internalizado" (ala OOP).

Dicho esto, reconozco que esto se puede considerar un antipatrón. Al mismo tiempo, una parte de mí considera que esto es lo suficientemente útil como para que el anti sea demasiado estricto.

+4

¿Estás preguntando si es una mala idea usar este patrón en lugares donde no estarías usando una enumeración? Es un gran patrón para asociar metadatos a valores enum.No es tan bueno como un reemplazo para las clases. –

+0

Podría considerarse un "abuso" del idioma; casi como usar 'dynamic' todo el tiempo cuando no está garantizado, por ejemplo. – Kit

+1

No exactamente sobre el punto (de ahí el comentario), pero tengo dudas sobre la codificación de texto que se mostrará en un atributo, o en cualquier lugar cerca de una clase de entidad, para el caso. Por un lado, creo que viola groseramente la "separación de preocupaciones" y por otro, no hay forma aparente de localizarlo. Por desgracia, MS parece alentar este tipo de cosas, así que tal vez estoy fuera a almorzar. –

Respuesta

6

Considero que este es un patrón pobre en C# simplemente porque el soporte del lenguaje para declarar y acceder a los atributos está tan lisiado; no están destinados a ser almacenes de mucha información. Es doloroso declarar atributos con valores no triviales, y es doloroso obtener el valor de un atributo. Tan pronto como desee algo remotamente interesante asociado con su enumeración (como un método que compute algo en la enumeración, o un atributo que contenga un tipo de datos no primitivos), deberá refactorizarlo a una clase o poner la otra cosa en algún lugar fuera de banda.

No es realmente más difícil hacer una clase inmutable con algunas instancias estáticas con la misma información, y en mi opinión, es más idiomática.

+0

No es difícil, no, en términos de * declaración * de un enum frente a un objeto rico inmutable, y hay dolor para el implementador de rich-enum, pero ¿qué pasa con * use *? Aunque tu punto en * idioma * me sienta bien. – Kit

+0

Buen punto sobre cómo escribir código idiomático. Siempre quieres escribir código que se adapte bien al lenguaje/marco en el que vive. – FMM

5

Diría que es un antipatrón. Este es el por qué. Vamos a tomar su enumeración existente (stripping atributos para mayor brevedad aquí):

public enum Movement 
{ 
    TurnedRight, 
    TurnedLeft, 
    Stopped, 
    Started 
} 

Ahora, digamos que se expande la necesidad de ser algo un poco más precisos; decir, un cambio de rumbo y/o velocidad, convirtiendo un "campo" en su "pseudo-clase" en dos:

public sealed class Movement 
{ 
    double HeadingDelta { get; private set; } 
    double VelocityDelta { get; private set; } 
    // other methods here 
} 

Por lo tanto, usted tiene una enumeración codificado que ahora tiene que ser transformada en una clase inmutable porque ahora está rastreando dos propiedades ortogonales (pero aún inmutables) que realmente pertenecen juntas en la misma interfaz. Cualquier código que haya escrito contra su "enum rica" ​​ahora debe ser destruido y rediseñado significativamente; mientras que, si hubieras comenzado con ella como clase, probablemente tendrías menos trabajo que hacer.

Tienes que preguntar cómo se va a mantener el código a lo largo del tiempo, y si la enumeración rica será más fácil de mantener que la clase. Mi apuesta es que no sería más fácil de mantener. Además, como señaló mquander, el enfoque basado en la clase es más idiomático en C#.

Algo más a considerar, también. Si el objeto es inmutable y es struct en lugar de class, se obtiene la misma semántica de pase por valor y diferencias insignificantes en el tamaño de serialización y el tamaño de tiempo de ejecución del objeto como lo haría con la enumeración.

+0

Me he encontrado con esto antes y no puedo estar más de acuerdo. –

Cuestiones relacionadas