Eu entendo os benefícios da injeção de dependência em si. Vamos pegar o Spring, por exemplo. Também entendo os benefícios de outros recursos do Spring, como AOP, auxiliares de diferentes tipos, etc. Estou apenas me perguntando quais são os benefícios da configuração XML, como:
<bean id="Mary" class="foo.bar.Female">
<property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
<property name="girlfriend" ref="Mary"/>
</bean>
em comparação com o código Java simples, como:
Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);
que é mais fácil de depurar, verificar o tempo de compilação e pode ser entendido por qualquer pessoa que conheça apenas java. Então, qual é o objetivo principal de uma estrutura de injeção de dependência? (ou um pedaço de código que mostra seus benefícios.)
ATUALIZAÇÃO:
Em caso de
IService myService;// ...
public void doSomething() {
myService.fetchData();
}
Como o framework IoC pode adivinhar qual implementação de myService eu desejo que seja injetada, se houver mais de uma? Se houver apenas uma implementação de determinada interface e eu permitir que o contêiner IoC decida automaticamente por usá-la, ela será quebrada após o aparecimento de uma segunda implementação. E se houver intencionalmente apenas uma implementação possível de uma interface, você não precisa injetá-la.
Seria realmente interessante ver um pequeno pedaço de configuração para IoC que mostra seus benefícios. Eu uso o Spring há algum tempo e não posso fornecer esse exemplo. E posso mostrar linhas únicas que demonstram os benefícios do hibernate, dwr e outros frameworks que eu uso.
ATUALIZAÇÃO 2:
Eu percebo que a configuração IoC pode ser alterada sem recompilar. É realmente uma boa ideia? Posso entender quando alguém deseja alterar as credenciais do banco de dados sem recompilar - ele pode não ser desenvolvedor. Em sua prática, com que frequência outra pessoa que não seja o desenvolvedor altera a configuração do IoC? Acho que para os desenvolvedores não há esforço para recompilar essa classe em particular em vez de alterar a configuração. E para quem não é desenvolvedor, você provavelmente gostaria de tornar sua vida mais fácil e fornecer algum arquivo de configuração mais simples.
ATUALIZAÇÃO 3:
Configuração externa de mapeamento entre interfaces e suas implementações concretas
O que há de tão bom em torná-lo extenso? Você não torna todo o seu código externo, embora definitivamente possa - apenas coloque-o no arquivo ClassName.java.txt, leia e compile manualmente em tempo real - uau, você evitou a recompilação. Por que a compilação deve ser evitada ?!
Você economiza tempo de codificação porque fornece mapeamentos declarativamente, não em um código procedural
Eu entendo que às vezes a abordagem declarativa economiza tempo. Por exemplo, declaro apenas uma vez que um mapeamento entre uma propriedade de bean e uma coluna DB e o hibernate usa esse mapeamento ao carregar, salvar, construir SQL baseado em HSQL, etc. É aqui que funciona a abordagem declarativa. No caso do Spring (no meu exemplo), a declaração tinha mais linhas e tinha a mesma expressividade do código correspondente. Se houver um exemplo em que tal declaração seja mais curta do que o código - eu gostaria de ver.
O princípio de inversão de controle permite fácil teste de unidade porque você pode substituir implementações reais por falsas (como substituir o banco de dados SQL por um na memória)
Eu entendo os benefícios da inversão de controle (prefiro chamar o padrão de design discutido aqui como Injeção de Dependência, porque IoC é mais geral - existem muitos tipos de controle, e estamos invertendo apenas um deles - controle de inicialização). Eu estava perguntando por que alguém precisa de algo diferente de uma linguagem de programação para isso. Definitivamente, posso substituir implementações reais por falsas usando código. E esse código expressará a mesma coisa que configuração - ele apenas inicializará campos com valores falsos.
mary = new FakeFemale();
Eu entendo os benefícios do DI. Não entendo quais benefícios são adicionados pela configuração XML externa em comparação com a configuração de código que faz o mesmo. Não acho que a compilação deva ser evitada - eu compilo todos os dias e ainda estou vivo. Acho que a configuração do DI é um mau exemplo de abordagem declarativa. A declaração pode ser útil se for declarada uma vez E é usada muitas vezes de maneiras diferentes - como hibernate cfg, onde o mapeamento entre a propriedade do bean e a coluna DB é usado para salvar, carregar, construir consultas de pesquisa, etc. A configuração do Spring DI pode ser facilmente traduzida para configurando código, como no começo dessa pergunta, não pode? E é usado apenas para inicialização do bean, não é? O que significa que uma abordagem declarativa não acrescenta nada aqui, certo?
Quando declaro o mapeamento do hibernate, apenas dou algumas informações ao hibernate e ele funciona com base nelas - não digo a ele o que fazer. No caso de spring, minha declaração diz a spring exatamente o que fazer - então por que declarar, por que não simplesmente fazer?
ÚLTIMA ATUALIZAÇÃO:
Pessoal, muitas respostas estão me falando sobre injeção de dependência, o que SEI QUE É BOM. A questão é sobre o propósito da configuração de DI em vez de inicializar o código - tendo a pensar que a inicialização do código é mais curta e clara. A única resposta que recebi até agora à minha pergunta, é que evita a recompilação, quando a configuração muda. Acho que devo postar outra pergunta, porque é um grande segredo para mim, por que compilar deve ser evitado neste caso.
fonte
Respostas:
Para mim, uma das principais razões para usar um IoC (e fazer uso de configuração externa) é em torno de duas áreas:
Testando
Se você dividir seu teste em 3 cenários (o que é bastante normal no desenvolvimento em grande escala):
O que você vai querer fazer é para os dois últimos cenários de teste (Integração e Caixa Preta), não recompilar nenhuma parte do aplicativo.
Se algum de seus cenários de teste exigir que você altere a configuração (ou seja: use outro componente para simular uma integração bancária ou fazer uma carga de desempenho), isso pode ser facilmente tratado (isso vem com os benefícios de configurar o lado DI de um IoC embora.
Além disso, se o seu aplicativo for usado em vários sites (com servidor e configuração de componente diferente) ou tiver uma configuração de alteração no ambiente ao vivo, você pode usar os estágios posteriores de teste para verificar se o aplicativo lidará com essas alterações.
Produção
Como desenvolvedor, você não (e não deve) ter controle do ambiente de produção (em particular quando seu aplicativo está sendo distribuído para vários clientes ou sites separados), este para mim é o benefício real de usar um IoC e uma configuração externa , já que cabe ao suporte de infraestrutura / produção ajustar e ajustar o ambiente de produção sem ter que voltar aos desenvolvedores e por meio de teste (custo mais alto quando tudo o que eles querem fazer é mover um componente).
Resumo
Os principais benefícios da configuração externa de um IoC vêm de dar a outros (não desenvolvedores) o poder de configurar seu aplicativo. Em minha experiência, isso só é útil em um conjunto limitado de circunstâncias:
Na prática, descobri que, mesmo ao desenvolver algo que você tem controle sobre o ambiente, ele será executado, com o tempo é melhor dar a outra pessoa os recursos para alterar a configuração:
Nota: Aplicativo refere-se à solução completa (não apenas o executável), portanto, todos os arquivos necessários para que o aplicativo seja executado .
fonte
A injeção de dependência é um estilo de codificação que tem suas raízes na observação de que a delegação de objeto é geralmente um padrão de design mais útil do que a herança de objeto (ou seja, o objeto tem-um relacionamento é mais útil do que o objeto é-um relacionamento). Um outro ingrediente é necessário, entretanto, para que o DI funcione, o de criar interfaces de objetos. Combinando esses dois poderosos padrões de projeto, os engenheiros de software perceberam rapidamente que podiam criar código flexível e fracamente acoplado e, assim, nasceu o conceito de injeção de dependência. No entanto, não foi até que a reflexão de objeto se tornou disponível em certas linguagens de alto nível que o DI realmente decolou. O componente de reflexão é fundamental para a maior parte de hoje '
Uma linguagem deve fornecer um bom suporte para técnicas normais de programação orientada a objetos, bem como suporte para interfaces de objetos e reflexão de objetos (por exemplo, Java e C #). Embora você possa construir programas usando padrões DI em sistemas C ++, sua falta de suporte a reflexão dentro da linguagem apropriada impede que ele ofereça suporte a servidores de aplicativos e outras plataformas DI e, portanto, limita a expressividade dos padrões DI.
Pontos fortes de um sistema construído usando padrões DI:
Definitivamente, o código DI parece mais complicado, as desvantagens de ter todos aqueles arquivos XML que configuram objetos a serem injetados em outros objetos parecem difíceis. Este é, no entanto, o ponto dos sistemas DI. Sua capacidade de misturar e combinar objetos de código como uma série de definições de configuração permite que você crie sistemas complexos usando código de terceiros com o mínimo de codificação de sua parte.
O exemplo fornecido na pergunta apenas toca na superfície do poder expressivo que uma biblioteca de objetos de DI devidamente fatorada pode fornecer. Com alguma prática e muita autodisciplina, a maioria dos praticantes de DI descobrem que podem construir sistemas que tenham 100% de cobertura de teste do código do aplicativo. Este único ponto é extraordinário. Isso não é 100% de cobertura de teste de um pequeno aplicativo de algumas centenas de linhas de código, mas 100% de cobertura de teste de aplicativos compreendendo centenas de milhares de linhas de código. Não consigo descrever qualquer outro padrão de design que forneça este nível de testabilidade.
Você está correto ao dizer que uma aplicação de meros 10s de linhas de código é mais fácil de entender do que vários objetos mais uma série de arquivos de configuração XML. No entanto, como acontece com os padrões de projeto mais poderosos, os ganhos são encontrados à medida que você continua a adicionar novos recursos ao sistema.
Resumindo, os aplicativos baseados em DI em grande escala são mais fáceis de depurar e de entender. Embora a configuração do Xml não seja 'verificada no tempo de compilação', todos os serviços de aplicativo dos quais este autor está ciente fornecerão mensagens de erro ao desenvolvedor se tentarem injetar um objeto com uma interface incompatível em outro objeto. E a maioria fornece um recurso de 'verificação' que cobre todas as configurações de objetos conhecidas. Isso é feito de forma fácil e rápida verificando se o objeto A a ser injetado implementa a interface exigida pelo objeto B para todas as injeções de objeto configuradas.
fonte
Esta é uma questão um pouco carregada, mas tendo a concordar que grandes quantidades de configuração xml não trazem muitos benefícios. Gosto que meus aplicativos sejam o mais leves possível em dependências, incluindo as estruturas pesadas.
Eles simplificam o código muitas vezes, mas também têm uma sobrecarga de complexidade que torna o rastreamento de problemas bastante difícil (eu vi esses problemas em primeira mão, e o Java direto seria muito mais confortável para mim).
Eu acho que depende um pouco do estilo e do que você se sente confortável ... você gosta de voar sua própria solução e tem a vantagem de conhecê-la de dentro para fora, ou aposta em soluções existentes que podem ser difíceis quando a configuração não é apenas certo? É tudo uma troca.
No entanto, a configuração XML é um pouco chata minha ... Tento evitá-la a todo custo.
fonte
Sempre que você pode alterar seu código para dados, está dando um passo na direção certa.
Codificar qualquer coisa como dados significa que seu próprio código é mais geral e reutilizável. Também significa que seus dados podem ser especificados em uma linguagem que se adapte exatamente a eles.
Além disso, um arquivo XML pode ser lido em uma GUI ou alguma outra ferramenta e facilmente manipulado de forma pragmática. Como você faria isso com o exemplo de código?
Estou constantemente fatorando coisas que a maioria das pessoas implementaria como código em dados, isso torna o código que resta MUITO mais limpo. Acho inconcebível que as pessoas criem um menu em código em vez de dados - deve ser óbvio que fazer isso em código é simplesmente errado por causa do clichê.
fonte
A razão para usar um contêiner de DI é que você não precisa ter um bilhão de propriedades pré-configuradas em seu código que são simplesmente getters e setters. Você realmente deseja codificar todos aqueles com o novo X ()? Claro, você pode ter um padrão, mas o contêiner DI permite a criação de singletons, o que é extremamente fácil e permite que você se concentre nos detalhes do código, não na tarefa diversa de inicializá-lo.
Por exemplo, Spring permite que você implemente a interface InitializingBean e adicione um método afterPropertiesSet (você também pode especificar um "método init" para evitar o acoplamento de seu código ao Spring). Esses métodos permitirão que você garanta que qualquer interface especificada como um campo em sua instância de classe seja configurada corretamente na inicialização, e então você não precisa mais verificar seus getters e setters (assumindo que você permite que seus singletons permaneçam seguros para thread )
Além disso, é muito mais fácil fazer inicializações complexas com um contêiner de DI em vez de fazer você mesmo. Por exemplo, eu ajudo a usar o XFire (não o CeltiXFire, usamos apenas Java 1.4). O aplicativo usava Spring, mas infelizmente usava o mecanismo de configuração services.xml do XFire. Quando uma coleção de elementos precisava declarar que tinha ZERO ou mais instâncias em vez de UMA ou mais instâncias, tive que substituir alguns dos códigos XFire fornecidos para este serviço específico.
Existem certos padrões XFire definidos em seu esquema Spring beans. Portanto, se estivéssemos usando Spring para configurar os serviços, os beans poderiam ter sido usados. Em vez disso, o que aconteceu foi que tive que fornecer uma instância de uma classe específica no arquivo services.xml em vez de usar os beans. Para fazer isso, precisei fornecer o construtor e definir as referências declaradas na configuração do XFire. A mudança real que eu precisava fazer exigia que eu sobrecarregasse uma única classe.
Mas, graças ao arquivo services.xml, tive que criar quatro novas classes, definindo seus padrões de acordo com seus padrões nos arquivos de configuração do Spring em seus construtores. Se tivéssemos sido capazes de usar a configuração Spring, eu poderia apenas ter declarado:
Em vez disso, parecia mais com isto:
Portanto, o resultado líquido é que quatro classes Java adicionais, em sua maioria sem sentido, tiveram que ser adicionadas à base de código para atingir o efeito que uma classe adicional e algumas informações de contêiner de dependência simples alcançaram. Esta não é a "exceção que prova a regra", esta É a regra ... lidar com peculiaridades no código é muito mais claro quando as propriedades já são fornecidas em um contêiner de DI e você simplesmente as altera para se adequar a uma situação especial, o que acontece com mais frequência do que não.
fonte
Eu tenho sua resposta
Obviamente, há compensações em cada abordagem, mas os arquivos de configuração XML externalizados são úteis para o desenvolvimento empresarial no qual os sistemas de construção são usados para compilar o código e não o seu IDE. Usando o sistema de construção, você pode querer injetar certos valores em seu código - por exemplo, a versão da construção (que pode ser doloroso ter que atualizar manualmente cada vez que você compila). A dor é maior quando seu sistema de compilação puxa o código de algum sistema de controle de versão. Modificar valores simples em tempo de compilação exigiria que você altere um arquivo, confirme, compile e, em seguida, reverta a cada vez para cada alteração. Estas não são mudanças que você deseja comprometer em seu controle de versão.
Outros casos de uso úteis sobre o sistema de compilação e configurações externas:
Atualização: Todos os exemplos acima foram sobre coisas que não necessariamente requerem dependências de classes. Mas você pode facilmente construir casos em que um objeto complexo e automação são necessários - por exemplo:
fonte
Você não precisa recompilar seu código cada vez que alterar algo na configuração. Isso simplificará a implantação e manutenção do programa. Por exemplo, você pode trocar um componente por outro com apenas 1 alteração no arquivo de configuração.
fonte
Você pode inserir uma nova implementação para namorada. Assim, uma nova fêmea pode ser injetada sem recompilar seu código.
(O acima pressupõe que Feminino e HotFemale implementam a mesma interface de namorada)
fonte
No mundo .NET, a maioria das estruturas IoC fornece configuração XML e de código.
StructureMap e Ninject, por exemplo, usam interfaces fluentes para configurar contêineres. Você não está mais limitado a usar arquivos de configuração XML. Spring, que também existe no .NET, depende muito de arquivos XML, pois é sua interface de configuração principal histórica, mas ainda é possível configurar contêineres de forma programática.
fonte
Facilidade de combinar configurações parciais em uma configuração final completa.
Por exemplo, em aplicativos da web, o modelo, a visualização e os controladores são normalmente especificados em arquivos de configuração separados. Use a abordagem declarativa, você pode carregar, por exemplo:
Ou carregue com uma IU diferente e alguns controladores extras:
Fazer o mesmo no código requer uma infraestrutura para combinar configurações parciais. Não é impossível fazer no código, mas certamente é mais fácil de fazer usando uma estrutura IoC.
fonte
Freqüentemente, o ponto importante é quem está mudando a configuração depois que o programa foi escrito. Com a configuração no código, você assume implicitamente que a pessoa que o altera possui as mesmas habilidades e acesso ao código-fonte, etc., que o autor original tinha.
Em sistemas de produção, é muito prático extrair algum subconjunto de configurações (por exemplo, idade em seu exemplo) para o arquivo XML e permitir, por exemplo, o administrador do sistema ou pessoal de suporte para alterar o valor sem dar a eles o poder total sobre o código-fonte ou outras configurações - ou apenas para isolá-los das complexidades.
fonte
Do ponto de vista da primavera, posso dar duas respostas.
Primeiro, a configuração XML não é a única maneira de definir a configuração. A maioria das coisas pode ser configurada usando anotações e as coisas que devem ser feitas com XML são a configuração do código que você não está escrevendo de qualquer maneira, como um pool de conexão que você está usando de uma biblioteca. Spring 3 inclui um método para definir a configuração de DI usando Java semelhante à configuração de DI enrolada à mão em seu exemplo. Portanto, usar Spring não significa que você precisa usar um arquivo de configuração baseado em XML.
Em segundo lugar, o Spring é muito mais do que apenas um framework de DI. Possui muitos outros recursos, incluindo gerenciamento de transações e AOP. A configuração Spring XML combina todos esses conceitos. Frequentemente, no mesmo arquivo de configuração, estou especificando dependências de bean, configurações de transação e adicionando beans com escopo de sessão que realmente manipulam usando AOP em segundo plano. Acho que a configuração XML oferece um lugar melhor para gerenciar todos esses recursos. Também sinto que a configuração baseada em anotação e a configuração XML aumentam melhor do que a configuração baseada em Java.
Mas eu vejo seu ponto e não há nada de errado em definir a configuração de injeção de dependência em Java. Normalmente faço isso em testes de unidade e quando estou trabalhando em um projeto pequeno o suficiente para não ter adicionado um framework DI. Normalmente não especifico a configuração em Java porque, para mim, esse é o tipo de código de encanamento que estou tentando evitar de escrever quando escolhi usar o Spring. No entanto, essa é uma preferência, não significa que a configuração XML seja superior à configuração baseada em Java.
fonte
Spring também possui um carregador de propriedades. Usamos este método para definir variáveis que dependem do ambiente (por exemplo, desenvolvimento, teste, aceitação, produção, ...). Pode ser, por exemplo, a fila para ouvir.
Se não houver motivo para a alteração da propriedade, também não há motivo para configurá-la desta forma.
fonte
Seu caso é muito simples e, portanto, não precisa de um container IoC (Inversion of Control) como o Spring. Por outro lado, quando você "programa para interfaces, não implementações" (o que é uma boa prática em OOP), você pode ter um código como este:
(observe que o tipo de myService é IService - uma interface, não uma implementação concreta). Agora pode ser útil deixar seu contêiner IoC fornecer automaticamente a instância concreta correta de IService durante a inicialização - quando você tem muitas interfaces e muitas implementações, pode ser complicado fazer isso manualmente. Os principais benefícios de um contêiner IoC (estrutura de injeção de dependência) são:
fonte
A inicialização em um arquivo de configuração XML simplificará seu trabalho de depuração / adaptação com um cliente que tenha seu aplicativo implantado em seus computadores. (Porque não requer recompilação + substituição de arquivos binários)
fonte
Um dos motivos mais atraentes é o " princípio de Hollywood ": não ligue para nós, nós ligaremos para você. Um componente não é necessário para fazer pesquisas em outros componentes e serviços; em vez disso, eles são fornecidos automaticamente. Em Java, isso significa que não é mais necessário fazer pesquisas JNDI dentro do componente.
Também é muito mais fácil testar a unidade de um componente isoladamente: em vez de dar a ele uma implementação real dos componentes de que ele precisa, você simplesmente usa simulações (possivelmente geradas automaticamente).
fonte