Spring Framework
IoC - Inversão de Controle
Jobson Ronan {[email protected]}
Motivação


Desenvolver uma aplicação sempre impõe
desafios
Muitos deles já foram solucionados por outros
esforçados “guerreiros”

Criaram padrões... Padrões de projeto... Design
patterns
Alguns padrões muito comuns










Facade
Singleton
Factory
Abstract Factory
Command
Adapter
Decorator
Service Locator
Prototype
...
Design e Manutenibilidade

Sistemas manuteníveis



Evoluem bem com mudanças e iterações
Chave do segredo: Gerenciar dependências
Outros aspectos importantes

Design por contrato




Testes unitários


Definição limpa do contrato das classes/interfaces
Contrato: O que faz, como se comporta
Não como está implementado!
Escrever testes que verificam o contrato da classe/interface
Separação de interesses

Escrever uma classe/interface para cada interesse
Dependência

É quando se prende um componente a outro por:






Herança
Composição
Instanciação
Instanciação por factory
Parâmetro de método
Uso de métodos ou atributos estáticos
Princípios de desenvolvimento ágil

Quando uma Componente possui uma dependência, sua
implementação pode precisar mudar quando



Abstrações tendem a ser estáveis


Os requisitos mudarem, ou...
Quando sua dependência mudar
Interfaces estão normalmente pouco sujeitas a serem alteradas
Componentes concretos tendem a ser instáveis

Classes concretas normalmente estão mais sujeitas a terem
sua implementação alterada
Dependência por Composição
public class UserManager {
private UserDao userDao;
}
UserManager
<<use>>
UserManager depende de UserDao

Composição cria um fraco acoplamento


