Considere estos métodos:
public List<Employee> GetAllEmployees()
{
using (Entities entities = new Entities())
{
return entities.Employees.ToList();
}
}
public List<Job> GetAllJobs()
{
using (Entities entities = new Entities())
{
return entities.Jobs.ToList();
}
}
public List<Task> GetAllTasksOfTheJob(Job job)
{
using (Entities entities = new Entities())
{
return entities.Tasks.Where(t => t.JobId == job.Id).ToList();
}
}
Usar el bloque es igual y se ha repetido 3 veces aquí (por supuesto, más de 100 veces en la aplicación real). ¿Cómo es posible implementar el DRY (Don't Repeat Yourself) principal para el using
bloque? ¿Se considera una violación del director DRY?
Actualización: no estoy hablando de lo que se ha implementado dentro del using
bloque. Lo que realmente quiero decir aquí, es el using (Entities entities = new Entities())
. Esta línea se repite 100 veces o más.
c#
design-patterns
dry
Saeed Neamati
fuente
fuente
Respuestas:
Una idea sería envolverlo con una función que tome a
Func
.Algo como esto
Entonces su código anterior se convierte en
También hice
Entities
un parámetro de tipo, porque supongo que tienes más de un tipo con el que estás haciendo esto. Si no lo está, puede eliminarlo y simplemente usar el tipo param para el tipo de retorno.Para ser sincero, este tipo de código no ayuda en absoluto a la legibilidad. En mi experiencia, los compañeros de trabajo de Jr también lo pasan muy mal.
Actualización Algunas variaciones adicionales sobre los ayudantes que podría considerar
fuente
Entities
.IEnumerable
fuera de la función en caso de que hubiera noIEnumerable
propiedades de T que la persona que llama quería que se devolviera, pero tiene razón, lo limpiaría un poco. Quizás tener un ayudante tanto para Single como paraIEnumerable
resultados sería bueno. Dicho esto, todavía creo que ralentiza el reconocimiento de lo que hace el código, especialmente para alguien que no está acostumbrado a usar muchos genéricos y lambdas (por ejemplo, sus compañeros de trabajo que NO están en SO :))WithEntities
, use enFunc<T,IEnumerable<K>>
lugar deFunc<T,K>
y asigne a "WithEntities" un nombre mejor (como SelectEntities). Y no creo que "Entidades" deba ser un parámetro genérico aquí.where T : IDisposable, new()
, como seusing
requiereIDisposable
para trabajar.Para mí, esto sería como preocuparse por predecir la misma colección varias veces: es algo que debe hacer. Cualquier intento de abstraerlo aún más hará que el código sea mucho menos legible.
fuente
foreach
trata de una colección muy grande o si la lógica dentro delforeach
ciclo consume mucho tiempo. Un lema que he llegado a adoptar: no te obsesiones, pero siempre ten en cuenta tu enfoqueParece que está confundiendo el principio "Once and Only Once" con el principio DRY. El principio DRY establece:
Sin embargo, el principio Once and Only Once es ligeramente diferente.
El principio DRY generalmente se usa en el contexto de la lógica real, no tanto redundante usando declaraciones:
Fuente
fuente
No veo el uso de
using
aquí:Qué tal si:
O incluso mejor, ya que no creo que necesite crear un nuevo objeto cada vez.
En cuanto a violar DRY: DRY no se aplica en este nivel. De hecho, ningún principio realmente lo hace, excepto el de legibilidad. Intentar aplicar DRY a ese nivel es realmente una microoptimización arquitectónica, que al igual que todas las microoptimizaciones es solo arrojar bicicletas y no resuelve ningún problema, pero incluso se arriesga a introducir otras nuevas.
Según mi propia experiencia, sé que si intentas reducir la redundancia de código a ese nivel, creas un impacto negativo en la calidad del código, al ofuscar lo que era realmente claro y simple.
Editar:
Ok. Entonces, el problema no es en realidad la declaración de uso, el problema es la dependencia del objeto que crea cada vez. Sugeriría inyectar un constructor:
fuente
using (CustomTransaction transaction = new CustomTransaction())
bloque de código en nuestro código para definir el alcance de una transacción. Eso no se puede agrupar en un solo objeto y en cada lugar donde desee usar una transacción, debe escribir un bloque. Ahora, ¿qué pasa si desea cambiar el tipo de esa transacciónCustomTransaction
aBuiltInTransaction
más de 500 métodos? Esto me parece una tarea repetitiva y un ejemplo de incumplimiento del director DRY.using
(en este contexto) una "sintaxis conveniente" más desconocida? Por qué es tan agradable de usar =)No solo usar es código duplicado (por cierto, es código duplicado y en realidad se compara con una declaración try..catch..finally) sino también toList. Refactorizaría su código así:
fuente
Dado que no hay ninguna lógica de negocios de ningún tipo aquí, excepto la última. No es realmente SECO, en mi opinión.
El último no tiene DRY en el bloque de uso, pero supongo que la cláusula where debería cambiar donde sea que se haya usado.
Este es un trabajo típico para los generadores de código. Escriba y cubra el generador de código y déjelo generar para cada tipo.
fuente
using (Entities entities = new Entities())
bloquear. Quiero decir, esta línea de código se repite 100 veces y se repite cada vez más.Como está creando y destruyendo el mismo objeto desechable una y otra vez, su clase es en sí misma un buen candidato para implementar el patrón IDisposable.
Esto te deja solo necesitando el "uso" al crear una instancia de tu clase. Si no desea que la clase sea responsable de deshacerse de los objetos, puede hacer que los métodos acepten la dependencia como argumento:
fuente
¡Mi parte favorita de magia incomprensible!
Wrap
existe solo para abstraer eso o cualquier magia que necesites. No estoy seguro de recomendarlo todo el tiempo, pero es posible usarlo. La idea "mejor" sería usar un contenedor DI, como StructureMap, y solo establecer el alcance de la clase Entities en el contexto de la solicitud, inyectarlo en el controlador y luego dejar que se ocupe del ciclo de vida sin que su controlador lo necesite.fuente
Func
suficiente como debería.