Injeção de dependência: devo usar uma estrutura?


24

Recentemente, trabalhei em um projeto Python em que realizamos injeção de dependência intensamente (porque precisamos que o aplicativo seja testável), mas não usamos nenhuma estrutura. Às vezes, era um pouco entediante conectar todas as dependências manualmente, mas no geral funcionava muito bem.

Quando um objeto precisava ser criado em vários locais, simplesmente tínhamos uma função (às vezes um método de classe desse objeto) para criar uma instância de produção. Essa função era chamada sempre que precisávamos desse objeto.

Nos testes, fizemos o mesmo: se várias vezes precisássemos criar uma 'versão de teste' de um objeto (ou seja, uma instância real suportada por objetos simulados), teríamos a função de criar essa 'versão de teste' de uma classe, para ser usado em testes.

Como eu disse, geralmente isso funcionou bem.

Agora estou entrando em um novo projeto Java e estamos decidindo se deve usar uma estrutura de DI ou DI manualmente (como no projeto anterior).

Por favor, explique como o trabalho com uma estrutura DI será diferente e de que maneira é melhor ou pior que a injeção manual de dependência 'vanilla'.

Edit: Eu também gostaria de acrescentar à pergunta: você testemunhou algum projeto de 'nível profissional' que faz DI manualmente, sem uma estrutura?


Ao lado da minha resposta. Ambas as formas são compatíveis. Você pode deixar a injeção de dependência suave na estrutura e manter suas fábricas para componentes críticos. Componentes relacionados ao modelo ou negócio.
Laiv

4
Eu acho que você já conferiu, apenas no caso de não precisar. Por que precisamos de estruturas para DI?
Laiv 15/05

Respostas:


19

A chave dos contêineres DI é a abstração . Os contêineres DI abstraem essa preocupação de design para você. Como você pode imaginar, ele tem um impacto notável no código, muitas vezes traduzido em um número menor de construtores, montadores, fábricas e construtores. Em conseqüência, eles tornam seu código mais frouxamente acoplado (devido à IoC subjacente ), mais limpo e mais simples. Embora não sem um custo.

Planos de fundo

Anos atrás, essa abstração nos exigia escrever vários arquivos de configuração ( programação declarativa ). Foi fantástico ver o número de LOCs diminuindo substancialmente, mas enquanto os LOCSs diminuíram, o número de arquivos de configuração aumentou ligeiramente. Para projetos médios ou pequenos, não era um problema, mas para projetos maiores, a "montagem do código" dispersa em 50% entre os descritores de código e XML tornou-se uma dor de cabeça ... No entanto, o principal problema foi o paradigma em si. Foi bastante inflexível porque os descritores deixaram pouco espaço para personalizações.

O paradigma mudou progressivamente para a convenção sobre configuração , que troca os arquivos de configuração por anotações ou atributos. Ele mantém nosso código mais limpo e simples, fornecendo a flexibilidade que o XML não podia.

Provavelmente, essa é a diferença mais significativa em relação à forma como você estava trabalhando até agora. Menos código e mais mágica.

No lado negativo...

A convenção sobre configuração torna a abstração tão pesada que você mal sabe como ela funciona. Às vezes parece mágica e aqui vem mais uma desvantagem. Uma vez que está funcionando, muitos desenvolvedores não se preocupam com o funcionamento.

Esta é uma desvantagem para quem aprendeu DI com contêineres DI. Eles não entendem completamente a relevância dos padrões de design, as boas práticas e os princípios abstraídos pelo contêiner. Perguntei aos desenvolvedores se eles estavam familiarizados com o DI e suas vantagens e eles responderam: - Sim, eu usei o Spring IoC -. (O que diabos isso significa?!?)

Pode-se concordar ou não com cada uma dessas "desvantagens". Na minha humilde opinião, é necessário saber o que está acontecendo no seu projeto. Caso contrário, não temos um entendimento completo disso. Também é saber para que serve o DI e ter uma noção de como implementá-lo, é uma vantagem a considerar.

Do lado positivo...

Produtividade de uma palavra . As estruturas nos concentramos no que realmente importa. Os negócios do aplicativo.

Apesar das deficiências comentadas, para muitos de nós (cujo trabalho é condicionado por prazos e custos), essas ferramentas são um recurso inestimável. Na minha opinião, esse deve ser nosso principal argumento em favor da implementação de um contêiner de DI. Produtividade .

