Devo escrever uma API de interface antes de uma implementação?

14

Recentemente, venho investigando uma programação mais "organizada" e aprendendo que devo programar para uma interface, não para uma implementação. Com isso em mente, seria melhor "esboçar" um projeto nas interfaces antes de escrever a implementação sempre que possível?

E se esse for o caso, no caso de usar bibliotecas de terceiros (por exemplo, Lidgren), eu também devo envolvê-las nas interfaces e resolvê-las através de contêineres IOC, ou é correto expô-las às interfaces?

Dan Pantry
fonte
Na minha experiência pessoal - é bom projetar a arquitetura primeiro - a responsabilidade de cada classe. Você não precisa anotá-la, apenas pensar ou esboçar em um papel. Então, trata-se de preferência pessoal, mas eu recomendo escrever comentários de documentos primeiro para cada método que você começar a implementar. Escrever documentos realmente faz você pensar sobre a funcionalidade antes de começar a escrever código.
Sulthan
Sim e programe para as interfaces (ou classes abstratas) antes de implementá-las. Isso ajuda a obter o fluxo de mensagens do cliente para o servidor e vice-versa "certo" antes de ser atrapalhado (e investido em) implementações. Muito boa apresentação de slides sobre o assunto: Como Projetar um bom API e porque importa
Marjan Venema

Respostas:

8

Infelizmente, você verá que isso geralmente se resume a preferências pessoais.

O que você descreveu até agora, no entanto, parece bom. De fato, se você quiser (e eu o recomendo), use a seguinte abordagem:

  1. Escreva o esqueleto do aplicativo como Interfaces, classes abstratas (stubbed) e classes (também stubbed)
  2. Escreva seus testes nessas interfaces e stubs (eles falharão por enquanto)
  3. Escreva suas implementações (seus testes começarão a passar quando você terminar sua implementação)

Você está tentando escrever um código mais "organizado". Seguir o TDD irá ajudá-lo com isso.

Alguns pontos extras:

  • Os recipientes de IoC são convenientes. Use-os e DI, tanto quanto você puder.
  • Não enrole bibliotecas 3o partido. Isso afrouxará o acoplamento entre o seu código (código que você controla) e o código de terceiros (código que você não controla)
MetaFight
fonte
1
Era o que eu pensava originalmente, mas me disseram que isso violaria o princípio YAGNI. O problema que encontro em muitos dos meus projetos que nunca são concluídos é que eles rapidamente se tornam insustentáveis ​​com a quantidade de código de blob que eu escrevi, porque não o organizo adequadamente nem planejo meu plano de ataque.
Dan Pantry
qual parte violaria YAGNI?
MetaFight
Agrupando bibliotecas de terceiros.
Dan Pantry
2
Eu acho que tudo se resume a: Quais são as chances da biblioteca de terceiros mudar? Se houver uma chance de 0% disso, então YAGNI. Mas isso raramente é o caso. Além disso, envolvendo as suas bibliotecas 3o partido pode fazer o seu outro código mais fácil de teste de unidade (se, por exemplo, você não pode zombar a biblioteca parte 3)
MetaFight
1
@ DanPantry: Encapsular bibliotecas de terceiros não é uma violação da YAGNI, mas uma proteção muito necessária contra "infestação de bibliotecas de terceiros de seu próprio código". Não se trata apenas de poder trocar uma biblioteca, mas como o MetaFight também diz uma defesa contra alterações nas versões mais recentes da biblioteca, que de outra forma exigiriam alterações em seu próprio código. Ao agrupar a biblioteca (e especialmente seus tipos específicos: classes, enumerações, estruturas, etc.), você isola seu próprio código e tem um único ponto para alterar quando a biblioteca é alterada (por qualquer motivo).
Marjan Venema
13

Sim, você deve codificar contra interfaces, em vez de implementações conhecidas, e sim, você deve construir interfaces primeiro, em vez de fazê-las emergir de seu próprio código.

As razões para ambas as recomendações são basicamente as mesmas: a programação de computadores trata principalmente de fatores humanos. Muitos acham isso surpreendente, mas consideram: existe um número quase infinito de maneiras diferentes de resolver o mesmo problema de computação que funciona igualmente bem. Quase todos eles são completamente impossíveis de entender para quem não os escreveu (ou de fato para o autor pouco tempo depois).

Daqui resulta que uma boa engenharia de software trata principalmente de como alcançar o efeito desejado (computação correta com eficiência razoável) de uma maneira que permita que o código fonte seja trabalhado posteriormente. Interfaces e APIs são uma parte crucial dessa disciplina: permitem que você pense sobre um problema em um nível de descrição por vez. Isso é muito mais fácil do que pensar em regras de consistência de negócios e em implementações de listas vinculadas ao mesmo tempo, e, portanto, impor essa separação de preocupações à força é melhor do que permitir que o programador cliente use seu código da maneira que desejar.

