Se bem entendi, o mecanismo típico para injeção de dependência é injetar através do construtor de uma classe ou através de uma propriedade pública (membro) da classe.
Isso expõe a dependência que está sendo injetada e viola o princípio de encapsulamento OOP.
Estou correto na identificação dessa troca? Como você lida com esse problema?
Veja também a minha resposta à minha própria pergunta abaixo.
Respostas:
Há outra maneira de analisar esse problema que você pode achar interessante.
Quando usamos injeção de IoC / dependência, não estamos usando conceitos de POO. É certo que estamos usando uma linguagem OO como o 'host', mas as idéias por trás da IoC vêm da engenharia de software orientada a componentes, não da OO.
O software componente é sobre gerenciamento de dependências - um exemplo de uso comum é o mecanismo de montagem do .NET. Cada montagem publica a lista de montagens a que faz referência, e isso torna muito mais fácil reunir (e validar) as peças necessárias para um aplicativo em execução.
Ao aplicar técnicas semelhantes em nossos programas de OO via IoC, nosso objetivo é tornar os programas mais fáceis de configurar e manter. A publicação de dependências (como parâmetros do construtor ou o que for) é uma parte essencial disso. O encapsulamento não se aplica, pois no mundo orientado a componentes / serviços, não há um 'tipo de implementação' para o qual os detalhes vazem.
Infelizmente, atualmente, nossas linguagens não segregam os conceitos refinados e orientados a objetos dos conceitos orientados a componentes de granularidade mais grossa; portanto, essa é uma distinção que você deve ter apenas em mente :)
fonte
É uma boa pergunta - mas, em algum momento, o encapsulamento em sua forma mais pura precisa ser violado para que o objeto tenha sua dependência cumprida. Algum provedor da dependência deve saber que o objeto em questão requer um
Foo
e que o provedor precisa ter uma maneira de fornecer oFoo
o objeto.Classicamente, este último caso é tratado como você diz, por meio de argumentos construtores ou métodos setter. No entanto, isso não é necessariamente verdade - eu sei que as versões mais recentes do framework Spring DI em Java, por exemplo, permitem anotar campos particulares (por exemplo, com
@Autowired
) e a dependência será definida por meio de reflexão sem a necessidade de expor a dependência por meio de qualquer uma das classes métodos / construtores públicos. Esse pode ser o tipo de solução que você estava procurando.Dito isto, também não acho que a injeção de construtores seja um problema. Eu sempre achei que os objetos deveriam ser totalmente válidos após a construção, de forma que tudo o que eles precisassem para desempenhar sua função (ou seja, estar em um estado válido) deveria ser fornecido pelo construtor de qualquer maneira. Se você tem um objeto que exige que um colaborador funcione, parece-me bom que o construtor anuncie publicamente esse requisito e garanta que ele seja cumprido quando uma nova instância da classe for criada.
Idealmente, ao lidar com objetos, você interage com eles por meio de uma interface, e quanto mais você faz isso (e tem dependências conectadas por meio do DI), menos você realmente precisa lidar com os construtores. Na situação ideal, seu código não lida com nem cria instâncias concretas de classes; então ele recebe um
IFoo
DI direto, sem se preocupar com o que o construtor deFooImpl
indica que precisa fazer seu trabalho e, de fato, sem nem mesmo estar ciente daFooImpl
existência do mesmo. Deste ponto de vista, o encapsulamento é perfeito.Essa é uma opinião, é claro, mas, na minha opinião, o DI não viola necessariamente o encapsulamento e, de fato, pode ajudá-lo, centralizando todo o conhecimento necessário dos internos em um só lugar. Isso não é apenas uma coisa boa por si só, mas ainda melhor este lugar está fora da sua própria base de código, portanto, nenhum código que você escreve precisa saber sobre as dependências das classes.
fonte
Bem, francamente falando, tudo viola o encapsulamento. :) É um tipo de princípio sensível que deve ser bem tratado.
Então, o que viola o encapsulamento?
Herança faz .
Programação orientada a aspectos sim . Por exemplo, você registra o retorno de chamada onMethodCall () e isso oferece uma ótima oportunidade de injetar código na avaliação normal do método, adicionando efeitos colaterais estranhos, etc.
Declaração de amigo em C ++ faz .
A extensão de classe no Ruby faz . Apenas redefina um método de string em algum lugar depois que uma classe de string foi totalmente definida.
Bem, muitas coisas fazem .
O encapsulamento é um princípio bom e importante. Mas não é o único.
fonte
Sim, o DI viola o encapsulamento (também conhecido como "ocultação de informações").
Mas o verdadeiro problema surge quando os desenvolvedores o usam como desculpa para violar os princípios do KISS (Mantenha-o Curto e Simples) e do YAGNI (Você não precisa disso).
Pessoalmente, prefiro soluções simples e eficazes. Eu uso principalmente o operador "novo" para instanciar dependências com estado, quando e onde elas forem necessárias. É simples, bem encapsulado, fácil de entender e fácil de testar. Então por que não?
fonte
Um bom recipiente / sistema de injeção de depenância permitirá a injeção do construtor. Os objetos dependentes serão encapsulados e não precisam ser expostos publicamente. Além disso, usando um sistema DP, nenhum dos seus códigos "conhece" os detalhes de como o objeto é construído, possivelmente incluindo o objeto que está sendo construído. Há mais encapsulamento nesse caso, já que quase todo o seu código não é apenas protegido do conhecimento dos objetos encapsulados, mas nem participa da construção dos objetos.
Agora, suponho que você esteja comparando com o caso em que o objeto criado cria seus próprios objetos encapsulados, provavelmente em seu construtor. Meu entendimento sobre DP é que queremos tirar essa responsabilidade do objeto e entregá-la a outra pessoa. Para esse fim, o "alguém mais", que é o contêiner DP neste caso, possui um conhecimento íntimo que "viola" o encapsulamento; o benefício é que ele próprio extrai esse conhecimento do objeto. Alguém tem que ter. O restante do seu aplicativo não.
Eu pensaria dessa maneira: O contêiner / sistema de injeção de dependência viola o encapsulamento, mas seu código não. De fato, seu código está mais "encapsulado" do que nunca.
fonte
Como Jeff Sternal apontou em um comentário para a pergunta, a resposta depende inteiramente de como você define o encapsulamento .
Parece haver dois campos principais do que significa encapsulamento:
File
objeto pode ter métodos paraSave
,Print
,Display
,ModifyText
, etc.Essas duas definições estão em contradição direta entre si. Se um
File
objeto puder ser impresso sozinho, isso dependerá muito do comportamento da impressora. Por outro lado, se ele apenas sabe sobre algo que pode ser impresso para ele (umaIFilePrinter
ou alguma interface), oFile
objeto não precisa saber nada sobre impressão e, portanto, trabalhar com ele trará menos dependências para o objeto.Portanto, a injeção de dependência interromperá o encapsulamento se você usar a primeira definição. Mas, francamente, não sei se gosto da primeira definição - ela claramente não aumenta (se o fizesse, o MS Word seria uma classe grande).
Por outro lado, a injeção de dependência é quase obrigatória se você estiver usando a segunda definição de encapsulamento.
fonte
Não viola o encapsulamento. Você está fornecendo um colaborador, mas a turma decide como é usada. Contanto que você siga Tell, não pergunte que as coisas estão bem. Acho a injeção de construtor preferível, mas os setters podem ser bons, desde que sejam inteligentes. Ou seja, eles contêm lógica para manter os invariantes que a classe representa.
fonte
Isso é semelhante à resposta votada, mas quero pensar em voz alta - talvez outros vejam as coisas dessa maneira também.
OO clássico usa construtores para definir o contrato de "inicialização" público para os consumidores da classe (ocultando TODOS os detalhes da implementação; também conhecido como encapsulamento). Este contrato pode garantir que, após a instanciação, você tenha um objeto pronto para uso (ou seja, nenhuma etapa adicional de inicialização seja lembrada (esquecida) pelo usuário).
(construtor) O DI inegavelmente quebra o encapsulamento sangrando os detalhes da implementação por meio dessa interface pública do construtor. Enquanto ainda considerarmos o construtor público responsável por definir o contrato de inicialização para os usuários, criamos uma violação horrível do encapsulamento.
Exemplo teórico:
A classe Foo possui 4 métodos e precisa de um número inteiro para a inicialização, portanto, seu construtor se parece com Foo (tamanho int) e fica imediatamente claro para os usuários da classe Foo que eles devem fornecer um tamanho na instanciação para que o Foo funcione.
Digamos que essa implementação específica do Foo também precise de um IWidget para fazer seu trabalho. A injeção de construtores dessa dependência nos faria criar um construtor como Foo (tamanho int, widget IWidget)
O que me irrita é que agora temos um construtor que mistura dados de inicialização com dependências - uma entrada é do interesse do usuário da classe ( tamanho ), a outra é uma dependência interna que serve apenas para confundir o usuário e é uma implementação detalhe ( widget ).
O parâmetro size NÃO é uma dependência - é simples um valor de inicialização por instância. A IoC é excelente para dependências externas (como widget), mas não para inicialização interna do estado.
Pior ainda, e se o Widget for necessário apenas para 2 dos 4 métodos nesta classe; Talvez esteja ocorrendo uma sobrecarga de instanciação no Widget, mesmo que ele não possa ser usado!
Como comprometer / conciliar isso?
Uma abordagem é mudar exclusivamente para interfaces para definir o contrato de operação; e abolir o uso de construtores pelos usuários. Para ser consistente, todos os objetos precisariam ser acessados apenas através de interfaces e instanciados apenas através de alguma forma de resolvedor (como um contêiner IOC / DI). Somente o contêiner consegue instanciar as coisas.
Isso cuida da dependência do Widget, mas como inicializamos o "tamanho" sem recorrer a um método de inicialização separado na interface Foo? Usando esta solução, perdemos a capacidade de garantir que uma instância do Foo seja totalmente inicializada no momento em que você obtém a instância. Que chatice, porque eu realmente gosto da idéia e da simplicidade da injeção de construtores.
Como obtenho a inicialização garantida neste mundo de DI, quando a inicialização é MAIS do que APENAS dependências externas?
fonte
O encapsulamento puro é um ideal que nunca pode ser alcançado. Se todas as dependências estivessem ocultas, você não precisaria de DI. Pense dessa maneira: se você realmente possui valores privados que podem ser internalizados no objeto, digamos, por exemplo, o valor inteiro da velocidade de um objeto carro, então você não tem dependência externa e não precisa inverter ou injetar essa dependência. Esses tipos de valores de estado interno que são operados exclusivamente por funções privadas são o que você deseja encapsular sempre.
Mas se você estiver construindo um carro que deseja um certo tipo de objeto de motor, terá uma dependência externa. Você pode instanciar esse mecanismo - por exemplo, o novo GMOverHeadCamEngine () - internamente no construtor do objeto car, preservando o encapsulamento, mas criando um acoplamento muito mais insidioso a uma classe concreta GMOverHeadCamEngine, ou você pode injetá-lo, permitindo que o objeto Car funcione de forma independente (e muito mais robusta) em, por exemplo, uma interface IEngine sem a dependência concreta. Não importa se você usa um contêiner do COI ou um DI simples para atingir esse objetivo - o ponto é que você tem um carro que pode usar muitos tipos de motores sem ser acoplado a nenhum deles, tornando sua base de código mais flexível e flexível. menos propenso a efeitos colaterais.
A DI não é uma violação do encapsulamento, é uma maneira de minimizar o acoplamento quando o encapsulamento é necessariamente interrompido, como é óbvio em praticamente todos os projetos de OOP. A injeção de uma dependência em uma interface externamente minimiza os efeitos colaterais do acoplamento e permite que suas classes permaneçam independentes da implementação.
fonte
Depende se a dependência é realmente um detalhe de implementação ou algo que o cliente deseja / precisa conhecer de uma maneira ou de outra. Uma coisa que é relevante é qual nível de abstração a classe está direcionada. aqui estão alguns exemplos:
Se você possui um método que usa o cache sob o capô para acelerar as chamadas, o objeto de cache deve ser um Singleton ou algo assim e não deve ser injetado. O fato de o cache estar sendo usado é um detalhe de implementação que os clientes da sua classe não precisam se preocupar.
Se sua classe precisar gerar fluxos de dados, provavelmente faz sentido injetar o fluxo de saída para que a classe possa produzir facilmente os resultados para uma matriz, um arquivo ou para qualquer outro lugar que outra pessoa possa enviar os dados.
Para uma área cinzenta, digamos que você tenha uma classe que faz algumas simulações de monte carlo. Precisa de uma fonte de aleatoriedade. Por um lado, o fato de precisar disso é um detalhe de implementação, pois o cliente realmente não se importa exatamente de onde vem a aleatoriedade. Por outro lado, como os geradores de números aleatórios do mundo real fazem trocas entre grau de aleatoriedade, velocidade etc. que o cliente pode querer controlar e o cliente pode querer controlar a propagação para obter um comportamento repetitivo, a injeção pode fazer sentido. Nesse caso, sugiro oferecer uma maneira de criar a classe sem especificar um gerador de números aleatórios e usar um Singleton local de encadeamento como padrão. Se / quando surgir a necessidade de um controle mais preciso, forneça outro construtor que permita a injeção de uma fonte aleatória.
fonte
Eu acredito na simplicidade. A aplicação de Injeção de IOC / Dependecy nas classes Domain não melhora, exceto tornando o código muito mais difícil de manter, por meio de arquivos xml externos que descrevem a relação. Muitas tecnologias, como o EJB 1.0 / 2.0 e o struts 1.1, estão revertendo, reduzindo as coisas colocadas no XML e tentando colocá-las no código como uma anotação, etc.
O IOC possui benefícios quando o objeto dependente não está pronto para criação em tempo de compilação. Isso pode acontecer na maioria dos componentes da arquitetura de nível abstrato de infra-estrutura, tentando estabelecer uma estrutura de base comum que pode precisar funcionar para diferentes cenários. Nesses lugares, o uso do COI faz mais sentido. Ainda assim, isso não torna o código mais simples / sustentável.
Como todas as outras tecnologias, isso também possui PROs e CONs. Minha preocupação é que implementemos as mais recentes tecnologias em todos os lugares, independentemente do melhor uso do contexto.
fonte
O encapsulamento é quebrado apenas se uma classe tiver a responsabilidade de criar o objeto (que requer conhecimento de detalhes de implementação) e depois usar a classe (que não requer conhecimento desses detalhes). Vou explicar o porquê, mas primeiro uma rápida anaologia do carro:
Mas voltando à codificação. Encapsulamento está "ocultando um detalhe de implementação de algo usando essa implementação". O encapsulamento é uma coisa boa porque os detalhes da implementação podem mudar sem que o usuário da classe saiba.
Ao usar injeção de dependência, a injeção de construtor é usada para construir objetos de tipo de serviço (em oposição a objetos de entidade / valor que modelam o estado). Quaisquer variáveis de membro no objeto de tipo de serviço representam detalhes de implementação que não devem vazar. por exemplo, número da porta do soquete, credenciais do banco de dados, outra classe a ser chamada para executar criptografia, cache, etc.
O construtor é relevante quando a classe está sendo criada inicialmente. Isso acontece durante a fase de construção enquanto o contêiner de DI (ou fábrica) conecta todos os objetos de serviço. O contêiner de DI conhece apenas detalhes de implementação. Ele sabe tudo sobre os detalhes da implementação, como os da fábrica Kombi conhecem sobre as velas de ignição.
No tempo de execução, o objeto de serviço criado é chamado apon para realizar algum trabalho real. No momento, o chamador do objeto não sabe nada sobre os detalhes da implementação.
Agora, de volta ao encapsulamento. Se os detalhes da implementação forem alterados, a classe que usa essa implementação em tempo de execução não precisará ser alterada. O encapsulamento não está quebrado.
Se os detalhes da implementação mudarem, o contêiner de DI (ou fábrica) precisa ser alterado. Você nunca estava tentando ocultar os detalhes de implementação da fábrica em primeiro lugar.
fonte
Tendo lutado um pouco mais com o assunto, estou agora na opinião de que a Injeção de Dependência (neste momento) viola o encapsulamento até certo ponto. Não me interpretem mal - acho que o uso de injeção de dependência vale a pena na maioria dos casos.
O motivo pelo qual a DI viola o encapsulamento fica claro quando o componente em que você está trabalhando deve ser entregue a uma parte "externa" (pense em escrever uma biblioteca para um cliente).
Quando meu componente exige que os subcomponentes sejam injetados por meio do construtor (ou propriedades públicas), não há garantia para
Ao mesmo tempo, não se pode dizer que
Ambas as citações são de Wikipedia .
Para dar um exemplo específico: preciso fornecer uma DLL do lado do cliente que simplifique e oculte a comunicação com um serviço WCF (essencialmente uma fachada remota). Como depende de três classes de proxy WCF diferentes, se eu adotar a abordagem de DI, sou forçado a expô-las por meio do construtor. Com isso, exponho as partes internas da minha camada de comunicação que estou tentando ocultar.
Geralmente sou a favor do DI. Neste exemplo (extremo) em particular, isso me parece perigoso.
fonte
O DI viola o encapsulamento para objetos não compartilhados - ponto final. Objetos compartilhados têm uma vida útil fora do objeto que está sendo criado e, portanto, devem ser AGREGADOS no objeto que está sendo criado. Objetos particulares ao objeto que está sendo criado devem ser COMPOSIDOS no objeto criado - quando o objeto criado é destruído, ele leva o objeto composto. Vamos tomar o corpo humano como exemplo. O que é composto e o que é agregado. Se usássemos DI, o construtor do corpo humano teria centenas de objetos. Muitos dos órgãos, por exemplo, são (potencialmente) substituíveis. Mas, eles ainda são compostos no corpo. As células sanguíneas são criadas no corpo (e destruídas) todos os dias, sem a necessidade de influências externas (exceto proteínas). Assim, as células sanguíneas são criadas internamente pelo corpo - o novo BloodCell ().
Os advogados da DI argumentam que um objeto NUNCA deve usar o novo operador. Essa abordagem "purista" não só viola o encapsulamento, mas também o Princípio de Substituição de Liskov para quem está criando o objeto.
fonte
Eu lutei com essa noção também. A princípio, o 'requisito' de usar o contêiner de DI (como o Spring) para instanciar um objeto parecia saltar através de aros. Mas, na realidade, não é realmente um aro - é apenas outra maneira 'publicada' de criar objetos de que preciso. Certamente, o encapsulamento está "quebrado" porque alguém "fora da classe" sabe do que precisa, mas na verdade não é o resto do sistema que sabe disso - é o contêiner DI. Nada de mágico acontece de maneira diferente porque o DI 'sabe' que um objeto precisa de outro.
De fato, fica ainda melhor - concentrando-me em Fábricas e Repositórios, nem preciso saber que o DI está envolvido! Isso para mim coloca a tampa de volta no encapsulamento. Uau!
fonte
PS. Ao fornecer injeção de dependência, você não necessariamente quebra o encapsulamento . Exemplo:
O código do cliente ainda não conhece os detalhes da implementação.
fonte
Talvez essa seja uma maneira ingênua de pensar sobre isso, mas qual é a diferença entre um construtor que aceita um parâmetro inteiro e um construtor que aceita um serviço como parâmetro? Isso significa que definir um número inteiro fora do novo objeto e alimentá-lo no objeto quebra o encapsulamento? Se o serviço for usado apenas dentro do novo objeto, não vejo como isso quebraria o encapsulamento.
Além disso, ao usar algum tipo de recurso de fiação automática (Autofac para C #, por exemplo), ele torna o código extremamente limpo. Ao criar métodos de extensão para o construtor Autofac, consegui recortar MUITO código de configuração de DI que eu teria que manter ao longo do tempo à medida que a lista de dependências aumentava.
fonte
Eu acho que é evidente que pelo menos o DI enfraquece significativamente o encapsulamento. Além disso, aqui estão algumas outras desvantagens da DI a serem consideradas.
Isso torna o código mais difícil de reutilizar. Um módulo que um cliente pode usar sem precisar fornecer explicitamente dependências é obviamente mais fácil de usar do que aquele em que o cliente precisa descobrir de alguma forma quais são as dependências desse componente e depois disponibilizá-las. Por exemplo, um componente criado originalmente para ser usado em um aplicativo ASP pode esperar que suas dependências sejam fornecidas por um contêiner de DI que forneça instâncias de objeto com vida útil relacionada a solicitações HTTP do cliente. Talvez não seja fácil reproduzir em outro cliente que não seja fornecido com o mesmo contêiner DI incorporado que o aplicativo ASP original.
Isso pode tornar o código mais frágil. As dependências fornecidas pela especificação da interface podem ser implementadas de maneiras inesperadas, o que gera uma classe inteira de erros de tempo de execução que não são possíveis com uma dependência concreta resolvida estaticamente.
Isso pode tornar o código menos flexível, no sentido de que você pode ter menos opções sobre como deseja que ele funcione. Nem toda classe precisa ter todas as suas dependências em existência durante toda a vida útil da instância proprietária, mas com muitas implementações de DI, você não tem outra opção.
Com isso em mente, acho que a pergunta mais importante se torna: "será que uma dependência específica precisa ser especificada externamente? ". Na prática, raramente achei necessário fazer uma dependência fornecida externamente apenas para dar suporte aos testes.
Onde uma dependência realmente precisa ser fornecida externamente, isso normalmente sugere que a relação entre os objetos é uma colaboração e não uma dependência interna; nesse caso, o objetivo apropriado é o encapsulamento de cada classe, em vez do encapsulamento de uma classe dentro da outra. .
Na minha experiência, o principal problema com relação ao uso de DI é que, se você inicia com uma estrutura de aplicativo com DI integrada ou adiciona suporte a DI à sua base de código, por algum motivo, as pessoas assumem que, como você tem suporte a DI, esse deve ser o correto maneira de instanciar tudo . Eles nem sequer se incomodam em fazer a pergunta "essa dependência precisa ser especificada externamente?". E pior, eles também começam a tentar forçar todo mundo a usar o suporte de DI para tudo também.
O resultado disso é que inexoravelmente sua base de código começa a se transformar em um estado em que a criação de qualquer instância de qualquer coisa em sua base de código requer resmas de configuração obtusa de contêiner de DI e a depuração de algo é duas vezes mais difícil, porque você tem a carga de trabalho extra de tentar identificar como e onde tudo foi instanciado.
Então, minha resposta para a pergunta é esta. Use o DI onde você pode identificar um problema real que ele resolve para você, que você não pode resolver de maneira mais simples.
fonte
Concordo que, ao extremo, o DI pode violar o encapsulamento. Normalmente, o DI expõe dependências que nunca foram verdadeiramente encapsuladas. Aqui está um exemplo simplificado emprestado dos Singletons de Miško Hevery : Mentirosos Patológicos :
Você começa com um teste de cartão de crédito e escreve um teste de unidade simples.
No próximo mês, você recebe uma fatura de US $ 100. Por que você foi cobrado? O teste de unidade afetou um banco de dados de produção. Internamente, o CreditCard faz chamadas
Database.getInstance()
. A refatoração do CreditCard para que seja necessário umDatabaseInterface
em seu construtor expõe o fato de que há dependência. Mas eu argumentaria que a dependência nunca foi encapsulada, pois a classe CreditCard causa efeitos colaterais visíveis externamente. Se você quiser testar o CreditCard sem refatorar, certamente poderá observar a dependência.Não acho que valha a pena se a exposição do banco de dados como uma dependência reduz o encapsulamento, porque é um bom design. Nem todas as decisões de DI serão tão diretas. No entanto, nenhuma das outras respostas mostra um exemplo contrário.
fonte
CreditCard
objeto pode mudar de uma WebAPI para PayPal, sem que nada externo à classe precise ser alterado?Eu acho que é uma questão de escopo. Ao definir o encapsulamento (sem informar como), você deve definir qual é a funcionalidade encapsulada.
Classificar como está : o que você está encapsulando é a única responsabilidade da classe. O que sabe fazer. Por exemplo, classificando. Se você injeta algum comparador para pedidos, digamos, clientes, isso não faz parte do encapsulado: quicksort.
Funcionalidade configurada : se você deseja fornecer uma funcionalidade pronta para uso, não está fornecendo a classe QuickSort, mas uma instância da classe QuickSort configurada com um Comparador. Nesse caso, o código responsável pela criação e configuração que deve estar oculto do código do usuário. E esse é o encapsulamento.
Quando você está programando classes, implementando responsabilidades únicas em classes, está usando a opção 1.
Quando você está programando aplicativos, é algo que faz algum trabalho concreto útil, e você usa repetidamente a opção 2.
Esta é a implementação da instância configurada:
É assim que outro código de cliente o usa:
Ele é encapsulado porque, se você alterar a implementação (alterar a
clientSorter
definição do bean), não interrompe o uso do cliente. Talvez, ao usar arquivos xml com todos escritos juntos, você esteja vendo todos os detalhes. Mas acredite, o código do cliente (ClientService
) não sabe nada sobre seu classificador.fonte
Provavelmente vale a pena mencionar que
Encapsulation
é um pouco dependente da perspectiva.Da perspectiva de alguém que trabalha na
A
classe, no segundo exemplo,A
sabe muito menos sobre a natureza dathis.b
Considerando que sem DI
vs
A pessoa que olha esse código sabe mais sobre a natureza do
A
segundo exemplo.Com o DI, pelo menos todo esse conhecimento vazado está em um só lugar.
fonte