2011-06-01 18 views
8

Tengo una clase abstracta llamada DatabaseRow que, después de ser derivada y construida, se carga principalmente desde un método Load(object id).Mejor práctica para el encadenamiento de métodos ("devuelva esto")

Tengo un montón de código que crea una nueva instancia de la clase, la carga desde una ID y luego devuelve la clase. Me gustaría simplificar este código en una línea de código (para ser claro, hay muchas clases que solo tendrán listas de propiedades que devuelven estas instancias cargadas).

Hay dos maneras en que puedo pensar en hacerlo, pero ninguno me parece "correcto".

1. pude return this; al final de mi método Load y uso return new Derived().Load(id);

2. que podría crear un método genérico para devolver un método cargado.

public static T LoadRow<T>(object id) where T : DatabaseRow, new() 
{ 
    T row = new T(); 
    row.Load(id); 
    return row; 
} 

que he visto algún otro código que utiliza el mismo método que el número , pero nunca he visto cualquier desarrollador con experiencia lo recomiendo, ni he encontré con cualquiera de los métodos en el marco .NET que hacer lo mismo, entonces tal vez esta no es la mejor práctica?

¿Alguien sabe de alguna otra solución que podría ser mejor que ambas?

Solución:

Después de leer el comentario de respuesta y SirViver, me di cuenta de que todas las propiedades que se devuelven necesidad de almacenar en caché de todos modos. La solución fue visualmente diferente, pero similar a la opción (que no esperaba que nadie obtuviera esta respuesta ya que no explico esta parte del diseño)

Todas estas instancias se cargarán desde un valor recuperado de otra columna en la base de datos (relaciones de la base de datos si lo desea). En lugar de tratar de cargar la nueva instancia desde este valor, hice un método para cargar la instancia desde el nombre de la columna y almacenar en caché el valor cargado en un diccionario. Esto funciona bien ya que esta es una de las principales funciones de la clase DatabaseRow.

private Dictionary<string, DatabaseRow> linkedRows; 

    protected T GetLinkedRow<T>(string key) where T : DatabaseRow, new() 
    { 
     if (linkedRows.ContainsKey(key)) return (T)linkedRows[key]; 
     else 
     { 
      T row = new T(); 
      row.Load(this[key]); 
      linkedRows.Add(key, row); 
      return row; 
     } 
    } 

Respuesta

3

Personalmente, creo que es una mala práctica a llamadas de método en cadena que tienen sideffects reales en la instancia del objeto. Para ser honesto, creo que ambos ejemplos son "hacks" bastante feos, cuyo único propósito es guardar dos líneas de código. No creo que el resultado sea realmente más legible tampoco.

Si desea que se cargue un registro de inmediato, probablemente prefiero suministrar una variante de constructor que tome la ID de la que carga y haga que el objeto se rellene automáticamente en la construcción, aunque cuando lo pienso no lo haría. No te preocupes en absoluto por ser sincero: incluir más información en una sola línea no genera más código de lectura y mantenimiento.

+0

En mi caso, tengo una clase que hereda 'DatabaseRow', y tengo 5 propiedades que usan esta misma función. Tener 5 líneas de código, similar a 'public Derived MyProperty {get {return new Derived(). Load (myInt); }} 'sería mucho mejor que separar las propiedades en código de varias líneas. – Connell

+0

Pero, ¿qué importancia tiene la "pulcritud" del código de propiedad? En la mayoría de los casos, el código de propiedad debería colapsarse por Visual Studio de todos modos, y no es como si tuviera que mirar constantemente la implementación de la clase de todos modos. Dicho esto, construir y cargar datos en un acceso a la propiedad parece bastante peligroso para empezar.Si alguien no sabe que el resultado debe almacenarse en caché, esto podría dar como resultado un mal rendimiento y/o un comportamiento inesperado. – SirViver

+0

Eso es todo! Me siento como un idiota ahora. ¡Tu comentario sobre el almacenamiento en caché me recordó que ya creé esta función justo en la parte inferior de mi clase DatabaseRow! 'protected T GetLinkedRow (clave de cadena) donde T: DatabaseRow, new()' carga la fila y la almacena en caché en un diccionario según el valor de 'key'. – Connell

2

Número 1 está ganando popularidad en algunos círculos; a menudo se llama , programación fluida cuando hay una interfaz que define grandes cantidades de estos métodos y se pueden ensamblar cadenas largas. La clave para hacerlo con éxito nunca es definir un método que a veces devuelve this, pero otras veces devuelve null. Si siempre se devuelve this (a menos que haya una excepción), es un estilo perfectamente bueno.

Personalmente no soy tan aficionado a las soluciones como el número 2, ya que se podría decir que viola el principio de "una responsabilidad".

0

Aunque personalmente me gustan los métodos que devuelven this, ya que les permite estar encadenados, creo que en el marco .NET (hasta Linq), esto estaba mal visto. La razón que me viene a la mente es la siguiente:

Los métodos devuelven un resultado o cambian el estado del objeto.Devolver this es un poco de ambos: cambiar el estado del objeto y luego devolver un "resultado" - excepto que el resultado es el objeto original modificado. Esto no se ajusta a las expectativas del usuario.

¿Qué hay de:

public class Derived : DatabaseRow 
{ 
    public Derived(object id): 
    { 
     Load(id); 
    } 
} 

Y utilizar de esta manera:

return new Derived(id); 
+0

No podría estar más de acuerdo con su segundo párrafo aquí. Originalmente estaba usando algo similar a su solución, sin embargo, decidí no hacerlo y cambié al método Load porque no creo que los constructores tengan tanta funcionalidad (en este caso, ejecutar consultas SQL) – Connell

+0

@ Connell Watkins: Tengo pensé de manera similar acerca de los constructores antes que yo, pero ahora los considero como una garantía de que el objeto está en un estado utilizable. Entonces, si los objetos solo están destinados a ser usados ​​después de llamar al método Load, ¡entonces puedes poner eso en el constructor! –

1

La opción 1 solo es aceptable si es posible tener una fila sin cargar. Esto se debe al patrón que le permite hacer esto:

return new Derived(); 

Personalmente prefiero el método estático. Pero sospecho que es simplemente una cuestión de preferencia personal.

Como ssg dice que la otra opción (llamémosla opción 3) es sobrecargar el constructor en Derived, que también funcionaría, sin embargo, tener muchos constructores puede ser confuso ya que el código de llamada no tiene nada que describa Que esta pasando.

Opción 1:

return new Derived().Load(10); 

Opción 2:

return Derived.Load(10); 

Opción 3:

return new Derived(10); 

Opción 1 parece que está recibiendo un objeto superfluo creado. La opción 2 es buena porque hace lo que parece que está haciendo. La opción 3 deja confusión sobre lo que hace.

+0

Es muy posible tener una clase y no cargarla. Si estuviera creando una nueva fila en la base de datos, crearía una nueva instancia, llenaría las propiedades y luego llamaría a un método 'Save()'. ¿Te refieres al método estático genérico? ¿O un método estático en cada clase derivada? – Connell

+0

Ah, acabo de ver su edición. La opción 2 parece perfecta, pero la cuestión es que me obligaría a crear el mismo método estático para cada clase derivada de este tipo, que parece ser innecesaria. – Connell

Cuestiones relacionadas