Programação para interfaces orientadas a dados

9

Há uma parte da nossa base de código escrita no seguinte 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.
}

Ou seja, há um grande número de interfaces especificando apenas um conjunto de propriedades sem comportamento, cada uma com uma única implementação correspondente que as implementa com propriedades automáticas. O código é escrito por alguém razoavelmente sênior (muito mais do que eu) e além desse uso de código processual razoável de interfaces. Fiquei me perguntando se alguém havia encontrado / usado esse estilo e se ele tem alguma vantagem sobre o uso de DTOs concretos em todos os lugares, sem as interfaces.

Walpen
fonte
11
Um dos motivos que fiz foi a compatibilidade com o COM, mas esse não parece ser o seu motivo aqui.
Mike apoia Monica
@ Mike Interessante, eu nunca usei COM e não é usado aqui. Vou perguntar ao autor desta seção do código se ele estava planejando usar o COM ou algo assim.
walpen
Meu palpite seria que ele espera tornar pelo menos uma dessas propriedades não automáticas em um futuro relativamente próximo, e escrever esse boilerplate no início é um hábito em que ele se envolveu para economizar algum tempo brincando mais tarde, embora eu esteja não é um cara de C #, então é tudo o que posso dizer.
Ixrec 7/01/16
6
IMHO é o excesso de obsessão e aplicação incorreta de código para interfaces e não implementação . Um indicador importante é a única implementação . O objetivo das interfaces é o polimorfismo, mas uma classe somente de propriedades é polimórfica em certo sentido, simplesmente por ter valores diferentes. Mesmo com o comportamento - métodos - não há sentido em fornecer uma única implementação. Esse absurdo está em toda a nossa base de código e, na melhor das hipóteses, dificulta a manutenção. Perco tempo demais perguntando por que, o que e onde. Por que eles fizeram isso, qual design eu não estou vendo e onde estão as outras implementações?
Radarbob
2
A maioria dos comentários anteriores se refere ao "cheiro de código" de implementação única da abstração prematura; no entanto, há também um modelo de domínio "código de cheiro" anêmica aqui, veja: martinfowler.com/bliki/AnemicDomainModel.html
Erik Eidt

Respostas:

5

Aqui estão meus dois centavos:

  • Não temos certeza de que um DTO nunca tenha pelo menos um pouco de comportamento. Então, para mim, existem objetos que podem ou não ter comportamento no futuro.
  • Uma causa possível ter tantas aulas único de implementação é uma falta de projeto , por exemplo, deixar de ver que ScheduledTask, ScheduledJob, ScheduledEvent, ScheduledInspection, etc, devem ser apenas um segregados Schedulableinterface de fazer qualquer schedulable implementor.
  • Criar as interfaces primeiro é uma prática muito boa, pois fornece informações sobre o que você precisa. Muitas interfaces começam por não ter nenhuma implementação. Então, alguém escreve uma implementação e eles têm essa. O estado de ter uma única implementação é temporário se você evitar o que mencionei no segundo ponto. Como você sabe de antemão que alguma interface nunca terá um segundo da terceira implementação?
  • O nome que escolhemos para uma interface bem pensada tem uma tendência a não mudar no futuro, portanto, as dependências dessa interface não serão afetadas. Por outro lado, uma classe concreta é propensa a ser renomeada quando algo importante na maneira como é implementada muda, por exemplo, uma classe concreta chamada TaxSheetpoderia mudar para SessionAwareTaxSheetporque uma revisão significativa foi feita, mas a interface ITaxSheetprovavelmente não será renomeada com tanta facilidade.

Conclusão:

  • Boas práticas de design também se aplicam aos DTOs.
  • Os DTOs podem ser adicionados um pouco de comportamento no caminho.
  • Toda interface começa com apenas uma implementação.
  • Por outro lado, se você tiver muitos combos de uma interface e uma classe, pode haver uma falta de design que deve ser destacada. Sua preocupação pode ser justificada .
Tulains Córdova
fonte
4
Votou sua resposta, mas seu segundo marcador implica que todas as classes devem ter automaticamente uma interface correspondente, uma posição com a qual eu nunca concordei. A criação de interfaces com a possibilidade de você ter uma segunda implementação apenas causa a proliferação da interface e o YAGNI.
Robert Harvey
1

Um problema específico que eu já vi com DTOs que usam interfaces é que isso permite:

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; } }
}

Vi esse padrão aplicado como um hack rápido e sujo para implementar algum comportamento crítico. Isso leva ao código que pode ter um comportamento muito confuso:

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

Isso é difícil de manter e confuso para tentar desembaraçar ou refatorar. Na IMO, a adição de comportamento aos DTOs viola o princípio da responsabilidade única. O objetivo de um DTO é representar dados em um formato que possa ser persistido e preenchido por uma estrutura de persistência.

DTOs não são modelos de domínio. Não devemos nos preocupar se os DTOs são anêmicos. Para citar a discussão de Martin Fowler sobre o modelo de domínio anêmico :

Também vale a pena enfatizar que colocar o comportamento nos objetos do domínio não deve contradizer a abordagem sólida do uso de camadas para separar a lógica do domínio de coisas como responsabilidades de persistência e apresentação.

Erik
fonte