Teste

Se implementarmos DI puro ou contêineres, o teste não será um problema. Muito pelo contrário. Os mesmos contêineres geralmente nos fornecem as simulações para testes de unidade prontos para uso. Ao longo dos anos, usei meus testes como termômetros. Eles me dizem se eu confiei muito nas instalações de contêineres. Digo isso porque esses contêineres podem inicializar atributos privados sem setters ou construtores. Eles podem injetar componentes quase em qualquer lugar!

Parece tentador, certo? Seja cuidadoso! Não caia na armadilha !!

Sugestões

Se você decidir implementar contêineres, sugiro enfaticamente seguir as boas práticas. Continue implementando construtores, setters e interfaces. Aplique a estrutura / contêiner para usá-los . Facilitará possíveis migrações para outra estrutura ou remover a atual. Pode reduzir significativamente a dependência do contêiner.

Em relação a essas práticas:

Quando usar contêineres DI

Minha resposta será influenciada pela própria experiência, que é principalmente a favor dos contêineres DI (como eu comentei, estou muito focada na produtividade, então nunca tive a necessidade de DI puro. Muito pelo contrário).

Acho que você encontrará um artigo interessante de Mark Seeman sobre esse assunto, que também responde à pergunta sobre como implementar o DI puro .

Finalmente, se estivéssemos falando de Java, consideraria primeiro dar uma olhada no JSR330 - Injeção de Dependência .

Resumindo

Benefícios :

  • Redução de custos e economia de tempo. Produtividade.
  • Um número menor de componentes.
  • O código se torna mais simples e limpo.

Desvantagens :

  • O DI é delegado a bibliotecas de terceiros. Devemos estar cientes de seus compromissos, pontos fortes e fracos.
  • Curva de aprendizado.
  • Eles rapidamente fazem você esquecer alguns bons hábitos.
  • Aumento das dependências do projeto (mais libs)
  • Recipientes baseados em reflexão requerem mais recursos (memória). Isso é importante se estivermos limitados pelos recursos.

Diferenças com DI puro :

  • Menos código e mais arquivos de configuração ou anotações.

11
“Essas estruturas não lhe são privativas para teste de unidade ou de integridade, portanto, não se preocupe com o teste.” O que exatamente isso significa? A redação me faz pensar que é um erro de digitação, mas estou tendo problemas para decodificá-lo.
Candied_orange 15/05

Isso significa que a depuração difícil ou o uso das estruturas para teste não são desvantagens suficientes para serem descartadas para usar essas estruturas. Porque mesmo que você não goste, eles ainda têm benefícios que realmente valem a pena tentar. As grandes desvantagens não são estruturas por si mesmas. É como você os usa. Algumas boas práticas, como interfaces e setters, ainda são necessárias, mesmo que sua estrutura não. Finalmente há boas libs para testes que integra Easly com estes tipo de Frameworks (DI)
LAIV

Se é isso que você quer dizer considerar: "Estes quadros não impedi-lo de realizar testes de unidade ou testes de integridade, então não se preocupe sobre como eles impacto testar"
candied_orange

Srry. Eu tento dizer que ele será capaz de testar seu código mesmo com essas estruturas. Apesar da abstração, o teste ainda é factível. Sinta-se livre para editar minha resposta. Como você vê o meu Inglês ainda está longe de ser decente :-)
LAIV

11
Observe que o Dagger2 faz DI ligeiramente diferente. O código de cola é gerado no tempo de compilação como fontes Java legíveis e normais, por isso é muito mais simples seguir como as coisas são coladas, em vez de precisar lidar com a mágica do tempo de execução.
Thorbjørn Ravn Andersen

10

Na minha experiência, uma estrutura de injeção de dependência leva a vários problemas. Alguns dos problemas dependerão da estrutura e das ferramentas. Pode haver práticas que atenuam os problemas. Mas esses são os problemas que atingi.

Objetos não globais

Em alguns casos, você deseja injetar um objeto global: um objeto que terá apenas uma instância no aplicativo em execução. Isso pode ser um MarkdownToHtmlRendererer, um DatabaseConnectionPool, o endereço para o seu servidor de API, etc. Para estes casos, os quadros de injeção de dependência trabalhar de maneira muito simples, basta adicionar a dependência necessários para o seu construtor e você tem isso.

