c# - polimorfismo - interfaces poo
¿Por qué se requieren clases Interface Layer/Abstract en nuestro proyecto? (8)
Creo que estás hablando de la capa Fachada.
Es una capa opcional que simplificará las funciones de Business Layer. Imaginemos, tiene un ProductManager y CategoryManager y desea realizar una acción particular que implique utilizar ambos (por ejemplo, obtener los 5 mejores productos en todas las categorías), entonces podría usar una capa de fachada que use ProductManager y CategoryManager.
Está inspirado en Facade Pattern .
Normalmente utilizamos funciones / interfaces abstractas en nuestros proyectos. ¿Por qué es realmente necesario? ¿Por qué no podemos simplemente ir por la Capa de lógica de negocios, la Capa de acceso a datos y la Capa de presentación solamente?
Función en la capa de presentación:
abc();
Función en Business Logic Layer:
public void abc()
{
//Preparing the list
}
Función en la capa de acceso a datos:
public abstract void abc();
Función en Data Access SQLServer Layer:
public override void abc()
{
//Connection with database
}
La pregunta es: ¿Por qué se requiere la capa de acceso a los datos?
En general, si usa interfaces en su código, obtendrá la capacidad de manutención del código en forma de Inyección de Dependencia.
Esto le ayudará a reemplazar partes de su implementación en ciertas situaciones, por ejemplo, proporcionando objetos Simulados durante la Prueba de la Unidad.
La abstracción ayuda a crear funcionalidad, ya sea a través de una clase base, una interfaz o composición que, cuando se usa correctamente, hace maravillas para el mantenimiento, la legibilidad y la reutilización del código.
Con respecto al código publicado en la pregunta, el código marcado como "Capa de acceso a los datos" actúa como una abstracción común para que la capa de negocios lo use. Al hacerlo, las implementaciones específicas del DAL (como lo que está debajo de "Data Access SQLServer Layer" en el ejemplo) están desacopladas de la capa empresarial. Ahora puede realizar implementaciones de DAL que accedan a bases de datos diferentes, o tal vez alimentar datos automáticamente para pruebas, etc.
El patrón de repositorio es un fantástico ejemplo de esto en funcionamiento en un DAL (el ejemplo se simplifica):
public interface IProductRepository
{
Product Get(int id);
...
}
public class SqlProductRepository : IProductRepository
{
public Product Get(int id) { ... }
...
}
public class MockProductRepository : IProductRepository
{
private IDictionary<int, Product> _products = new Dictionary<int, Product>()
{
{ 1, new Product() { Name = "MyItem" } }
};
public Product Get(int id) { return _products[id]; }
...
}
public class AwesomeBusinessLogic
{
private IProductRepository _repository;
public AwesomeBusinessLogic(IProductRepository repository)
{
_repository = repository;
}
public Product GetOneProduct()
{
return _repository.GetProduct(1);
}
}
Aunque este ejemplo usa interfaces, lo mismo se aplica al uso de clases base. La belleza es que ahora puedo alimentar SqlProductRepository
o MockProductRepository
en MockProductRepository
y no tener que cambiar nada sobre AwesomeBusinessLogic
. Si aparece otro caso, todo lo que se necesita es una nueva implementación de IProductRepository
y IProductRepository
todavía lo manejará sin cambios porque solo accede al repositorio a través de la interfaz.
La forma más fácil de entender esto, imo, es una abstracción sobre DataLayer
.
Ha configurado una función para recuperar datos del archivo xml
. Pero un día su producto se amplía y xml
no es suficiente como un almacenamiento de datos. Entonces pasas a una base de datos embebida: sqlite
. Pero un día necesita reutilizar su biblioteca en algún contexto empresarial. Entonces, ahora necesita desarrollar acceso a sqlserver
, oracle
, webservice
... En todos estos cambios, necesitará cambiar no solo el código que realmente accede a los datos, sino también el código que realmente lo consume . ¿Y el código que ya usa durante años su primer acceso a datos xml
en el cliente y está contento con él? ¿Qué hay de la compatibilidad?
Tener la abstracción si no soluciona directamente la mayoría de estos problemas, pero definitivamente hace que su aplicación sea scalable y más resistente a los cambios que, en nuestro mundo, ocurren a veces con demasiada frecuencia.
Todas las respuestas anteriores pueden explicar las necesidades de las capas abstractas, pero aún quiero agregar algunas de mis ideas.
Digamos que en nuestro proyecto solo tenemos una implementación de un servicio en cada capa. Por ejemplo, tengo un contacto DAL y un servicio de contacto BLL, y podríamos hacer algo como esto
namespace
{
public class ContactDbService
{
public Contact GetContactByID(Guid contactID)
{
//Fetch a contact from DB
}
}
}
Póngase en contacto con el servicio BLL:
namespace
{
public class ContactBLLService
{
private ContactDbService _dbService;
public ContactBLLService()
{
_dbService = new ContactDbService();
}
public bool CheckValidContact(Guid contactID)
{
var contact = _dbService.GetContactByID(contactID);
return contact.Age > 50;
}
}
}
sin definir interfaces / clases abstractas.
Si lo hacemos así, habría algunos inconvenientes obvios.
- Comunicación de código: imagine que si su proyecto involucra, sus servicios pueden tener muchos métodos diferentes, ¿cómo podría un mantenedor (aparte de usted) saber qué hacen sus servicios? ¿Tendrá que leer todo su servicio para solucionar un pequeño error como InvalidCastOperation? Al observar la interfaz, las personas tendrán un conocimiento inmediato de las capacidades del servicio (al menos).
Examen de la unidad
Puede probar su lógica utilizando un servicio falso / falso para detectar errores de antemano, así como evitar errores de regresión más adelante.
Más fácil de cambiar:
Al hacer referencia solo por interfaces / clases abstractas en otras clases, podría reemplazar fácilmente esas implementaciones de interfaz más adelante sin demasiados esfuerzos de trabajo.
La clase o interfaz abstracta no es realmente una capa separada: debe ser parte de la capa de lógica empresarial y define la interfaz que la capa de acceso a datos real (repositorio de datos SQL, por ejemplo) necesita implementar para proporcionar el servicio de acceso a datos a su capa de negocios.
Sin esta interfaz, su capa de negocios dependería directamente de la capa de SQL, mientras que la interfaz eliminaría esta dependencia: coloque la clase abstracta o la interfaz en la capa de lógica de negocios. Luego, la capa SQL (un ensamblaje separado, por ejemplo) implementa la clase / interfaz abstracta. De esta forma, la capa de SQL depende de la capa de negocios, no al revés.
El resultado es una aplicación flexible con una capa empresarial independiente que puede funcionar con múltiples repositorios de datos; todo lo que necesita es una capa que implemente la interfaz que define la capa empresarial. Y no se trata solo de repositorios de datos: la capa de su empresa no debe depender del contexto (asp.net frente a la aplicación de consola frente a servicio, etc.), no debe depender de las clases de interfaz de usuario, los módulos que interactúan con su aplicación comercial, etc.
¿Por qué interfaces? ¿Alguna vez ha utilizado en c #: using (Formulario f = new Form ()) {}
Aquí verá que puede usar solo aquellas clases internas que implementen la interfaz IDisposable.
Dos cosas que no se conocen entre sí pueden interactuar entre sí utilizando solo interfaces. La interfaz garantiza que la "cierta" funcionalidad seguramente ha sido implementada por este tipo.
Por qué capas:
Para que pueda tener dlls separadas que le permitirán reutilizar en diferentes aplicaciones.
Básicamente todo es para la reutilización del código y la ganancia de rendimiento.
La abstracción le permite hacer refactorizaciones rápidamente. Piensa que en lugar de usar SQL server, decides usar algún otro proveedor; si no tiene una capa de acceso a datos, entonces debe hacer un enorme refactor porque está llamando métodos de acceso a datos directamente. Pero si tiene una capa de acceso a datos, solo escribe una nueva capa de acceso a datos, hereda de su capa de acceso a datos abstractos y no cambia nada en la capa empresarial.