Para ser intercambiável e testável, normalmente os serviços com lógica precisam ter interface, por exemplo
public class FooService: IFooService
{ ... }
Em termos de design, concordo com isso, mas uma das coisas que me incomoda com essa abordagem é que, para um serviço, você precisará declarar duas coisas (a classe e a interface) e, em nossa equipe, normalmente dois arquivos (um para a classe e um para a interface). Outro desconforto é a dificuldade na navegação, pois o uso de "Ir para definição" no IDE (VS2010) apontará para a interface (já que outras classes se referem à interface), não à classe real.
Eu estava pensando que escrever o IFooService no mesmo arquivo que o FooService reduzirá a estranheza acima. Afinal, IFooService e FooService são muito relacionados. Esta é uma boa prática? Existe uma boa razão para que o IFooService precise estar localizado em seu próprio arquivo?
fonte
Respostas:
Ele não precisa estar em seu próprio arquivo, mas sua equipe deve decidir sobre um padrão e cumpri-lo.
Além disso, você está certo de que "Ir para a definição" leva você à interface, mas se você tiver o Resharper instalado, basta apenas um clique para abrir uma lista de classes / interfaces derivadas dessa interface, portanto não é grande coisa. É por isso que mantenho a interface em um arquivo separado.
fonte
Eu acho que você deve mantê-los arquivos separados. Como você disse, a idéia é permanecer testável e intercambiável. Ao colocar a interface no mesmo arquivo que sua implementação, você está associando a interface à implementação específica. Se você decidir criar um objeto simulado ou outra implementação, a interface não será logicamente separada do FooService.
fonte
De acordo com o SOLID, você não apenas deve criar a interface, mas também em um arquivo diferente, em um assembly diferente.
Por quê? Como qualquer alteração em um arquivo de origem que é compilado em um assembly requer recompilação do assembly, e qualquer alteração em um assembly requer recompilação de qualquer assembly dependente. Portanto, se seu objetivo, com base no SOLID, é poder substituir uma implementação A por uma implementação B, enquanto a classe C dependente da interface I não precisa saber a diferença, é necessário garantir que a montagem com I nele não muda, protegendo assim os usos.
"Mas é apenas uma recompilação" eu ouço você protestar. Bem, pode ser, mas no aplicativo para smartphone, que é mais fácil com a largura de banda de dados dos usuários; baixar um binário que mudou ou fazer o download desse binário e cinco outros com código que depende dele? Nem todo programa é escrito para ser consumido por computadores de mesa em uma LAN. Mesmo nesse caso, onde a largura de banda e a memória são baratas, os lançamentos menores de patches podem ter valor porque são triviais para serem enviados a toda a LAN através do Active Directory ou de camadas de gerenciamento de domínio semelhantes; seus usuários aguardarão apenas alguns segundos para que sejam aplicados na próxima vez que fizerem login, em vez de alguns minutos para que tudo seja reinstalado. Sem mencionar que, quanto menos montagens precisarem ser recompiladas ao criar um projeto, mais rápido ele será construído,
Agora, o aviso: nem sempre é possível ou possível fazer isso. A maneira mais fácil de fazer isso é criar um projeto "interfaces" centralizado. Isso tem suas próprias desvantagens; o código se torna menos reutilizável porque o projeto de interface E o projeto de implementação precisam ser referenciados em outros aplicativos que reutilizam a camada de persistência ou outros componentes principais do seu aplicativo. Você pode superar esse problema dividindo as interfaces em montagens mais firmemente acopladas, mas você tem mais projetos em seu aplicativo, o que torna muito difícil uma compilação completa. A chave é o equilíbrio e a manutenção do design fracamente acoplado; geralmente você pode mover os arquivos conforme necessário, portanto, quando perceber que uma classe precisará de muitas alterações ou que novas implementações de uma interface serão necessárias regularmente (talvez para fazer a interface com versões de outro software com suporte recente,
fonte
Por que ter arquivos separados é um desconforto? Para mim, é muito mais limpo e arrumado. É comum criar uma subpasta chamada "Interfaces" e colar seus arquivos IFooServer.cs lá, se você quiser ver menos arquivos no Gerenciador de Soluções.
A razão pela qual a interface é definida em seu próprio arquivo é pelo mesmo motivo que as classes geralmente são definidas em seu próprio arquivo: o gerenciamento de projetos é mais simples quando a estrutura lógica e a estrutura de arquivos são idênticas, para que você sempre saiba qual arquivo uma determinada classe está definida. in. Isso pode facilitar sua vida ao depurar (os rastreamentos de pilha de exceção geralmente fornecem números de arquivo e linha) ou ao mesclar o código-fonte em um repositório de controle de origem.
fonte
Geralmente, é uma boa prática permitir que seus arquivos de código contenham apenas uma única classe ou uma única interface. Mas essas práticas de codificação são um meio para atingir um fim - para estruturar melhor seu código, facilitando o trabalho. Se você e sua equipe acham mais fácil trabalhar com as classes mantidas juntas com suas interfaces, faça isso de qualquer maneira.
Pessoalmente, prefiro ter a interface e a classe no mesmo arquivo quando houver apenas uma classe que implemente a interface, como no seu caso.
Em relação aos problemas que você tem com a navegação, recomendo o ReSharper . Ele contém alguns atalhos altamente úteis para ir diretamente para o método que implementa um método de interface específico.
fonte
Há muitas vezes em que você deseja que sua interface não esteja apenas em um arquivo separado para a classe, mas mesmo em um assembly separado .
Por exemplo, uma interface de contrato de serviço do WCF pode ser compartilhada pelo cliente e pelo serviço se você tiver controle sobre as duas extremidades da ligação. Ao mover a interface para seu próprio assembly, ele terá menos dependências de assembly próprias. Isso facilita muito o consumo do cliente, diminuindo o acoplamento com sua implementação.
fonte
Raramente, se é que alguma vez, faz sentido ter uma única implementação de uma interface 1 . Se você colocar uma interface pública e uma classe pública implementando essa interface no mesmo arquivo, é provável que você não precise de uma interface.
Quando a classe que você co-localiza com a interface é abstrata e você sabe que todas as implementações de sua interface devem herdar essa classe abstrata, faz sentido localizar as duas no mesmo arquivo. Você ainda deve examinar sua decisão de usar uma interface: pergunte-se se uma classe abstrata por si só seria adequada e abandone a interface se a resposta for afirmativa.
Geralmente, porém, você deve seguir a estratégia "uma classe / interface pública corresponde a um arquivo": é fácil de seguir e facilita a navegação na árvore de fontes.
1 Uma exceção notável é quando você precisa ter uma interface para fins de teste, porque sua escolha de estrutura de simulação colocou esse requisito adicional em seu código.
fonte
sealed
. Se você suspeitar que em algum momento no futuro possa adicionar uma segunda subclasse, coloque uma interface imediatamente. PS Um assistente decente qualidade refatoração pode ser seu por um preço modesto de uma única licença ReSharper :)virtual
.As interfaces pertencem a seus clientes e não a suas implementações, conforme definido em Princípios, práticas e padrões ágeis de Robert C. Martin. Assim, combinar interface e implementação no mesmo local é contrário ao seu princípio.
O código do cliente depende da interface. Isso permite a possibilidade de compilar e implantar o código do cliente e a interface sem a implementação. Você pode ter diferentes implementações funcionando como plug - ins no código do cliente.
ATUALIZAÇÃO: Este não é um princípio somente ágil aqui. A Gangue dos Quatro em Design Patterns, em 94, já está falando de clientes aderindo a interfaces e programando para interfaces. A visão deles é semelhante.
fonte
Eu pessoalmente não gosto do "eu" na frente de uma interface. Como usuário desse tipo, não gostaria de ver que essa é uma interface ou não. O tipo é a coisa interessante. Em termos de dependências, seu FooService é UMA implementação possível do IFooService. A interface deve estar próxima do local onde é usada. Por outro lado, a implementação deve estar em um local onde possa ser facilmente alterada sem afetar o cliente. Então, eu preferiria dois arquivos - normalmente.
fonte
new
-lo, mas, por outro lado, posso usá-lo de forma covariável ou contra-variante. EI
é uma convenção de nomenclatura estabelecida no .Net, por isso é uma boa razão para segui-la, mesmo que seja consistente com outros tipos.I
na interface foi apenas a introdução aos meus argumentos sobre a separação em dois arquivos. O código é melhor legível e torna a inversão das dependências mais clara.I
é normal em seu ambiente: Use-o! (Mas eu não gosto :))A codificação para interfaces pode ser ruim, é de fato tratada como um antipadrão para certos contextos e não é uma lei. Normalmente, um bom exemplo de codificação para interfaces antipadrão é quando você possui uma interface que possui apenas uma implementação em seu aplicativo. Outra seria se o arquivo de interface se tornar tão incômodo que você precisará ocultar sua declaração no arquivo da classe implementada.
fonte
Colocar uma classe e uma interface no mesmo arquivo não impõe limites sobre como eles podem ser usados. Uma interface ainda pode ser usada para zombarias, etc, da mesma maneira, mesmo que esteja no mesmo arquivo que uma classe que a implementa. A questão pode ser entendida apenas como uma conveniência organizacional; nesse caso, eu diria que faça o que facilitar sua vida!
Certamente, alguém poderia argumentar que ter os dois no mesmo arquivo pode levar os incautos a considerá-los mais programaticamente acoplados do que realmente são, o que é um risco.
Pessoalmente, se eu encontrasse uma base de código que usasse uma convenção sobre a outra, eu não ficaria muito preocupado de qualquer maneira.
fonte