Programación a interfaces orientadas a datos

9

Hay una parte de nuestra base de código escrita en el siguiente estilo:

// IScheduledTask.cs
public interface IScheduledTask
{
    string TaskName { get; set; }
    int TaskPriority { get; set; }
    List<IScheduledTask> Subtasks { get; set; }
    // ... several more properties in this vein
}

// ScheduledTaskImpl.cs
public class ScheduledTaskImpl : IScheduledTask
{
    public string TaskName { get; set; }
    public int TaskPriority { get; set; }
    public List<IScheduledTask> Subtasks { get; set; }
    // ... several more properties in this vein, 
    // perhaps a constructor or two for convenience.
}

Es decir, hay una gran cantidad de interfaces que especifican solo un conjunto de propiedades sin comportamiento, cada una con una única implementación correspondiente que las implementa con propiedades automáticas. El código está escrito por alguien bastante mayor (mucho más que yo) y está separado de este uso de interfaces de código de procedimiento razonable. Me preguntaba si alguien más había encontrado / usado este estilo y si tiene alguna ventaja sobre el uso de DTO concretos en todas partes sin las interfaces.

Walpen
fuente
1
Una razón por la que lo he hecho es por la compatibilidad con COM, pero esa no parece ser su razón aquí.
Mike
@ Mike Interesante, nunca he usado COM y no se usa aquí. Le preguntaré al autor de esta sección del código si estaba planeando usar el COM o algo así.
walpen
Supongo que espera hacer que al menos una de esas propiedades no sea automática en un futuro relativamente cercano, y escribir esta repetitiva al principio es un hábito en el que se ha metido para ahorrar algo de tiempo más tarde, aunque estoy No soy un chico C #, así que eso es todo lo que puedo decir.
Ixrec
66
En mi humilde opinión, es la obsesión excesiva y la aplicación incorrecta del código a las interfaces, no la implementación . Una revelación clave es la única implementación . El punto de las interfaces es el polimorfismo, pero una clase de solo propiedades es polimórfica en un sentido simplemente por tener valores diferentes. Incluso con el comportamiento - métodos - no tiene sentido dado una sola implementación. Estas tonterías están en toda nuestra base de código y, en el mejor de los casos, dificulta el mantenimiento. Pierdo demasiado tiempo preguntando por qué, qué y dónde. ¿Por qué lo hicieron, qué diseño no estoy viendo y dónde están las otras implementaciones?
radarbob
2
La mayoría de los comentarios anteriores se refieren a la implementación única del "olor a código" de la abstracción prematura; sin embargo, también hay un modelo de dominio anémico "olor a código" aquí, ver: martinfowler.com/bliki/AnemicDomainModel.html
Erik Eidt

Respuestas:

5

Aquí están mis dos centavos:

  • No estamos seguros de que un DTO nunca tenga al menos un poco de comportamiento. Entonces para mí hay objetos que pueden o no tener un comportamiento en el futuro.
  • Una posible causa de tener tantas clases de suela de implementación es la falta de diseño , por ejemplo, no ver que ScheduledTask, ScheduledJob, ScheduledEvent, ScheduledInspection, etc, debe ser sólo una segregados Schedulableinterfaz de hacer cualquier planificable implementador.
  • Crear las interfaces primero es una muy buena práctica, te da una idea de lo que necesitas. Muchas interfaces comienzan sin tener ninguna implementación en absoluto. Entonces alguien escribe una implementación y tienen esa. El estado de tener una implementación única es temporal si evita lo que menciono en el segundo punto. ¿Cómo sabe de antemano que alguna interfaz nunca tendrá una segunda o tercera implementación?
  • El nombre que elegimos para una interfaz bien pensada tiende a no cambiar en el futuro, por lo que las dependencias de esa interfaz no se verán afectadas. Por otro lado, es probable que se TaxSheetcambie el nombre de una clase concreta cuando algo importante en la forma en que se implementa cambia, por ejemplo, una clase concreta nombrada podría cambiar SessionAwareTaxSheetporque se realizó una revisión importante, pero la interfaz ITaxSheetprobablemente no se cambiará de nombre tan fácilmente.

Línea de fondo:

  • Las buenas prácticas de diseño se aplican también a los DTO.
  • A los DTO se les puede agregar un poco de comportamiento en el futuro.
  • Cada interfaz comienza teniendo solo una implementación.
  • Por otro lado, si tiene demasiados combos de una interfaz y una clase, podría haber una falta de diseño que debería señalarse. Su preocupación puede estar justificada .
Tulains Córdova
fuente
44
Voté su respuesta, pero su segunda viñeta implica que todas las clases deberían tener automáticamente una interfaz correspondiente, una posición con la que nunca he estado de acuerdo. Escribir interfaces en caso de que pueda tener una segunda implementación simplemente causa la proliferación de interfaces y YAGNI.
Robert Harvey
1

Un problema particular que he visto con los DTO que usan interfaces es que permite esto:

public interface ISomeDTO { string SomeProperty { get; set; }}

public class SomeDTO : ISomeDTO
{
    public string SomeProperty { get; set; }
    string ISomeDTO.SomeProperty { get { /* different behavior */ } set { SomeProperty = value; } }
}

He visto este patrón aplicado como un truco rápido y sucio para implementar algunos comportamientos críticos. Esto lleva a un código que puede tener un comportamiento muy confuso:

SomeDTO a = GetSomeDTO();
ISomeDTO ia = a;
Assert.IsTrue(a.SomeProperty == ia.SomeProperty); // assert fails!

Es difícil de mantener y confuso tratar de desenredar o refactorizar. En mi opinión, agregar comportamiento a los DTO viola el principio de responsabilidad única. El propósito de un DTO es representar los datos en un formato que puede ser persistido y poblado por un marco de persistencia.

Los DTO no son modelos de dominio. No deberíamos preocuparnos si los DTO son anémicos. Para citar la discusión de Martin Fowler sobre el modelo de dominio anémico :

También vale la pena enfatizar que poner el comportamiento en los objetos de dominio no debería contradecir el enfoque sólido de usar capas para separar la lógica de dominio de cosas tales como la persistencia y las responsabilidades de presentación.

Erik
fuente