Segundo Martin Fowler todo e qualquer conteiner leve deve por si só implementar Inversão de Controle (Inversion of Control, ou IoC), definidos por três possíveis formas de Injeção de Dependência (DI), constructor Injection (Injeção por Construtores), Setter Injection (Injeção por Métodos Set) e Interface Injection (Injeção por Interfaces), a DI está relacionada ao baixo acoplamento de diferentes componentes de seu sistema e como acessar o mesmos não por uma chamada direta e sim por uma “injeção” do conteiner, hoje existem vários conteiner leves que fazem essas implementações como PicoContainer, Spring, Avalon. O Guice criado pelos “Googlers” Bob Lee e Kevin Bourrillion traz como principal característica um conteiner puramente DI, suportando injeção via constructor e setter e podendo funcionar somente com anotações ou se preferir, construir módulos para definir os bind entre objetos.
DI, Guice Style
Diferentemente do clássico pattern Factory que traz a instância de uma implementação, quando se trata de DI ocorre a inversão desse controle e o Guice simplifica com uma simples anotação @Inject dizendo onde o conteiner vai injetar.
Code 1.
public class Cliente {
private Service service;
@Inject
public Cliente(Service service) {
this.service = service;
}
public void getMyService(){
service.getService();
}
}
No Code 1 acima usa-se uma injeção por construtor. O mais interessante é a configuração de dependência:
Code 2.
@ImplementedBy(ServiceImpl.class)
public interface Service {
void getService();
}
@Singleton
public class ServiceImpl implements Service {
public void getService() {
System.out.println(“Metodo getService foi chamado”);
}
}
Pronto, agora podemos testar o nosso exemplo a partir da classe Cliente do Code 1, as anotações @ImplementedBy define sua implementação de classe eliminando qualquer código, e a @Singleton define o escopo de implementação por padrão o Guice cria uma instância a cada chamada.
Code 3.
public class App {
public static void main(String[] args) {
Injector guiceInj = Guice.createInjector(new Module[0]);
Cliente c = guiceInj.getInstance(Cliente.class);
c.getMyService ();
}
}
Se tivermos sorte nosso Code 3 irá imprimir “Metodo getService foi chamado”, é claro podemos também definir nosso bind explicitamente por construção de módulos:
Code 4.
public class MyModule implements Module {
public void configure(Binder binder) {
binder.bind(Service.class).to(ServiceImpl.class).in(Scopes.SINGLETON);
}
}
…..
Injector guiceInj = Guice.createInjector(new MyModule());
Cliente c = guiceInj.getInstance(Cliente.class);
c.getMyService ();
……
O Code 4 mostra que um único metodo da interface é definido com um objeto Bind, podendo ser usado para definir sua especial sintaxe que pode ser lido da seguinte forma: “Amarra interface Service a implementação ServiceImpl no escopo Singleton”. Ate ai tudo bem, mais como sair da situação onde temos mais de uma implementação para nossa Service.class? Podemos também utilizar o AbstractModule para fazer nossa subclasse. A mesma implementa a interface Module e não define argumento no metodo configure, sigamos o exemplo com essa implementação:
Code 5.
public class MyModule extends AbstractModule {
public void configure() {
bind(Service.class).to(ServiceImpl.class);
bind(Service.class).to(ServiceImplNew.class);
}
}
Provavelmente quando for executar esse Code 5 irá ocorrer um erro de execução, pois o Guice não trabalha desta forma, então como sair dessa situação? Simples, basta fazer um bind com anotação da seguinte forma:
Code 6.
public class MyModule extends AbstractModule {
public void configure() {
bind(Service.class).to(ServiceImpl.class);
bind(Service.class).annotateWith(Marca.class).to(ServiceImplNew.class);
}
}
Isso mesmo, podemos definir um bind com uma marcação anotada em uma determinada requisição, vamos a sintaxe lendo da esquerda para direita: “Amarra todas as requisições para a interface Service anotado com @Marca para a implementação ServiceImpl”, porém a anotação @Marca deve ser criada programaticamente com a seguinte estrutura:
|
Code
|
Explanation
|
|
@Retention(RetentionPolicy.RUNTIME)
|
Anotação em tempo de execução
|
|
@Target({ElementType.FIELD,ElementType.PARAMETER})
|
Onde pode ser colocada
|
|
@BindingAnnotation
|
Informa que é uma anotaçao ‘binding’
|
|
Public @interface Marca{}
|
Como é declarada
|
Feito isso basta definir como marcação:
public class Cliente {
private Service service;
@Inject
public Cliente(@Marca Service service) {
this.service = service;
}
……
Bom, explorando mais a fundo o Guice podemos encontrar inúmeras funcionalidades como bindInterceptor, Aspect-Oriented Programming (AOP), Integrating Web(Struts 2, Wicket) entre outros, onde pode ou não trazer benefícios para sua arquitetura, isso vai depender muito do que você precisa e com que você esta trabalhando. Anteriormente em muitos fóruns já foi discutido isso, e muita gente não se sente muito confortável de como o Guice trabalha, porém, temos que concordar que não há muito que refazer com o que já foi feito só nos resta a escolha. Boa sorte a todos e espero que eu tenha ajudado de alguma forma com esse meu primeiro post.
Referencias:
http://martinfowler.com/bliki/InversionOfControl.html
http://www.javafree.org/content/view.jf?idContent=1
http://www.apress.com/book/view/9781590599976
http://code.google.com/p/google-guice/