Inversão de Controle - IoC
Tópicos (Avançados) de
Programação Orientada a Objetos
Prof. Fabio Kon
DCC - IME - USP
Diego Tarábola e Denise Goya
outubro/2006
Copyleft by Diego Tarábola e Denise Goya
1
Inversão de Controle - IoC
Visão Geral
Princípio de Hollywood: “Não nos ligue, nós ligaremos”.
Dependência de componentes.
Dependência de componentes também conhecida como
colaboradores de objetos.
O objeto que exige dependência é conhecido como objeto
dependente.
Arcabouço é responsável pela execução da operação.
Ocorre em tempo de execução.
Copyleft by Diego Tarábola e Denise Goya
2
Inversão de Controle — Motivação
Cenário 1
Classe A precisa de uma referência
para a Classe B.
Classe B é uma classe concreta que
tem um construtor padrão.
Classe A possui uma instância de B.
Nenhuma outra classe pode acessar
uma instância da Classe B [Mal06].
public class A{
private B b;
public A(){
b=new B();
}
}
1 - diagrama UML de seqüência
Copyleft by Diego Tarábola e Denise Goya
3
Inversão de Controle — Motivação
Cenário 2
Objeto a possui referência para os
objetos c e b.
public class A{
private B b;
public A(){
C c=new C();
b=new B(c);
}
}
2 - diagrama UML de seqüência
Copyleft by Diego Tarábola e Denise Goya
4
Inversão de Controle — Motivação
Cenário 3
A precisa de uma referência
para B, e não precisa saber
como B é instanciado.
B pode ser uma interface, uma
classe abstrata ou concreta.
Antes de instanciar a classe A,
precisa de uma referência para
a classe B.
public class A{
private B b;
public A(){ }
public setB(B b){
this.b=b;
}
}
3 - diagrama UML de seqüência
Copyleft by Diego Tarábola e Denise Goya
5
Tipos de Inversão de Controle
Inversão de
Controle
Procura por
Dependência
Contextualized
Dependency
Lookup
Injeção de
Dependência
Dependency
Pull
Interface
Copyleft by Diego Tarábola e Denise Goya
Setter
Construtor
6
Tipos de Inversão de Controle
Inversão de Controle (IoC) possui dois tipos [Har05]:
Injeção de Dependência (Dependency Injection)
Procura por Dependência (Dependency Lookup)
Injeção de Dependência sempre diz respeito a IoC, mas IoC
nem sempre referencia Injeção de Dependência
Copyleft by Diego Tarábola e Denise Goya
7
Tipos de Inversão de Controle
Procura por Dependência:
um componente deve obter uma referência para uma
dependência;
dois subtipos:
Dependency Pull
Contextualized Dependency Lookup (CDL)
Injeção de Dependência:
as dependências são literalmente injetadas (incluídas)
três subtipos:
Interface – Tipo 1
Setter – Tipo 2
Constructor – Tipo 3
Copyleft by Diego Tarábola e Denise Goya
8
Contêineres de Inversão de Controle
Spring
Excalibur: Fortress
Avalon
Resin
PicoContainer
Copland (Ruby)
HiveMind
Mentawai
Copyleft by Diego Tarábola e Denise Goya
9
Procura por Dependência —
Dependency Pull
As dependências são obtidas através de um registro assim que
necessário;
Mecanismo para encontrar componentes que gerenciam o
arcabouço.
Procura por Dependência com Spring
public static void main(String[] args) throws Exception {
// get the bean factory
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer) factory.getBean("renderer");
mr.render();
}
Copyleft by Diego Tarábola e Denise Goya
10
Procura por Dependência —
Contextualized Dependency Lookup (CDL)
Semelhante ao Dependency Pull;
Procura é execuada pelo contêiner que está gerenciando o
recurso e não a partir de um registro central;
CDL funciona através da implementação de uma interface.
Interface do Componente para CDL com Spring
public interface ManagedComponent {
public void performLookup(Container container);
}
Copyleft by Diego Tarábola e Denise Goya
11
Procura por Dependência —
Contextualized Dependency Lookup (CDL)
Executa o método performLookup() implementado pelo
componente;
O componente pode procurar sua dependência utilizando a
interface Container.
Obtendo dependência com o CDL
public class ContextualizedDependencyLookup implements ManagedComponent
{
private Dependency dep;
public void performLookup(Container container) {
this.dep = (Dependency) container.getDependency("myDependency");
}
}
Copyleft by Diego Tarábola e Denise Goya
12
Injeção de Dependência —
por meio de Construtor
Dependência do componente é disponibilizada através de
seu(s) construtor(es);
O argumento recebido será sua dependência.
Injeção de Dependência pelo Construtor com Spring
public class ConstructorInjection {
private Dependency dep;
public ConstructorInjection(Dependency dep) {
this.dep = dep;
}
}
Copyleft by Diego Tarábola e Denise Goya
13
Injeção de Dependência —
por meio de método Setter
O contêiner IoC injeta um componente de dependência através
de seu método set (JavaBean);
O componente setter permite um conjunto de dependências que
o contêiner de inversão de controle pode gerenciar.
Injeção de Dependência através do método Setter com Spring
public class SetterInjection {
private Dependency dep;
public void setMyDependency(Dependency dep) {
this.dep = dep;
}
}
Copyleft by Diego Tarábola e Denise Goya
14
Injeção de Dependência —
por meio de Interface [Fow06]
Componente de dependência através de uma interface;
A interface InjectFinder será definida por qualquer um que
fornecer a interface MovieFinder (no exemplo abaixo).
Injeção de Dependência através de Interface. Exemplo no arcabouço Avalon
public interface InjectFinder {
public void injectFinder(MovieFinder finder);
}
public class MovieLister implements InjectFinder {
public void injectFinder(MovieFinder finder){
this.finder = finder;
}
Copyleft by Diego Tarábola e Denise Goya
15
Injeção de Dependência —
por meio de Interface
Exemplo:
Classe de Teste no Avalon: configura os componentes e os
injetores.
public class Tester {
private Container container;
private void configureContainer(){
container = new Container();
registerComponents();
registerInjectors();
container.start();
}
}
Copyleft by Diego Tarábola e Denise Goya
16
Injeção de Dependência —
por meio de Interface
É preciso registrar os injetores que irão injetar os componentes
dependentes;
Cada interface de injeção precisa de algum código para injetar
o objeto dependente;
No código abaixo, registram-se os objetos injetores no
contêiner:
private void registerInjectors(){
container.registerInjector(InjectFinder.class,
container.lookup(“MovieFinder”));
}
Copyleft by Diego Tarábola e Denise Goya
17
Injeção versus Procura
O tipo de IoC é definido pelo contêiner adotado.
Com Dependency Pull, é necessário:
registrar dependências;
obter referências das dependências;
interagir com as dependências obtidas.
Utilizando CDL, é preciso:
que as classes implementem uma interface específica;
e procure por todas dependências manualmente.
Com Injeção de Dependência, as classes precisam:
permitir que as dependências sejam injetadas por meio de
construtores, métodos setters ou interfaces.
Copyleft by Diego Tarábola e Denise Goya
18
Contêiner de Inversão de Controle
Arcabouços incluem contêineres de IoC;
Arcabouços manifestam inversão de controle em tempo de
execução, por meio de retorno de chamadas [SGN04] ;
Chamadas invocam métodos hook de componentes definidos
pela aplicação, sempre que ocorre algum evento;
Na ocorrência de um evento, o arcabouço chama de volta um
método virtual num componente de aplicação pré-registrado;
O componente executa o processamento definido pela
aplicação, em resposta ao evento;
O controle é alternado entre o arcabouço e a aplicação.
Copyleft by Diego Tarábola e Denise Goya
19
Contêiner de Inversão de Controle
Exemplo: Recebimento de chamada telefônica [POSA 2]
Uma companhia de telefone possui um mecanismo IoC;
Um usuário: um tratador de evento, que está registrado junto à
companhia, para tratar as chamadas ao número desse usuário.
Quando alguém chama pelo número daquele usuário, a rede
notifica o tratador que um evento de requisição de chamada
está pendente (via toque do telefone);
Assim que o telefone é tirado do gancho, existe uma reação a
esta requisição (o tratador assume o controle), iniciando uma
conversação entre as partes conectadas.
Copyleft by Diego Tarábola e Denise Goya
20
Ex.: Injeção de Dependência com Spring
Bean:
qualquer componente gerenciado pelo contêiner;
configuração do bean:
armazena informações próprias e do bean do qual depende;
representada por instâncias de classes que implementam a interface
BeanDefinition;
Interface BeanFactory:
responsável por gerenciar componentes no contêiner;
qualquer aplicação interage com o Spring por meio dessa
interface;
a aplicação deve:
criar uma instância da classe que implemente essa interface;
configurar com a informação de dependência.
Copyleft by Diego Tarábola e Denise Goya
21
Ex.: Injeção de Dependência com Spring
arquivo de configuração para BeanDefinition:
classe PropertiesBeanDefinitionReader ou
XmlBeanDefinitionReader
private static BeanFactory getBeanFactory() throws Exception {
// obtém a fábrica do bean
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// cria uma definição do leitor
PropertiesBeanDefinitionReader rdr = new
PropertiesBeanDefinitionReader(factory);
// lê as opções de configuração
Properties props = new Properties();
props.load(new FileInputStream("./src/conf/beans.properties"));
rdr.registerBeanDefinitions(props);
return factory;
}
Copyleft by Diego Tarábola e Denise Goya
22
Ex.: Injeção de Dependência com Spring
A informação de BeanDefinition é obtida através de um arquivo
de propriedades (properties).
Uma vez criada e configurada a implementação de
BeanFactory, o bean é encontrado utilizando seu nome, que
está configurado no arquivo de propriedades.
public static void main(String[] args) throws Exception {
//
get the bean factory
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer) factory.getBean("renderer");
mr.render();
}
Copyleft by Diego Tarábola e Denise Goya
23
Ex.: Injeção de Dependência com Spring
Com XmlBeanDefinitionReader, é possível gerenciar a
configuração do bean utilizando XML em vez de propriedades:
DefaultListableBeanFactory factory = new
DefaultListableBeanFactory();
XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
rdr.loadBeanDefinitions(new
FileSystemResource("src/conf/beans.xml"));
Oracle oracle = (Oracle)factory.getBean("oracle");
XmlBeanFactory é derivada de
DefaultListableBeanFactory que estende a configuração
utilizando XmlBeanDefinitionReader
XmlBeanFactory factory = new XmlBeanFactory(
new FileSystemResource("ch4/src/conf/beans.xml"));
Oracle oracle = (Oracle)factory.getBean("oracle");
Copyleft by Diego Tarábola e Denise Goya
24
Ex.: Injeção de Dependência com Spring
Configuração do bean:
O ponto chave para qualquer aplicação baseada no Spring é
configurar um arquivo para sua aplicação.
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
</beans>
Cada bean é definido com a tag <bean>, dentro de <beans>
<bean> possui dois atributos:
<id> utilizado para obter seu nome padrão
<class> especifica o tipo do bean
Copyleft by Diego Tarábola e Denise Goya
25
Ex.: Injeção de Dependência com Spring
Exemplo de configuração com XML:
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="renderer"
class="com.apress.prospring.ch2.StandardOutMessageRenderer"/>
<bean id="provider"
class="com.apress.prospring.ch2.HelloWorldMessageProvider"/>
</beans>
Copyleft by Diego Tarábola e Denise Goya
26
Ex.: Injeção de Dependência com Spring
Lendo configuração do XML:
public class HelloWorldXml {
public static void main(String[] args) throws Exception {
// get the bean factory
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer)factory.getBean("renderer");
MessageProvider mp = (MessageProvider)factory.getBean("provider");
}
mr.setMessageProvider(mp);
mr.render();
private static BeanFactory getBeanFactory() throws Exception {
// get the bean factory
XmlBeanFactory factory = new XmlBeanFactory(new
FileSystemResource("ch4/src/conf/beans.xml"));
return factory;
}
}
Copyleft by Diego Tarábola e Denise Goya
27
Ex.: Injeção de Dependência com Spring
Usando Setter Injection
A tag <property> especifica a injeção de dependência;
Para relacionar o bean “provider” a messageProvider do
bean “renderer”
<bean id="renderer”
class="com.apress.prospring.ch2.StandardOutMessageRenderer">
<property name="messageProvider">
<ref local="provider"/>
</property>
</bean>
A tag <ref> relaciona uma referência do bean à propriedade;
Não é mais preciso utilizar o método set para injetar as
dependências.
Copyleft by Diego Tarábola e Denise Goya
28
Ex.: Injeção de Dependência com Spring
Configuração de Injeção de Dependência com XML:
public class HelloWorldXmlDI {
public static void main(String[] args) throws Exception {
// get the bean factory
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer)factory.getBean("renderer");
}
mr.render();
private static BeanFactory getBeanFactory() throws Exception {
// get the bean factory
XmlBeanFactory factory = new XmlBeanFactory(
new FileSystemResource("ch4/src/conf/beans.xml"));
return factory;
}
}
Copyleft by Diego Tarábola e Denise Goya
29
Ex.: Injeção de Dependência com Spring
Usando Constructor Injection
A tag <constructor-arg> especifica a injeção de dependência,
em vez da tag <property>;
É passada uma String (e não mais um bean);
Utiliza-se a tag <value> em vez da tag <ref> para especificar
o valor do argumento do construtor;
<bean id="provider"
class="com.apress.prospring.ch4.ConfigurableMessageProvider">
<constructor-arg>
<value> This is a configurable message </value>
</constructor-arg>
</bean>
Copyleft by Diego Tarábola e Denise Goya
30
Ex.: Injeção de Dependência com Spring
Configuração de Injeção de Dependência com XML:
public class ConfigurableMessageProvider {
private String message;
public ConfigurableMessageProvider(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
Para passar mais de um argumento ou para utilizar mais de
um construtor deve-se usar um índice, iniciando-se em 0 na
tag <constructor-arg>.
Copyleft by Diego Tarábola e Denise Goya
31
Inversão de Controle e Padrão Reactor
Reactor: Padrão de Arquitetura [POSA 2];
Conhecido também como
Dispatcher, Notifier
Contexto:
Aplicação orientada a eventos que processa as informações
de forma síncrona e serial.
Problema: aplicações devem:
atender muitas requisições simultaneamente,
demultiplexar e despachar as indicações de eventos às
respectivas implementações de serviço.
Copyleft by Diego Tarábola e Denise Goya
32
Inversão de Controle e Padrão Reactor
Define uma interface que permite que aplicações:
registrem ou removam tratadores de eventos;
sejam executadas no laço de eventos da aplicação.
Reactor utiliza um demultiplexador síncrono de evento para
aguardar pela indicação de eventos que se originam de uma ou
mais fontes.
Quando ocorre um evento:
o demultiplexador síncrono de evento notifica o Reactor;
o Reactor aciona o tratador associado, para realização do
serviço solicitado.
O Reactor inverte o fluxo de controle, pois é responsabilidade
dele (e não da aplicação) em disparar os tratadores concretos
para cada tipo de evento.
Copyleft by Diego Tarábola e Denise Goya
33
Desvantagens na Inversão de Controle
Dificuldade na integração de arcabouços:
quando dois ou mais arcabouços chamam
simultaneamente o código da aplicação, cada qual
pressupondo seu próprio fluxo de controle;
Dificuldade na depuração do código e
compreensão do fluxo:
o controle é alternado entre o código da aplicação e
o código do arcabouço.
Copyleft by Diego Tarábola e Denise Goya
34
Referências
[Fow06] Fowler, Martin. Inversion of Control Containers and
Dependency Injection Pattern. Disponível em:
http://www.martinfowler.com/articles/injection.html, acesso em
30/08/2006.
[Har05] Harrop, Rob. Machacek, Jan. Pro Spring. Capítulo 4,
páginas 49 a 92. Apress, 2005.
[Mal06] Malarvannan Mani. Design Better Software with the
Inversion of Control Pattern. Disponível em:
http://www.devx.com/Java/Article/27583, acesso em 20/09/2006
[POSA 2] Schmidt, Douglas. Stal, Michael. Rohnert, Hans.
Buschmann, Frank. Pattern-Oriented Software Architecture:
Patterns for Concurrent and Networked Objects. Volume 2.
Capítulo 3, páginas 175 a 214. Ed. John Wiley & Sons, 2000.
[SGN04] Schmidt, Douglas. Gokhale, Aniruddha. Natarajan,
Balachandran. Leveraging Application Frameworks. ACM
Queue Magazine, Vol2, Nº 5, jul-ago/2004.
Copyleft by Diego Tarábola e Denise Goya
35