É difícil de acreditar para muitos programadores de cowboys, convencidos de que entendem tudo o que escrevem, são muito melhores do que os pensadores comuns e podem lidar com toda a complexidade que causa problemas aos programadores "menores". Não estar ciente dos próprios limites cognitivos é um fenômeno extremamente comum - é por isso que as melhores práticas em organização de códigos são tão importantes (e muitas vezes ignoradas).

Para repetir, interfaces e barreiras de API são bastante boas , mesmo quando você coopera apenas consigo mesmo. Quanto às bibliotecas externas, se elas trouxerem uma API bem pensada, não vejo problema em usá-la, desde que você não espere ter que trocar essa biblioteca por outra. Caso contrário, um invólucro ou camada anticorrupção pode ser uma ótima ideia.

Kilian Foth
fonte
Eu gosto do seu ponto de vista sobre o SE ser em grande parte alcançar o efeito desejado de uma maneira que permita que o código fonte seja trabalhado posteriormente. Eu gostaria de ter conseguido expressá-lo tão bem no meu último emprego, onde eu estava sempre lutando por código limpo!
MetaFight
Existe uma convenção de nomenclatura para APIs que são apenas interfaces que eu acabarei usando em todo o lugar? Por exemplo, se estou executando um padrão de comando, chamo-o de "comandável"?
Snoop
@StevieV Existem vários, por exemplo, IBlahimplementados por Blahou Blahimplementados por BlahImpl. Não gosto de ambos, e costumo usar Blahimplementado por OralBlah, WrittenBlahou ASLBlah. Mas, como sempre, é mais importante estar em conformidade com a base de código e as expectativas existentes do que com qualquer padrão geral.
precisa saber é o seguinte
4

Em vez de apenas programar servilmente para interfaces, por que não olhar para TDD (Development Driven Development / Design)?

Muitas pessoas pensam no TDD como uma prática de teste, mas, na verdade, é uma abordagem de design em que você permite que os testes exponham como o seu código será usado por meio de testes (inicialmente por testes de unidade, mas também por testes de integração).

A programação para interfaces é uma arma importante no seu conjunto de ferramentas, mas, como a maioria das coisas, nem sempre é a solução / técnica / prática apropriada, pois nem sempre é necessária. Você deve programar para interfaces onde precisar.

O uso do TDD forçará você a explorar onde essas interfaces são importantes e onde, francamente, não importa. E no final, você deve ter um bom conjunto de testes de unidade em toda a sua base de código.

Quanto ao uso de bibliotecas de terceiros, eu recomendo agrupá-las em suas próprias abstrações, quando apropriado; e não deixe que os clientes da sua API "saibam" sobre eles.

Boa sorte!

[editar: viu a resposta do megaflight - concordo completamente]

rupjones
fonte
2
TDD implicitamente você pensa em termos de interface em vez de implementação, embora possa não ser uma declaração formal de "interface".
DougM
1
Esta é uma ótima resposta. +1 por sugerir TDD, que eu acho que é a solução para o verdadeiro problema do OP de onde começar quando estiver trabalhando em um novo projeto, e +1 novamente se pudesse por "Usar TDD forçará você a explorar onde essas interfaces são importantes e onde, francamente, não importa ".
Benjamin Hodgson
2

Eu acho que é um exagero. Se o usuário da sua API não precisar ser forçado a implementar / usar algo de uma certa maneira, eu o deixaria de fora. Interfaces são contratos, se eu não preciso, por que me dar um?

Eu acho que as pessoas usam demais interfaces. Você está adicionando uma camada de complexidade que não é necessária na maioria dos casos.

Kyle Johnson
fonte
Eu acho que, as pessoas sob as interfaces -Use. Se você deseja criar peças reutilizáveis, a interface não é apenas uma adição "agradável de ter", mas a principal coisa a se cuidar. Além da implementação real, é claro.
perfil completo de JensG
1

Programar contra um contrato é quase sempre uma boa ideia. Esse contrato não precisa ser uma interface, mas pode ser cumprido por uma classe. Na minha opinião, as interfaces tornaram-se um pouco usadas demais com o DI devido a preocupações com testes de unidade e estruturas de simulação.

Pessoalmente, prefiro trazer apenas interfaces quando é provável que eu tenha ou tenha mais de uma implementação de contrato. As interfaces são ótimas para repositórios nos quais desejo abstrair o acesso a dados, mas provavelmente menos ainda para minha lógica comercial padrão, que provavelmente é relativamente inflexível.

Agora, não ter uma interface pode causar problemas com o teste de unidade, especialmente para puristas. Mas estou interessado em zombar das dependências externas dos meus programas, não de suas dependências internas. Quero que meus testes realizem a validação do código, não ecoem a estrutura do código.

Peter Smith
fonte