UserDao
Quando o comportamento ou a interface da dependência
(UserDao) muda, apenas o dependente precisa se adaptar
Não Possui os efeitos colaterais de herança
Dependência por Instanciação
public class WebServerProbe {
public boolean isRunning() {
Socket socket = new Socket();
...
WebServerProbe
<<use>>
Socket
WebServerProbe depende de Socket
Dependência por factory
UserManager
public class UserManager {
public Collection getUsers() {
UserDAO userDAO =
UserDAOFactory.newInstance();
...
<<use>>
<<use>>
UserDaoFactory
<<create>>
UserDao
UserManager depende de UserDaoFactory
(UserManager também depende de UserDao)
Dependência por parâmetro de metodo
public class UserManager {
public void saveUser(User user) {
...
UserManager
<<use>>
User
UserManager depende de Usuário

Mesmo acoplamento fraco obtido por composicao
Dependência por uso de método estático
public class WebServerProbe {
private Collection ports;
WebServerProbe
<<use>>
public void setPorts(Integer[] ports) {
this.ports =
java.util.Arrays.asList(ports);
...
WebServerProbe depende de Arrays
Arrays
Dependências

Algumas formas de dependência são piores que outras

Herança: Quando novos métodos são adicionados e usados,
os componentes não recebem nenhum aviso para
sobrescreve-los


Prefira Composição
Classes, métodos e atributos estáticos (ex: Singletons):
dependências ficam escondidas nos componentes
dependentes, tornando difícil a alteração dos dependentes
quando componentes estáticos são alterados

Usar inversão de controle
Dependências

Dependências não são um mal


O objetivo é



Minimizar o número de dependências no seu modelo
Depender apenas de interfaces
Deve-se primar por



Claro que sempre existirão dependências.
Componentes fracamente acoplados
Depender de interfaces e não de classes, o máximo possível
Quanto menos sua aplicação estiver sujeita a mudanças,
mais estável e manutenível estará
“Fracamente acoplados”

Acoplamento forte

Instanciação de classes concretas dentro da lógica de negócio
InputStream is = new FileInputStream(...);

Uso de métodos estáticos
InputStream is = StreamUtils.getConfigFileInputStream();


Herança
Acoplamento fraco

Usando Factories e ServiceLocator
InputStream is = new ConfigFactory().createInputStream();

Usando inversão de controle
Inversão de controle

...Mas primeiro, sem IoC
Implementação sem IoC

O próprio componente precisa obter suas dependências


Ex: JNDI, EJB Stub, JDBC Connection, arquivos de
propriedades, etc.
Desacoplável por:



Codificar por interfaces
Obter esses objetos usando um Factory ou um ServiceLocator
Efetuar-se chamadas a esses objetos, potencialmente
para obter outras dependências


Ex: JNDI, JDBC Connection Pool, etc.
Encadeia dependências
Conseqüências




Código cheio de código específico de obtenção a
dependências
Instanciação cria acoplamento
Código ligado a um determinado ambiente (container)
(EJB Container, Servlet Container, Rich client, ...)
Dificulta a implementação de testes unitários
Código específico de obtenção a dependências




As vezes, mais código para isso que para a
implementação da lógica de negócio
Solução clássica: encapsular > Service locator, Factory
Problema: Escrever os Factories
Problema: Singletons are evil



Dependências não muito claras, atravessam todo o código
Dificultam unit testing
Dificultam Refactories
Codificação por interface desacopla, mas...



A instanciação ainda usa uma implementação concreta
(=> acoplamento)
Solução clássica: usar uma Factory
Problema:



forte acoplamento com a Factory
Torna difícil a manutenção
Torna difícil a implementação de Testes Unitários
Código ligado a um determinado ambiente


Código de recuperação de dependências dentro da lógica de
negócio
Ambiente/container variados





Standalone Java Application
Swing/SWT Java Application
Dentro de um container EJB
Dentro de um servlet container
Exemplo: Obtenção de um objeto Connection (JDBC)




standalone: usando java.sql.DriverManager
standalone com pool: usando Apache Commons DBCP
Tomcat: usando InitialContext e DataSource
...
Dificulta a implementação de testes unitários


Porque o “como” de obter uma dependência está codificado dentro
da classe que se quer testar
Ex: Usando um Factory, como você pode mudar seu
comportamento de acordo com um teste unitário?
class MyLogic {
public void doSomething() {
// use the InitialContext (JNDI) Service Locator
// to retrieve the “BusinessLogic” object
InitialContext ctx = new InitialContext();
BusinessLogic partner =
(BusinessLogic) ctx.lookup(“my/business/logic/impl”);
// now perform the actual logic:
partner.doYourOwnBusiness();
}
}

Agora... Como testar MyLogic sem EJB e precisando de um EJB
container?
Inversão de controle

...E agora com IoC
Inversão de controle


Um meio de por os componentes juntos
Define-se



Interfaces e implementações
Dependências entre classes e interfaces (transformando-as em
“colaboradores”)
O Container de Inversão de controle


Cria o dependente e a dependência, e injeta esta ultima no
dependente
Possibilita selecionar que implementação de dependência
injetar em cada dependente (por configuração, código,
automática)
Inversão de controle
Inversão de controle
Inversão de controle

Arquitetura do container leve




Usa POJOs
Sem necessidade de deploy em um container pesado
Aumenta testabilidade
Não é intrusiva


Não depende de nenhuma API especifica do container
Sem interfaces para implementar, sem classes para herdar,
exceto as suas
IoC - Princípios

“Hollywood principle”




“Don´t call me, i´ll call you”
Sem IoC, componentes lógicos tem o controle sobre suas
dependências e,
por conseguinte,
devem obtê-las
Com IoC, a lógica dos componentes não tem controle sobre
suas dependências e não as obtém
Um container de IoC irá injetar as dependências nos objetos
(Dependency Injection)
Principais vantagens




Efetivamente desacopla componentes lógico de suas
dependências
Remove o código de obtenção de dependências dos
dependentes (Agora é trabalho do container).
Melhora o design do modelo
Aumenta a flexibilidade e o reuso de componentes


Testes unitários ficam mais fáceis
Não depende de ambientes específicos
Exemplos

Sem inversão de controle
class MyLogic {
private BusinessServiceInterface businessService = null;
protected final BusinessServiceInterface getBusinessService() {
if (businessService == null) {
// retrieve JNDI InitialContext
Context ctx = new InitialContext();
Context env = (Context) ctx.lookup(“java:comp/env”);
Object obj = env.lookup(“ejb/BusinessServiceHome”);
// retrieve EJB stub
BusinessServiceHome businessServiceHome =
(BusinessServiceHome) PortableRemoteObject.narrow(
env, BusinessServiceHome.class);
businessService = businessServiceHome.create();
}
return businessService;
}
public void doYourThing() {
getBusinessService().doYourBusiness();
}
}
Exemplos

Sem inversão de controle




Tenta testar isso!
Terá que re-implementar quando BusinessService não for mais
um EJB
Terá que re-implementar quando não estiver em um EJB
container (Sem JNDI)
Terá que re-implementar quando não quiser mais um cache do
Stub
Exemplos

Sem IoC, com ServiceLocator:
protected final BusinessServiceInterface getBusinessService(){
if (businessService == null) {
businessService = MyServices.getBusinessService();
}
return businessService;
}





Estratégia de ciclo de vida centralizado para BusinessServiceInterface
Dependência centralizada com o container EJB
Testes unitários ainda difíceis (precisa alterar o comportamento de MyService)
Se diferentes classes precisarem de objetos BusinessServiceInterface
de diferentes fontes? Como cuidar disso?
Você terá um não manutenível número de Services Locators, ou catastróficos
efeitos colaterais ao alterar a implementação de MyService
Exemplos

Com IoC
class MyLogic {
// businessService will be set by the IoC container:
private BusinessServiceInterface businessService;
// we'll use setter-based injection (explained later):
public void setBusinessService(BusinessServiceInterface businessService) {
this.businessService = businessService;
}
public void doYourThing() {
// perform call on businessService
businessService.doYourBusiness();
}
}



Sem dependência em como o BusinessService é obtido
Sem código de obtenção dedependêcia, apenas um atributo e um setter
Depende apenas da interface BusinessServiceInterface
Exemplos

Sem inversão de controle
Exemplos

Sem IoC, com ServiceLocator
Exemplos

Com IoC
Mas como funciona??
class MyMain {
public static void main(String[] args) {
// initialize the IoC container (here it's Spring):
XmlBeanFactory xmlBeanFactory =
new XmlBeanFactory(new ClassPathResource(“beans.xml”));
// retrieve MyLogic:
MyLogic myLogic = (MyLogic) xmlBeanFactory.getBean(“myLogic”);
// call the method:
myLogic.doYourThing();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans ...>
<!-- this is beans.xml -->
<beans>
<bean id=”theBusinessSvc” class=”foo.BusinessServiceMock”/>
<bean id=”myLogic” class=”foo.MyLogic”>
<property name=”businessService”>
<bean ref=”theBusinessSvc”/>
</property>
</bean>
</beans>
Conclusões



O princípio da inversão de controle pode ser aplicado
elegantemente com o Spring
Isto irá reduzir em grande quantidade a quantidade de padrões de
projeto aplicados, simplificando o design do modelo
Isto está mudando a forma de como desenvolver aplicações
“Precisamos de novos Design Patterns,
os que conheciamos não são mais necessários.”
Jobson Ronan
Tipos de injeção de dependência



Tipo 1: Interface-based injection
Tipo 2: Setter-based injection
Tipo 2: Contructor-based injection
Exercício

Criar aplicação completa de reservas de videos



Extrair dependências possuídas pela fachada a DAOs
concretos.


DAOs + Fachada
Não é necessário classes de cadastro
A fachada deve apenas conhecer a interface dos DAOs
Use o springframework para injetar as dependências
Spring Framework
IoC - Inversão de Controle
Jobson Ronan {[email protected]}
Download

Spring Framework Inversão de Controle