Mas e os objetos que não são globais? E se você precisar do usuário para a solicitação atual? E se você precisar da transação de banco de dados ativa no momento? E se você precisar do elemento HTML correspondente à div que contém um formulário em que é uma caixa de texto que acabou de disparar um evento?

A solução que eu vi empregada pelas estruturas de DI são os injetores hierárquicos. Mas isso torna o modelo de injeção mais complicado. Torna-se mais difícil descobrir exatamente o que será injetado em qual camada da hierarquia. Torna-se difícil saber se um determinado objeto existirá por aplicativo, por usuário, por solicitação, etc. Você não pode mais apenas adicionar suas dependências e fazê-lo funcionar.

Bibliotecas

Muitas vezes, você deseja ter uma biblioteca de códigos reutilizáveis. Isso também inclui as instruções de como construir objetos a partir desse código. Se você usar uma estrutura de injeção de dependência, isso normalmente incluirá regras de ligação. Se você quiser usar a biblioteca, adicione as regras de ligação delas às suas.

No entanto, vários problemas podem resultar. Você pode acabar com a mesma classe vinculada a diferentes implementações. A biblioteca pode esperar que certas ligações existam. A biblioteca pode substituir algumas ligações padrão, fazendo com que seu código faça algo estranho. Seu código pode substituir alguma ligação padrão, fazendo com que o código da biblioteca faça coisas estranhas.

Complexidade oculta

Ao escrever fábricas manualmente, você tem uma noção de como as dependências do aplicativo se encaixam. Quando você usa uma estrutura de injeção de dependência, não vê isso. Em vez disso, os objetos tendem a aumentar cada vez mais dependências até que mais cedo ou mais tarde tudo dependa de praticamente tudo.

Suporte IDE

Seu IDE provavelmente não entenderá a estrutura de injeção de dependência. Com fábricas manuais, você pode encontrar todos os chamadores de um construtor para descobrir todos os locais em que o objeto pode ser construído. Mas não encontrará as chamadas geradas pela estrutura DI.

Conclusão

O que obtemos de uma estrutura de injeção de dependência? Evitamos alguns clichês simples necessários para instanciar objetos. Sou a favor de eliminar clichês simplórios. No entanto, é fácil manter uma clichê simplificada. Se vamos substituir esse padrão, devemos insistir em substituí-lo por algo mais fácil. Devido às várias questões que discuti acima, não acho que as estruturas (pelo menos como estão) se encaixem no projeto.


Esta é uma resposta muito boa, mas não consigo imaginar usar um contêiner de IoC para uma biblioteca de classes. Isso parece um projeto ruim para mim. Eu esperaria que uma biblioteca me desse explicitamente todas as dependências necessárias.
precisa

@RubberDuck, no meu caso, trabalhei para uma grande empresa com um grande número de projetos java, todos padronizados na mesma estrutura de injeção de dependência. Nesse ponto, torna-se tentador para algumas equipes exportar uma biblioteca que espera ser montada pela estrutura de DI, em vez de fornecer uma interface externa sensata.
Winston Ewert

Isso é lamentável @WinstonEwert. Obrigado por preencher o contexto para mim.
RubberDuck 17/05

Seria ótimo para a parte IDE, com a estrutura de ligação das injeções em tempo de execução, em oposição à ligação do tipo de tempo de design das injeções manuais. O compilador evita injeções esquecidas quando manuais.
Basilevs

O IntelliJ entende CDI (padrão Java EE DI)
Thorbjørn Ravn Andersen

3

A injeção de dependência Java + = estrutura é necessária?

Não.

O que uma estrutura DI me compra?

Construção de objeto acontecendo em uma linguagem que não é Java.


Se os métodos de fábrica são bons o suficiente para suas necessidades, você pode executar DI em java completamente sem estrutura, em 100% autêntico pojo java. Se suas necessidades vão além do que você tem outras opções.

Os padrões de design da Gangue dos Quatro realizam muitas coisas excelentes, mas sempre tiveram fraquezas no que diz respeito aos padrões criacionais. O próprio Java tem algumas fraquezas aqui. As estruturas de DI realmente ajudam mais com essa fraqueza criacional do que com as injeções reais. Você já sabe como fazer isso sem eles. O quanto de bom eles farão depende de quanta ajuda você precisa na construção do objeto.

