design patterns - patterns - ¿Es una buena o mala práctica usar interfaces uno a uno con entidades de dominio? ¿Por qué?
patrones de diseño java (6)
¿Por qué este ha desaparecido?
Encuentro que tener una interfaz para un objeto de dominio, como dijo Charlie Martin, me permite elegir mi implementación.
Un ejemplo fundamental es el identificador (object.Id) para un objeto, esto será diferente dependiendo de dónde almacene ese objeto y la responsabilidad de crearlo puede o no depender de la implementación de los datos más adelante. En SQL Server, puede optar por un número automático, pero en el almacenamiento de tablas de Azure puede buscar un Guid, pero no quiere tener que cambiar la lógica de negocios de su aplicación porque cambia el lugar donde almacena sus datos.
Puedo o no elegir si mi objeto de dominio persiste, o incluso usarlo en la capa de presentación; depende del alcance de mi aplicación, que es el mejor. Pero agregar un conjunto de interfaces de dominio comunes en una capa común me permite escribir servicios contra ellos y reutilizarlos una y otra vez.
Repasaríamos el mismo argumento sobre qué debería ser una dirección si no tuviéramos IAddress, los nuevos programadores estarían reescribiendo cosas para tarjetas de crédito si no fuera para ICreditCard.
La etiqueta anti-patrón es un mal uso del lenguaje, es una simplificación excesiva para describir el valor de las soluciones para tareas complejas y variadas.
Hay un lugar para la mayoría de los patrones, incluso el difamado Singleton, lo que significa que no es un "antipatrón", al menos en lo que sugiere el término.
Una cosa que veo en algunas aplicaciones empresariales de DDD en las que trabajo, es el uso de interfaces que son idénticas a las entidades de dominio, con un mapeo de propiedades y funciones uno a uno. De hecho, un objeto de dominio siempre se usa a través de su interfaz uno a uno, y todas las entidades de dominio tienen una interfaz uno a uno en este estilo.
Por ejemplo:
Cuenta de objeto de dominio:
public class Account : IAccount
{
public string Name {get;set;}
//...some more fields that are also in IAccount
public decimal Balance {get;set;}
}
Y es la interfaz correspondiente
public interface IAccount
{
string Name {get;set;}
//... all the fields in Account
decimal Balance {get;set;}
}
Pero últimamente me he vuelto cada vez más convencido de que esto es, de hecho, un anti-patrón.
Lo ejecuté por algunos arquitectos en la comunidad de código abierto, y dicen que esto se basa en errores de diseño o fallas, en algún lugar de la cadena de diseño.
Así que les digo a mis colegas que deberían dejar de crear interfaces para los objetos de dominio. Porque no tiene ningún propósito para ellos, y usted debe actualizar la interfaz cada vez que actualice las entidades del dominio.
Primero se afirmó que estas interfaces proporcionan ''desacoplamiento'', pero yo respondo que debido a que las interfaces tienen una relación de uno a uno con las entidades de dominio que realmente no proporcionan ningún desacoplamiento, un cambio en la interfaz significa un cambio en La entidad de dominio y viceversa.
La siguiente afirmación es que necesitamos las interfaces para propósitos de prueba. Mi respuesta es que Rhino-mock proporciona la burla y el aplastamiento de clases concretas. Pero afirman que Rhino-Mock tiene problemas con las clases concretas. No sé si compro eso, incluso si Rhino-Mocks tiene problemas con clases concretas, eso no significa necesariamente que debamos usar interfaces para las entidades de dominio.
Así que tengo curiosidad:
¿Por qué tendría interfaces uno a uno para sus entidades de dominio?
Por qué no?
¿Por qué es una buena o mala práctica?
¡Gracias por leer!
EDITAR : Debo tener en cuenta que utilizo interfaces todo el tiempo, y creo que si es necesario, usaré una interfaz en un abrir y cerrar de ojos. Pero me refiero específicamente a las entidades de dominio con interfaces de uno a uno.
En general, si mis clases no van a ser parte de un patrón de diseño como Strategy o Visitor, no agrego interfaces.
Agregar interfaces es realmente útil para patrones de diseño como Estrategia y Visitante, pero en esos casos no copio a los captadores y definidores de las clases de dominio. En su lugar, creo interfaces que son específicas para las interfaces de patrón de diseño que creo.
interface SomeStrategy {
void doSomething(StrategyData data);
}
interface StrategyData {
String getProperty1();
String getProperty2();
}
Eso me permite dejar que las clases de dominio implementen esas interfaces, o usar el patrón del Adaptador. Me parece que este es un enfoque mucho más limpio que simplemente creando interfaces por el bien de él.
El diseño siempre debe reducir la incertidumbre. Crear interfaces por sí mismo no reduce la incertidumbre, de hecho, probablemente aumenta la confusión ya que no tiene ningún sentido.
Es una mala práctica como se describe, pero ...
No hay ninguna razón específica por la que sus interfaces deban ser diferentes a las entidades de su dominio; A veces realmente es el mapeo correcto. Pero es sospechoso que siempre sea así. El punto de preocupación allí es la cuestión de si las interfaces fueron realmente diseñadas o no, o si simplemente se colocaron en su lugar por falta de tiempo / pereza.
Para usar su ejemplo, la interfaz IAccount que describe expone a los captadores y definidores en el objeto Cuenta; parece un poco extraño e improbable que todo lo que use una Cuenta tenga la necesidad de establecer el saldo en la cuenta, y que ese permiso implícito esté especificado en ese nivel de interfaz. ¿No hay un lugar en su sistema en el que desee simplemente verificar pero no establecer el saldo de la Cuenta?
Estoy de acuerdo contigo. Las interfaces deben actuar como un contrato, por lo que no tiene ningún valor tener interfaces uno a uno con entidades de dominio. Puede ser útil si desea abstraer un determinado comportamiento. Pero, esto funcionará en ciertos casos.
La razón principal para especificar siempre los objetos de dominio como interfaces en lugar de directamente como clases es para darle un grado de libertad en la implementación. En su ejemplo, solo tiene un tipo de cuenta IA, por lo que es un poco redundante.
Pero que tal si tuvieras, por ejemplo:
public class Account : IAccount { ... } // Usual account, persistent
public class MockAccount : IAccount { ... } // Test mock object
public class TransAccount : IAccount { ... } // Account, not persistent
public class SimAccount : IAccount { ... } // Account in a performance sim
¿y así?
Al definir los objetos de dominio como interfaces, puede reemplazar las implementaciones sin alterar la definición de su dominio.
Las interfaces uno a uno en las entidades son un anti-patrón
James Gregory lo puso mejor que yo here .