Supondo que você não esteja procurando uma estrutura de zombaria, porque é ultra-onipresente e fácil de encontrar , há algumas coisas que vale a pena observar com antecedência:
- "Nunca" existe algo que você deve "sempre" fazer.
Nem sempre é melhor agrupar uma biblioteca de terceiros. Se seu aplicativo é intrinsecamente dependente de uma biblioteca, ou se é literalmente construído em torno de uma ou duas bibliotecas principais, não perca seu tempo finalizando. Se as bibliotecas mudarem, seu aplicativo precisará mudar de qualquer maneira.
- Não há problema em usar testes de integração.
Isso é especialmente verdadeiro em relação aos limites que são estáveis, intrínsecos ao seu aplicativo ou que não podem ser facilmente ridicularizados. Se essas condições forem atendidas, o acondicionamento e a zombaria serão complicados e tediosos. Nesse caso, eu evitaria os dois: não envolva nem zombe; basta escrever testes de integração. (Se o teste automatizado é uma meta.)
- Ferramentas e estrutura não podem eliminar a complexidade lógica.
Em princípio, uma ferramenta só pode reduzir o padrão. Porém, não há algoritmo automatizado para pegar uma interface complexa e simplificá-la - muito menos pegar a interface X e adaptá-la às suas necessidades. (Somente você conhece esse algoritmo!) Portanto, embora haja indubitavelmente ferramentas que podem gerar invólucros finos, sugiro que elas ainda não sejam onipresentes porque, no final, você ainda precisa apenas codificar de forma inteligente e, portanto, manualmente, contra a interface, mesmo que oculta atrás de um invólucro.
Dito isto, existem táticas que você pode usar em vários idiomas para evitar se referir diretamente a uma classe. E, em alguns casos, você pode "fingir" uma interface ou um invólucro fino que não existe realmente. Em C #, por exemplo, eu usaria uma das duas rotas:
- Use uma fábrica e digitação implícita .
Você pode evitar o esforço de agrupar completamente uma classe complexa com este pequeno combo:
// "factory"
class PdfDocumentFactory {
public static ExternalPDFLibraryDocument Build() {
return new ExternalPDFLibraryDocument();
}
}
// code that uses the factory.
class CoreBusinessEntity {
public void DoImportantThings() {
var doc = PdfDocumentFactory.Build();
// ... i have no idea what your lib does, so, I'm making stuff but.
// but, you can do whatever you want here without explicitly
// referring to the library's actual types.
doc.addHeader("Wee");
doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
return doc.exportBinaryStreamOrSomething();
}
}
Se você puder evitar o armazenamento desses objetos como membros, por meio de uma abordagem mais "funcional" ou armazenando-os em um dicionário (ou qualquer outro ), essa abordagem terá o benefício de verificar o tipo em tempo de compilação sem que suas principais entidades de negócios precisem saber exatamente com que classe eles estão trabalhando.
Tudo o que é necessário é que, em tempo de compilação, a classe retornada por sua fábrica realmente possua os métodos que seu objeto de negócios está usando.
- Use digitação dinâmica .
Isso ocorre da mesma maneira que o uso de digitação implícita , mas envolve outra desvantagem: você perde verificações do tipo de compilação e ganha a capacidade de adicionar anonimamente dependências externas como membros da classe e injetar suas dependências.
class CoreBusinessEntity {
dynamic Doc;
public void InjectDoc(dynamic Doc) {
Doc = doc;
}
public void DoImortantThings() {
Doc.addHeader("Wee");
Doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
return Doc.exportBinaryStreamOrSomething();
}
}
Com essas duas táticas, quando chega a hora de zombar ExternalPDFLibraryDocument
, como afirmei anteriormente, você tem algum trabalho a fazer - mas é um trabalho que você precisaria fazer de qualquer maneira . E, com essa construção, você evitou definir tediosamente centenas de pequenas e finas classes de embalagens. Você simplesmente usou a biblioteca sem olhar diretamente para ela - na maior parte.
Com tudo isso dito, há três grandes razões pelas quais eu consideraria explicitamente encerrar uma biblioteca de terceiros - nenhuma das quais sugeriria o uso de uma ferramenta ou estrutura:
- A biblioteca específica não é intrínseca ao aplicativo.
- Seria muito caro trocar sem empacotá-lo.
- Eu não gosto da própria API.
Se eu não tiver alguma preocupação de nível nessas três áreas, você não fará nenhum esforço significativo para concluí-lo. E, se você tiver alguma preocupação nas três áreas, um invólucro fino gerado automaticamente não ajudará.
Se você decidiu encerrar uma biblioteca, o uso mais eficiente e eficaz do seu tempo é criar seu aplicativo na interface desejada ; não contra uma API existente.
Em outras palavras, preste atenção ao conselho clássico: adie todas as decisões que puder. Crie o "núcleo" do seu aplicativo primeiro. Codifique contra interfaces que eventualmente farão o que você deseja, que acabará sendo cumprido por "coisas periféricas" que ainda não existem. Preencha as lacunas conforme necessário.
Esse esforço pode não parecer economizado em tempo; mas se achar que precisa de um invólucro, essa é a maneira mais eficiente de fazê-lo com segurança.
Pense desta maneira.
Você precisa codificar nessa biblioteca em algum canto escuro do seu código - mesmo que esteja finalizado. Se você zomba da biblioteca durante o teste, há um esforço manual inevitável por lá - mesmo que seja finalizado. Mas isso não significa que você precise reconhecer diretamente essa biblioteca pelo nome em toda a maior parte do seu aplicativo.
TLDR
Se vale a pena agrupar a biblioteca, use táticas para evitar referências diretas e difundidas à sua biblioteca de terceiros, mas não use atalhos para gerar wrappers finos. Construa sua lógica de negócios primeiro, seja deliberado sobre suas interfaces e eleve seus adaptadores organicamente, conforme necessário.
E, se for o caso, não tenha medo de testes de integração. Eles são um pouco mais confusos, mas ainda oferecem evidências de código funcional e ainda podem ser feitos com facilidade para manter as regressões afastadas.