Existem muitos padrões de criação que surgiram após a Gangue dos Quatro. O construtor Josh Bloch é um truque maravilhoso sobre a falta de parâmetros nomeados por java. Além disso, há construtores iDSL que oferecem muita flexibilidade. Mas cada um deles representa um trabalho significativo e padronizado.

As estruturas podem salvá-lo de parte desse tédio. Eles não são isentos de desvantagens. Se você não for cuidadoso, poderá se sentir dependente da estrutura. Tente alternar estruturas depois de usar uma e veja por si mesmo. Mesmo que você mantenha o xml ou as anotações genéricos de alguma maneira, poderá acabar apoiando o que são essencialmente duas linguagens que você deve mencionar lado a lado ao anunciar trabalhos de programação.

Antes de se apaixonar demais pelo poder das estruturas de DI, dedique algum tempo e investigue outras estruturas que oferecem recursos para os quais você poderia pensar que precisa de uma estrutura de DI. Muitos se ramificaram além do simples DI . Considere: Lombok , AspectJ e slf4J . Cada um se sobrepõe a algumas estruturas de DI, mas cada um funciona bem por conta própria.

Se o teste for realmente a força motriz por trás disso, verifique: jUnit (qualquer xUnit realmente) e Mockito (qualquer mock realmente) antes de começar a pensar que uma estrutura de DI deve dominar sua vida.

Você pode fazer o que quiser, mas, para um trabalho sério, prefiro uma caixa de ferramentas bem preenchida a um canivete suíço. Gostaria que fosse mais fácil traçar essas linhas, mas alguns se esforçaram muito para desfocá-las. Nada de errado com isso, mas pode tornar essas escolhas desafiadoras.


O PowerMock resolveu alguns desses casos. Pelo menos ele deixar você mock up estática
LAIV

Meh. Se você está realizando DI corretamente, deve ser relativamente simples trocar uma estrutura de IoC Container por outra. Mantenha essas anotações fora do seu código e você ficará bem. Basicamente, use o contêiner de IoC para realizar DI, em vez de usá-lo como localizador de serviço.
precisa

@RubberDuck correto. Com falta de teste e manutenção constante de sua base de códigos em várias estruturas de DI, você conhece uma maneira simples e confiável de verificar se "você está fazendo DI corretamente"? Mesmo que eu não use nada além de pojo em java, se houver um esforço significativo na criação do XML, ainda não será uma troca simples quando o XML for dependente do contêiner de IoC.
Candied_orange 15/05

@CandiedOrange Estou usando "simples" como um termo relativo. No grande esquema das coisas, substituir uma raiz de composição por outra não é uma tarefa excessivamente difícil. Alguns dias funcionam no máximo. Quanto a garantir que você esteja realizando o DI corretamente, é para isso que servem as Revisões de Código.
precisa

2
@RubberDuck a maioria das "raízes de composição" que eu vi são cerca de 5 linhas de código em main. Então não. não é uma tarefa excessivamente difícil. É o material proprietário que escapa daquelas linhas contra as quais estou alertando. Você chama isso de "fazer DI corretamente". Estou perguntando se você o que você usaria para provar um problema em uma revisão de código. Ou você apenas diria "Meh"?
Candied_orange 15/05

2

Criar classes e atribuir-lhes dependências faz parte do processo de modelagem, as partes divertidas. É a razão pela qual a maioria dos programadores gosta de programar.

Decidir qual implementação será usada e como as classes serão construídas é algo que precisará ser implementado de alguma forma e faz parte da configuração do aplicativo.

Escrever fábricas manualmente é um trabalho tedioso. As estruturas de DI facilitam a resolução automática de dependências que podem ser resolvidas e exigem configuração para as que não podem.

O uso de IDEs modernos permite navegar pelos métodos e seus usos. Ter fábricas escritas manualmente é muito bom, porque você pode simplesmente destacar o construtor de uma classe, mostrar seus usos e mostrar todos os locais onde e como a classe específica está sendo construída.

Mas mesmo com a estrutura DI, você pode ter um lugar central, como a configuração de construção de gráfico de objetos (OGCC a partir de agora), e se uma classe depende de dependências ambíguas, você pode simplesmente olhar para o OGCC e ver como uma classe específica está sendo construída. ali.

