Perfeito! Agora vamos aprofundar o conceito de injeção de dependência, que se encaixa naturalmente na arquitetura que estamos construindo com IMotor e ITransmissao.
Este será o próximo post da série, dando continuidade à ideia de sistemas conectados por contrato (interfaces), e mostrando como deixar essas conexões ainda mais limpas e flexíveis.
🔌 Injeção de Dependência: Dê o Motor Certo para Cada Carro
Nos artigos anteriores, aprendemos como encapsular os detalhes de uma classe, e como fazer com que objetos como TMotor e TTransmissao se conectem sem criar dependências circulares — como um parafuso e uma porca.
Também vimos como isso fica ainda mais poderoso quando usamos interfaces, permitindo que cada parte do sistema conheça apenas o contrato da outra parte, e não sua implementação interna.
Agora, vamos explorar o passo seguinte na arquitetura orientada a objetos: a injeção de dependência.
🤝 O que é dependência?
Primeiro, vamos entender o que significa “dependência” nesse contexto.
Quando uma classe precisa de outra para funcionar, dizemos que ela tem uma dependência.
No nosso exemplo:
TCarrodepende deIMotorpara gerar torque.TCarrodepende deITransmissaopara enviar esse torque às rodas.
Sem essas duas peças, o carro não anda.
🚫 O problema: criar dependências dentro da própria classe
Uma prática comum (e perigosa) é a classe instanciar suas dependências internamente, assim:
constructor TCarro.Create;
begin
FMotor := TMotorCombustao.Create;
FTransmissao := TTransmissaoManual.Create;
end;
Isso funciona, mas tem um custo:
- Você amarra a classe
TCarroa implementações específicas. - Fica impossível trocar o motor ou a transmissão sem alterar a própria classe.
- Testar o carro com simulações ou mocks se torna inviável.
Esse padrão é chamado de injeção interna (ou acoplamento direto) — e é exatamente o que a injeção de dependência tenta evitar.
✅ A solução: Injetar dependências de fora
A ideia da Injeção de Dependência (DI) é simples:
Em vez da classe criar suas dependências, ela as recebe de fora, prontas para uso.
No nosso exemplo, TCarro pode ser construído assim:
constructor TCarro.Create(AMotor: IMotor; ATransmissao: ITransmissao);
begin
FMotor := AMotor;
FTransmissao := ATransmissao;
end;
E o uso:
Motor := TMotorCombustao.Create;
Transmissao := TTransmissaoManual.Create;
Carro := TCarro.Create(Motor, Transmissao);
Isso é injeção de dependência via construtor — a forma mais comum e direta.
🔁 Outras formas de injeção
Além do construtor, podemos fazer injeção por propriedade ou por método:
1. Injeção por propriedade
type
TCarro = class
public
property Motor: IMotor write SetMotor;
property Transmissao: ITransmissao write SetTransmissao;
end;
Essa abordagem dá mais flexibilidade, mas pode causar erros se esquecer de configurar algo antes de usar o objeto.
2. Injeção por método
procedure TCarro.Configurar(AMotor: IMotor; ATransmissao: ITransmissao);
begin
FMotor := AMotor;
FTransmissao := ATransmissao;
end;
Funciona bem para inicializações dinâmicas, mas exige disciplina no uso.
🧪 Benefícios reais da Injeção de Dependência
- Flexibilidade: troque o motor ou a transmissão a qualquer momento.
- Testabilidade: injete versões falsas (mocks) para testes.
- Reutilização:
TCarronão muda se você criarTMotorEletrico,TTransmissaoCVT, etc. - Baixo acoplamento: classes sabem apenas o necessário sobre suas dependências.
- Coesão: cada classe cuida apenas da sua própria lógica.
🛠️ Dica prática: use interfaces sempre que possível
Quando combinamos:
- Encapsulamento
- Interfaces
- Injeção de Dependência
… estamos construindo um sistema verdadeiramente modular, escalável e fácil de evoluir.
🔄 Evoluindo ainda mais: Inversão de Controle
A Injeção de Dependência é um dos pilares do padrão maior chamado Inversão de Controle (IoC).
Em vez das classes controlarem suas dependências, o controle é movido para o nível superior do sistema (ou um contêiner de injeção).
No Delphi, isso pode ser feito manualmente, por fábricas, configurações externas, ou usando frameworks como o Spring4D, que oferece suporte a contêineres IoC (mas você pode fazer tudo isso sem framework, como estamos mostrando).
📌 Conclusão
A Injeção de Dependência é como montar um carro com peças modulares:
- O chassi (
TCarro) não precisa saber como o motor ou a transmissão foram feitos. - Ele só precisa receber componentes compatíveis — conectados por interfaces — e tudo funciona.
Essa abordagem reduz o acoplamento, aumenta a coesão e transforma seu código em uma plataforma extensível, pronta para crescer com segurança.
Se quiser, no próximo artigo podemos mostrar como criar fábricas para montar objetos com dependências pré-configuradas, ou partir para um exemplo mais completo com testes simulados (mocks).
Qual direção você gostaria de seguir agora?