Estive pensando em como equilibrar o design testável usando injeção de dependência com o fornecimento de API pública fixa e simples. Meu dilema é: as pessoas gostariam de fazer algo assim var server = new Server(){ ... }
e não precisavam se preocupar em criar as muitas dependências e o gráfico das dependências que Server(,,,,,,)
podem ter. Durante o desenvolvimento, não me preocupo muito, pois uso uma estrutura de IoC / DI para lidar com tudo isso (não estou usando os aspectos de gerenciamento do ciclo de vida de nenhum contêiner, o que complicaria ainda mais as coisas).
Agora, é improvável que as dependências sejam reimplementadas. Neste caso, a componente é quase puramente testável (e com um design decente!), Em vez de criar costuras para extensão, etc. As pessoas 99.999% do tempo desejam usar uma configuração padrão. Então. Eu poderia codificar as dependências. Não quero fazer isso, perdemos nossos testes! Eu poderia fornecer um construtor padrão com dependências codificadas e uma que aceita dependências. Isso é ... bagunçado, e provavelmente confuso, mas viável. Eu poderia tornar a dependência do construtor interno e tornar minha unidade testa um assembly amigo (assumindo C #), que arruma a API pública, mas deixa uma armadilha oculta desagradável à espreita para manutenção. Ter dois construtores que estão implicitamente conectados e não explicitamente seria um projeto ruim em geral no meu livro.
No momento, esse é o mínimo mal que posso pensar. Opiniões? Sabedoria?
HttpListener
construtor mudar, aServer
classe também deverá mudar. Eles estão acoplados. Uma solução melhor é o que o @Winston Ewert diz abaixo, que é fornecer uma classe padrão com dependências pré-especificadas, fora da API da biblioteca. Então o programador não é obrigado a configurar todas as dependências desde que você toma a decisão por ele, mas ele ainda tem flexibilidade para alterá-las posteriormente. Isso é importante quando você tem dependências de terceiros.Em Java, nesses casos, é comum ter uma configuração "padrão" criada por uma fábrica, mantida independente do servidor "com comportamento" real. Então, seu usuário escreve:
enquanto o
Server
construtor ainda aceita todas as suas dependências e pode ser usado para uma customização profunda.fonte
Que tal fornecer uma subclasse que forneça a "API pública"
O usuário pode
new StandardServer()
e está a caminho. Eles também podem usar a classe base do servidor se quiserem ter mais controle sobre como o servidor funciona. Esta é mais ou menos a abordagem que eu uso. (Não uso uma estrutura porque ainda não entendi o ponto.)Isso ainda expõe a API interna, mas acho que você deveria. Você dividiu seus objetos em diferentes componentes úteis, que devem funcionar independentemente. Não há como saber quando um terceiro pode querer usar um dos componentes isoladamente.
fonte
Isso não parece uma "armadilha escondida desagradável" para mim. O construtor público deve apenas chamar o interno com as dependências "padrão". Desde que fique claro que o construtor público não deve ser modificado, tudo ficará bem.
fonte
Eu concordo totalmente com a sua opinião. Não queremos poluir o limite de uso de componentes apenas para fins de teste de unidade. Esse é todo o problema da solução de teste baseada em DI.
Gostaria de sugerir a usar InjectableFactory / InjectableInstance padrão. InjectableInstance são classes de utilidade reutilizáveis simples que são detentoras de valor mutável . A interface do componente contém uma referência a esse detentor de valor que foi inicializado com a implementação padrão. A interface fornecerá um método singleton get () que delega ao detentor do valor. O cliente do componente chamará o método get () em vez de new para obter uma instância de implementação para evitar dependência direta. Durante o tempo de teste, opcionalmente, podemos substituir a implementação padrão por uma simulação. A seguir, usarei um exemplo TimeProviderclasse que é uma abstração de Date () , permitindo que o teste de unidade injete e faça simulações para simular momentos diferentes. Desculpe, vou usar java aqui, pois estou mais familiarizado com java. C # deve ser semelhante.
O InjectableInstance é muito fácil de implementar, eu tenho uma implementação de referência em java. Para obter detalhes, consulte a publicação no meu blog Indirection Dependency with Injectable Factory
fonte