Você pode objetar que, pessoalmente, acha que poder ver os usos de um construtor específico é uma grande vantagem. Eu diria que o que sai melhor para você não é apenas subjetivo, mas vem principalmente da experiência pessoal.

Se você sempre trabalhou em projetos que se baseavam em estruturas de DI, realmente saberia apenas isso e, quando quisesse ver uma configuração de uma classe, sempre procuraria o OGCC e nem tentaria ver como os construtores são usados. , porque você saberia que todas as dependências são conectadas automaticamente de qualquer maneira.

Naturalmente, as estruturas DI não podem ser usadas para tudo e de vez em quando você terá que escrever um ou dois métodos de fábrica. Um caso muito comum é construir uma dependência durante a iteração sobre uma coleção, em que cada iteração fornece um sinalizador diferente que deve produzir uma implementação diferente de uma interface comum.

No final, provavelmente tudo se resumirá às suas preferências e ao que você está disposto a sacrificar (seja escrevendo fábricas ou transparência).


Experiência pessoal (aviso: subjetivo)

Falando como desenvolvedor PHP, embora eu goste da idéia por trás das estruturas de DI, eu as usei apenas uma vez. Foi quando a equipe com quem eu trabalhava deveria implantar um aplicativo muito rapidamente e não perdemos tempo escrevendo fábricas. Então, simplesmente pegamos uma estrutura de DI, configuramos e funcionou, de alguma forma .

Para a maioria dos projetos, gosto de ter as fábricas escritas manualmente, porque não gosto da magia negra que acontece nas estruturas de DI. Eu era o responsável por modelar uma classe e suas dependências. Fui eu quem decidiu por que o design tem a aparência que tem. Então, eu serei quem construirá adequadamente a classe (mesmo que a classe use dependências difíceis, como classes concretas em vez de interfaces).


2

Pessoalmente, não uso contêineres DI e não gosto deles. Eu tenho mais algumas razões para isso.

Geralmente é uma dependência estática. Portanto, é apenas um localizador de serviço com todas as suas desvantagens. Então, devido à natureza das dependências estáticas, elas ficam ocultas. Você não precisa lidar com eles, eles já estão de alguma forma lá e é perigoso, porque você deixa de controlá-los.

Ele quebra os princípios de POO , como coesão e a metáfora de objetos de tijolos Lego de David West .

Portanto, construir o objeto todo o mais próximo possível do ponto de entrada do programa é o caminho a seguir.


1

Como você já notou: Dependendo do idioma que você usa, existem diferentes pontos de vista, diferentes abordagens sobre como lidar com problemas semelhantes.

Em relação à injeção de dependência, ele se resume a isto: injeção de dependência é, como o termo diz, nada mais do que colocar as dependências um necessidades objeto em que objeto - contrastando o contrário: deixar o objeto em si instatiate instâncias de objetos que depende. Você dissocia a criação e o consumo de objetos para obter mais flexibilidade.

Se o seu design estiver bem estruturado, você geralmente tem poucas classes com muitas dependências; portanto, a instalação de dependências - manualmente ou por uma estrutura - deve ser relativamente fácil e a quantidade de clichê para escrever os testes deve ser fácil de gerenciar.

Dito isto, não há necessidade de uma estrutura DI.

Mas por que você deseja usar uma estrutura, no entanto?

I) Evite o código padrão

Se o seu projeto obtiver um tamanho, em que você sentirá a dor de escrever fábricas (e instatações) repetidamente, use uma estrutura DI

II) Abordagem declacativa da programação

Quando Java estava programando com XML , agora é: polvilhar poeira de fadas com anotações e a mágica acontece .

Mas falando sério: vejo uma clara vantagem disso. Você comunica claramente a intenção de dizer o que é necessário, em vez de onde encontrá-lo e como construí-lo.


tl; dr

As estruturas de DI não tornam sua base de código magicamente melhor, mas ajuda a tornar o código com boa aparência realmente nítido . Use-o quando achar necessário. Em um determinado momento do seu projeto, você economizará tempo e evitará náuseas.


