350 likes | 449 Views
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. Inversão de Controle - IoC. Princípio de Hollywood: “Não nos ligue, nós ligaremos”. Dependência de componentes.
E N D
Copyleft by Diego Tarábola e Denise Goya 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 Inversão de Controle - IoC • 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. Visão Geral
Copyleft by Diego Tarábola e Denise Goya 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 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 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. 3 - diagrama UML de seqüência public class A{ private B b; public A(){ } public setB(B b){ this.b=b; } }
Copyleft by Diego Tarábola e Denise Goya Tipos de Inversão de Controle
Copyleft by Diego Tarábola e Denise Goya 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 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 Contêineres de Inversão de Controle Spring Avalon PicoContainer HiveMind Excalibur: Fortress Resin Copland (Ruby) Mentawai
Copyleft by Diego Tarábola e Denise Goya 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 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 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 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 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 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 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 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 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 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 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 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 Ex.: Injeção de Dependência com Spring • arquivo de configuração para BeanDefinition: • classe PropertiesBeanDefinitionReader ou XmlBeanDefinitionReader privatestatic 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 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. publicstaticvoid 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 Ex.: Injeção de Dependência com Spring • Com XmlBeanDefinitionReader, é possível gerenciar a configuração do bean utilizando XML em vez de propriedades: • XmlBeanFactory é derivada de DefaultListableBeanFactory que estende a configuração utilizando XmlBeanDefinitionReader DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory); rdr.loadBeanDefinitions(new FileSystemResource("src/conf/beans.xml")); Oracle oracle = (Oracle)factory.getBean("oracle"); 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 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. • 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 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> </beans>
Copyleft by Diego Tarábola e Denise Goya 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 Ex.: Injeção de Dependência com Spring • Lendo configuração do XML: publicclass HelloWorldXml { publicstaticvoid 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(); } privatestatic 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 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” • A tag <ref> relaciona uma referência do bean à propriedade; • Não é mais preciso utilizar o método set para injetar as dependências. <bean id="renderer” class="com.apress.prospring.ch2.StandardOutMessageRenderer"> <property name="messageProvider"> <ref local="provider"/> </property> </bean>
Copyleft by Diego Tarábola e Denise Goya Ex.: Injeção de Dependência com Spring • Configuração de Injeção de Dependência com XML: publicclass HelloWorldXmlDI { publicstaticvoid main(String[] args) throws Exception { // get the bean factory BeanFactory factory = getBeanFactory(); MessageRenderer mr = (MessageRenderer)factory.getBean("renderer"); mr.render(); } privatestatic 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 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 Ex.: Injeção de Dependência com Spring • Configuração de Injeção de Dependência com XML: • 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>. publicclassConfigurableMessageProvider { private String message; public ConfigurableMessageProvider(String message){ this.message = message; } public String getMessage(){ return message; } }
Copyleft by Diego Tarábola e Denise Goya 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 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 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 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.