patterns - strategy pattern c#
Implementación de patrones de repositorio (5)
El consenso se está construyendo: 2ª opción hasta el final. Aparte de la lógica de consulta que se filtra por todas partes con IQueryable, la dificultad de implementarlo correctamente, es muy difícil realizar PRUEBAS y simular.
Parece que cada ejemplo que encuentro del patrón de repositorio, la implementación es diferente de alguna manera. Los siguientes son los dos ejemplos que principalmente encuentro.
interface IProductRepository
{
IQueryable<Product> FindAll();
}
Por lo general, luego hay otra capa que habla con el repositorio y llama al método FindAll () y realiza cualquier operación, como encontrar productos que comiencen con la letra ''s'' o buscar productos en una categoría particular.
El otro ejemplo me parece mucho poner todos los métodos de búsqueda en el repositorio
interface IProductRepository
{
IEnumerable<Product> GetProductsInCategory(int categoryId);
IEnumerable<Product> GetProductsStartingWith(string letter);
IEnumerable<PromoCode> GetProductPromoCodes(int productId);
}
¿Qué camino me recomienda tomar? ¿O cuáles son las ventajas / desventajas entre sí?
A mi entender, después de leer http://martinfowler.com/eaaCatalog/repository.html el primer enfoque parece reflejar mejor esto?
El primero es horrible. IQueryable
es como un objeto DIOS . Es muy difícil encontrar una implementación completa al 100% (incluso entre todos los OR / M). Puede exponer su ORM directamente en lugar de usarlo ya que, de lo contrario, probablemente obtendrá una capa de abstracción permeable .
Joel lo dice mejor (el texto es del artículo de wikipedia ):
En el artículo de Spolsky, llama la atención sobre muchos ejemplos de abstracciones que funcionan la mayor parte del tiempo, pero donde no se puede ignorar un detalle de la complejidad subyacente y, por lo tanto, genera complejidad en el software que se suponía que se simplificaba con la abstracción misma.
El segundo enfoque es mucho más fácil de implementar y de mantener intacta la abstracción.
Actualizar
Su repositorio está violando el principio de responsabilidad única, ya que tiene dos razones para cambiar. La primera es si se cambia la API de productos y la otra si se modifica la API de código de promoción. Debes usar dos repositorios diferentes como:
interface IProductRepository
{
IEnumerable<Product> FindForCategory(int categoryId);
IEnumerable<Product> FindAllStartingWith(string letter);
}
interface IPromoCodeRepository
{
IEnumerable<PromoCode> FindForProduct(int productId);
}
Cambió las cosas:
- Tiendo a comenzar con los métodos
Find
cuando se devuelven varios elementos yGet
si se devuelve un solo elemento. - Nombres de métodos más cortos = más fácil de leer.
- Una sola responsabilidad. Es más fácil saber qué tienen las clases que usan los repositorios para las dependencias.
Las interfaces pequeñas y bien definidas hacen que sea más fácil detectar violaciones de los principios de SOLID, ya que las clases en que se rompen los principios tienden a generar constructores inflados.
En realidad creo que el primero es mejor. Asumo mi decisión sobre los siguientes factores:
Si la estructura del producto se refacta:
- Enfoque de IQueryable: solo necesita cambiar los métodos de llamada en el código.
- IEnumerables: también es necesario cambiar los nombres de los métodos.
Si muchos están a punto de derivar las interfaces que desea iterar de forma polimórfica:
- Enfoque de IQueryable - beneficio de los nombres de métodos uniformes genéricos
- IEnumerables: es posible que algunos nombres no describan el método que necesita.
Elasticidad
- Enfoque de IQueryable: fácil de dividir en el enfoque de IEnumerables.
- Enfoque de IEnumerables: difícil de convertir de nuevo al enfoque de IQueryable.
Por lo tanto, le sugiero que comience con IQueryable como opción predeterminada y, a medida que avance con su código, siempre puede cambiar al enfoque de IEnumerables más específico que necesita.
Personalmente, sugeriría usar el segundo ejemplo, de esa manera usted está encapsulando la lógica de búsqueda en un solo lugar y la intención de la persona que llama está claramente definida por el nombre del método que están llamando. Si sigue el primer ejemplo, su código de consulta se filtrará a lo largo de su aplicación y terminará duplicando las consultas.
Recomiendo evitar las duplicaciones. Ese es el primer objetivo. Si tiene una lógica que encuentra productos que comienzan con alguna letra en varios lugares, entonces es un caso especial y vale la pena extraerlo a un método separado (también proporciona una buena descripción para su caso específico). Código sin duplicaciones mucho más fácil de cambiar, entender y mantener.
Por lo tanto, tiendo a tener un método de búsqueda genérico con IQueryable
y un conjunto de métodos que se utilizaron más de una vez:
interface IRepository<T>
{
IQueryable<T> FindAll();
}
interface IProductRepository : IRepository<Product>
{
IEnumerable<Product> GetProductsInCategory(int categoryId);
IEnumerable<Product> GetProductsStartingWith(string letter);
IEnumerable<PromoCode> GetProductPromoCodes(int productId);
}
Considere también la prueba de unidad. Los métodos específicos son mucho más fáciles de burlar, que IQueryable.