11
Você está certo! Costumo me preocupar muito com a forma como as coisas funcionam sob o capô. Eu tive experiências terríveis trabalhando com estruturas que eu mal entendi. Não estou advogando a favor de testar os frameworks, apenas entenda o que eles fazem e como o fazem (se possível). Mas isso depende dos seus próprios interesses. Penso que, ao entender como eles funcionam, você está em uma posição melhor para decidir se eles são adequados para você. Por exemplo, da mesma maneira que você compara ORMs diferentes.
LAIV

11
Eu acho que é sempre certo saber como as coisas funcionam sob o capô. Mas não deve assustar você não saber. Eu acho que o ponto de venda para o Spring, por exemplo, é que ele simplesmente funciona. E sim, você está absolutamente certo de que, para tomar uma decisão informada sobre a utilização de uma estrutura, você precisa ter algum grau de conhecimento. Mas eu vejo com muita frequência um padrão de desconfiança para pessoas que não têm as mesmas cicatrizes de si mesmas, embora não no seu caso, onde existe a crença, apenas quem experimentou a caça manual pode ir ao supermercado.
21817 Thomas Junk

Felizmente, atualmente, nem precisamos das estruturas Spring ou DI para Java. Já temos o JSR330 que infelizmente é ignorado (pelo menos não vi um projeto implementando). Então, as pessoas ainda tem tempo de ganhar as suas cicatrizes :-)
LAIV

Editei minha resposta e levei em consideração seus comentários. Eu removi as "desvantagens" relacionadas à depuração. Cheguei à conclusão de que você estava certo nisso. Dê uma olhada e considere atualizar sua resposta também, porque agora você citaria partes que removi da minha resposta.
21417 Laiv

11
A resposta mudou tão pouco !!! Nada que deveria te preocupar :-). A mensagem e os argumentos permanecem os mesmos. Eu só queria que sua resposta continuasse sendo como é. Eu acho que você levou apenas para remover uma das aspas.
21417 Laiv

0

Sim. Você provavelmente deve ler o livro de Mark Seemann intitulado "Injeção de Dependência". Ele descreve algumas boas práticas. Com base no que você disse, você pode se beneficiar. Você deve ter uma raiz de composição ou uma raiz de composição por unidade de trabalho (por exemplo, solicitação da web). Eu gosto do jeito dele de pensar que qualquer objeto que você cria é considerado uma dependência (assim como qualquer parâmetro para um método / construtor); portanto, você deve realmente ter cuidado com o local e como criar objetos. Uma estrutura ajudará você a manter esses conceitos claros. Se você ler o livro de Mark, encontrará mais do que apenas a resposta para sua pergunta.


0

Sim, ao trabalhar em Java, eu recomendaria uma estrutura DI. Existem algumas razões para isso:

  • O Java possui vários pacotes DI extremamente bons que oferecem injeção automatizada de dependência e também geralmente vêm com recursos adicionais mais difíceis de escrever manualmente do que o DI simples (por exemplo, dependências de escopo que permitem a conexão automática entre escopos, gerando código para procurar em que escopo deve estar use d encontrando a versão correta da dependência para esse escopo - assim, por exemplo, os objetos com escopo do aplicativo podem se referir a solicitar aqueles com escopo).

  • anotações java são extremamente úteis e fornecem uma excelente maneira de configurar dependências

  • a construção de objetos java é excessivamente detalhada em comparação com o que você está acostumado em python; o uso de uma estrutura aqui economiza mais trabalho do que em uma linguagem dinâmica.

  • As estruturas java DI podem fazer coisas úteis, como gerar automaticamente proxies e decoradores, que funcionam mais em Java do que em uma linguagem dinâmica.


0

Se seu projeto for pequeno o suficiente e você não tiver muita experiência com o DI-Framework, eu recomendaria não usar o framework e o faça manualmente.

Razão: Uma vez trabalhei em um projeto que usava duas bibliotecas que tinham dependências diretas para diferentes estruturas. ambos tinham código específico da estrutura como di-give-me-an-instance-of-XYZ. Tanto quanto eu sei, você só pode ter uma implementação de estrutura ativa por vez.

com um código como esse, você pode fazer mais mal do que benefício.

Se você tiver mais experiência, provavelmente abstrairá o trabalho de estrutura por uma interface (fábrica ou localizador de serviço), para que esse problema não exista mais e a estrutura de estrutura de trabalho traga mais benefícios.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.