2011-05-16 15 views
6

Tengo un problema similar al del Accessing a static property of a child in a parent method. La respuesta preferida insinúa que el diseño de las clases es defectuoso y se necesita más información para analizar el problema.Acceder a una propiedad estática de un niño en un método principal - Consideraciones de diseño

Aquí está la situación que deseo analizar con usted.

Quiero implementar algunos tipos de datos de unidad de conocimiento como longitud, masa, actual, ... Debe haber un molde implícito para crear las instancias de una cadena dada. Como ejemplo "1.5 m" debería dar lo mismo que "150 cm", o "20 in" debe tratarse correctamente.

Para poder convertir unidades diferentes, necesito constantes de conversión de cantidades específicas. Mi idea era crear una clase base abstracta con algunos métodos de traducción estáticos. Esos deben usar el diccionario específico de la clase definido estáticamente para hacer su trabajo. Así que eche un vistazo al ejemplo.

public class PhysicalQuantities 
{ 
    protected static Dictionary<string, double> myConvertableUnits; 

    public static double getConversionFactorToSI(String baseUnit_in) 
    { 
     return myConvertableUnits[baseUnit_in]; 
    } 
} 

public class Length : PhysicalQuantities 
{ 
    protected static Dictionary<string, double> myConvertableUnits = new Dictionary<string, double>() 
    { 
     { "in", 0.0254 }, { "ft", 0.3048 } 
    }; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Length.getConversionFactorToSI("in"); 
    } 
} 

creo que esto le da un uso más intuitivo y mantiene el código compacto y muy fácil de leer y extensible. Pero, por supuesto, encontré los mismos problemas que describe el post.

Ahora mi pregunta es: ¿Cómo puedo evitar este problema por diseño?

+0

Me pregunto si una conversión definida como 'f (doble)' alguna vez te meterá en problemas . Puede ser que una conversión requiera algo más. 'Func ' o 'Func ' podría ser mejor. – Hogan

+0

puede pasar de estático a no estático - no creo que pierda mucho –

Respuesta

4

Creo que esto podría resolverse con genéricos para que aún se vea legible. Refinado según la sugerencia de Slaks para ajustar el registro en el constructor estático para que sea seguro en sí mismo.

Así que si no me equivoco:

  • hilo de seguridad (todos los trabajos en el diccionario en el constructor estático)
  • sintaxis sigue siendo fácil de usar y fácil de leer SIConversion<Length>.GetFactor() (1 carácter más)
  • código necesario para implementar en las clases derivadas muy repetitivo register(string,double); (en realidad más corto que su definición del diccionario)

    interface ISIConversionSubscriber 
    { 
        void Register(Action<string, double> regitration); 
    } 
    
    static class SIConversion<T> where T : ISIConversionSubscriber, new() 
    { 
    
        private static Dictionary<string, double> myConvertableUnits = new Dictionary<string, double>(); 
    
        static SIConversion() { 
         T subscriber = new T(); 
         subscriber.Register(registrationAction); 
        } 
    
        public static double GetFactor(string baseUnit) 
        { 
         return myConvertableUnits[baseUnit]; 
        } 
    
        private static void registrationAction(string baseUnit, double value) 
        { 
         myConvertableUnits.Add(baseUnit, value); 
        } 
    
    } 
    
    abstract class PhysicalQuantities : ISIConversionSubscriber 
    { 
        public abstract void Register(Action<string, double> register); 
    } 
    
    class Length : PhysicalQuantities 
    { 
        public override void Register(Action<string, double> register) 
        { 
         // for each derived type register the type specific values in this override 
         register("in", 1); 
        } 
    } 
    
    class Program 
    { 
        static void Main(string[] args) 
        { 
         Console.WriteLine(SIConversion<Length>.GetFactor("in")); 
        } 
    } 
    

Salida: 1

En caso de que se preguntan por qué hice PhysicalQuantities abstracta: evitar el uso con SIConversion<PhysicalQuantities>.GetFactor() ya que no tenemos una conversión para una clase base. Probablemente no necesites instancias de una clase base como esta de todos modos: no es una representación completa de una cantidad, por lo que probablemente solo contenga métodos reutilizables.

Otra sugerencia sería usar un Enum para la baseUnit en lugar de una cadena. Dado que todo el mundo se esfuerza por la seguridad y el llanto de las cadenas mágicas, es probable que sea un buen camino a seguir :))

+0

http://stackoverflow.com/questions/686630/static-generic-class-as-dictionary – SLaks

+0

Estoy completamente consciente de que el bloqueo es necesario para la seguridad de la rosca. Es necesario incluso si se utiliza SIConversion < Length> .GetFactor(), ya que el diccionario no es seguro para hilos per se. Entonces, todo va a la preferencia de sintaxis de codificación. Lo mismo sucedería si dos hilos estuvieran accediendo a una conversión de Si simple < Length>. Se necesitan bloqueos en cualquiera de los casos:/ –

+1

Una clase estática probablemente también sea más rápida, y (en mi humilde opinión) más elegante. – SLaks

3

La mejor opción aquí, por lo general, suele ser evitar la naturaleza estática de su diseño y, en cambio, trabajar en las instancias. Tengo una biblioteca similares que he desarrollado, pero el uso tiende a ser más como:

static void Main() 
{ 
    // Since I'm working with instances, I tend to pass the actual 
    // measured amount in as well... However, you could leave this out (or pass 1) 
    var len = Length.Create(42, "in"); 
    double conversionFactory = len.ConversionFactorToSI; 
} 

he desarrollado de esta manera para que yo pudiera definir un diccionario según el tipo, de forma estática, pero el uso de un método de fábrica que pasa esto al constructor de la clase base (que está protegido). Esto permite crear una instancia de la clase base con una referencia al diccionario del tipo específico, que funciona muy limpiamente.

2

He descubierto que el desarrollo impulsado por pruebas a menudo me ha llevado también a mejores diseños. En su caso, los 'métodos de traducción' son una pieza importante que querrá probar independientemente de su uso en sus clases. Sugeriría encapsular esa lógica en su propio tipo.

Luego puede centrarse en las instancias como sugiere Reed sabiendo que su lógica de traducción está bien probada. Su clase base abstracta simplemente sirve como una raíz común que sabe cómo obtener el traductor correcto.

Cuestiones relacionadas