Inversão de Controle (em inglês Inversion of Control (IoC)) é um princípio de design de software com o objetivo de termos sistemas modulares com baixo acoplamento onde um framework define o fluxo de controle de módulos feitos pelo programador.
Por eventos, podemos entender por exemplo:
Nesta parte do curso vamos tratar de Interface de Usuário (em inglês Graphical User Interface ou GUI), que é fazer uma interface para que o usuário possa usar o seu programa. Tipicamente faremos uma janela com alguns botões, caixa de texto de entrada, tabelas, etc...
Em C#, usaremos o framework WPF (Windows Presentation Foundation). Para definir o funcionamento de qualquer elemento estas bibliotecas (assim como a maioria das bibliotecas de GUI) usam o princípio de inversão de controle.
A primeira coisa que devemos aprender é que este padrão separa a lógica de desenhar a interface, da lógica de interação da interface,
ou seja: vamos desenhar uma interface em uma parte do código e definir funcionamento desta interface em outra parte.
Mais explicitamente:
Mas primeiro... vamos treinar como separar a definição de uma ação da criação da classe que faz esta ação, definindo a ação depois da criada do objeto
Primeiro vamos relembrar como fizemos em LPOO, definindo um comportamento em uma classe e testando:
Definição da classe com o comportamento voar:
public class Pato {
public void voar() {
System.out.println("Definido dentro da classe: O pato voa");
}
}
Testando a classe:
public class TestePato {
public static void main(String[] args) {
Pato pato = new Pato();
pato.voar();
}
}
Saída
Definido dentro da classe: O pato voa
Ok, agora vamos treinar como definir o comportamento voar depois da criação do objeto pato:
Primeiro vamos definir a interface da ação de voar (lembrando que aqui interface quer dizer classe abstrata)
public interface AcaoVoar {
public void fazVoar();
}
Agora podemos criar uma função void fazVoar(), inclusive ter essa função como parâmetro ou passar essa função como argumento para outra função.
public class Pato {
// podemos ter a acao de voar como atributo
AcaoVoar acaoVoar;
// podemos passar qualquer acao de voar como parametro
// para definir (ou redefinir) a acao de voar do pato
public void setAcaoVoar(AcaoVoar aAcaoVoar){
this.acaoVoar = aAcaoVoar;
}
// finalmente pode usar a acao de voar
public void voar() {
this.acaoVoar.fazVoar();
}
}
Agora vamos criar uma ação possivel, entre outras
public class AcaoVoarConcreta implements AcaoVoar {
@Override
public void fazVoar() {
System.out.println("Classe concreta que definine acao: faz voar");
}
}
Agora vamos testar: vamos criar o objeto e depois definir a sua ação
public class TestaPato {
public void testaPato() {
Pato pato = new Pato();
// setando como objeto anonimo de classe externa
pato.setAcaoVoar(new AcaoVoarConcreta());
pato.voar();
}
public static void main(String[] args) {
new TestaPato().testaPato();
}
}
O resultado:
Classe concreta que definine acao: faz voar
Podemos reescrever o teste da seguinte forma:
ATENÇÃO: Você não precisa saber todas, apenas uma delas
public class TestaPato {
public void testaPato() {
Pato pato = new Pato();
// setando como objeto anonimo de classe externa
pato.setAcaoVoar(new AcaoVoarConcreta());
System.out.println("\n1) Pato voando");
pato.voar();
System.out.println("\n");
// setando como inner class
pato.setAcaoVoar(new AcaoVoarInnerClass());
System.out.println("\n2) Pato voando");
pato.voar();
System.out.println("\n");
// setando como classe anonima
pato.setAcaoVoar(new AcaoVoar() {
@Override
public void fazVoar() {
System.out.println("Acao de classe anonima: faz voar");
}
});
System.out.println("\n3) Pato voando");
pato.voar();
System.out.println("\n");
// setando como expressao lambda (VOCE NAO PRECISA SABER EXPRESSOES lambda)
pato.setAcaoVoar(() -> System.out.println("Acao como expressao lambda: faz voar"));
System.out.println("\n4) Pato voando");
pato.voar();
System.out.println("\n");
}
// Definicao de inner class (uma classe dentro de outra classe)
class AcaoVoarInnerClass implements AcaoVoar{
@Override
public void fazVoar() {
System.out.println("Acao definida em classe interna: faz voar");
}
}
public static void main(String[] args) {
new TestaPato().testaPato();
}
}
E se eu quiser que a ação voar seja uma lista de várias ações que eu queira adicionar ao objeto?
Agora podemos também fazer com que Pato tenha uma lista de ações de voar, e adicionar ações de voar no pato para que ele faça todas adicionadas quando voar.
Reescreveremos a classe Pato como a seguir
import java.util.ArrayList;
import java.util.List;
public class Pato {
List<AcaoVoar> acaoVoarList = new ArrayList<>();
public void addAcaoVoar(AcaoVoar aAcaoVoar){
this.acaoVoarList.add(aAcaoVoar);
}
public void voar() {
for(AcaoVoar acaoVoar : this.acaoVoarList) {
acaoVoar.fazVoar();
}
}
}
Reescreveremos o teste como a seguir (mudando apenas setAcaoVoar para addAcaoVoar)
public class TestaPato {
public void testaPato() {
Pato pato = new Pato();
pato.addAcaoVoar(new AcaoVoarConcreta());
System.out.println("\n1) Pato voando");
pato.voar();
System.out.println("\n");
pato.addAcaoVoar(new AcaoVoarInnerClass());
System.out.println("\n2) Pato voando");
pato.voar();
System.out.println("\n");
pato.addAcaoVoar(new AcaoVoar() {
@Override
public void fazVoar() {
System.out.println("Acao de classe anonima: faz voar");
}
});
System.out.println("\n3) Pato voando");
pato.voar();
System.out.println("\n");
pato.addAcaoVoar(() -> System.out.println("Acao como expressao lambda: faz voar"));
System.out.println("\n4) Pato voando");
pato.voar();
System.out.println("\n");
}
// Definicao de inner class (uma classe dentro de outra classe)
class AcaoVoarInnerClass implements AcaoVoar{
@Override
public void fazVoar() {
System.out.println("Acao definida em classe interna: faz voar");
}
}
public static void main(String[] args) {
new TestaPato().testaPato();
}
}
Vamos testar o que aprendemos em um botão JButton:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class TesteBotao {
public void testeBotao() {
// Primeiro vamos fazer uma janela para colocar o botao
JFrame janela = new JFrame();
// Agora vamos criar o botao e colocar na janela
JButton botao = new JButton("Botao Teste");
janela.add(botao);
// Agora vamos criar a acao usando inner class
// (poderia ser criada de qualquer jeito, desde que implemente ActionListener)
AcaoBotao acaoBotao = new AcaoBotao();
// Vamos inserir a acao no botao
botao.addActionListener(acaoBotao);
// Agora vamos fazer algumas configuracoes que vamos aprender mais adiante para que servem
// 1 - redimensiona para caber todos componentes
janela.pack();
// 2 - configura para terminar o programa quando a janela for fechada
janela.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// 3 - torna a janela visivel
janela.setVisible(true);
}
// Este eh o ActionListener que vai ser inserido no botao
class AcaoBotao implements ActionListener{
// Um action listener deve ter um actionPerformed
// esta eh a acao que sera acionada quando o botao (evento) for pressionado
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("O Botao foi pressionado!");
}
}
public static void main(String[] args) {
new TesteBotao().testeBotao();
}
}
Também podemos inserir ações em botões de outras formas:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class TesteBotoes {
public void testeBotoes() {
JFrame janela = new JFrame();
// Vamos incluir varios botoes, precisamos selecionar um layout
janela.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
// criando acao por inner class
JButton botaoInnerClass = new JButton("Botao Inner class");
janela.add(botaoInnerClass);
botaoInnerClass.addActionListener(new AcaoBotao());
// criando acao por classe anonima
JButton botaoClasseAnonima = new JButton("Botao classe anonima");
janela.add(botaoClasseAnonima);
botaoClasseAnonima.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Classe Anonima: o botao foi pressionado");
}
});
// criando acao por expressao lambda
JButton botaoLambda = new JButton("Botao lambda expression");
janela.add(botaoLambda);
botaoLambda.addActionListener((ActionEvent e) -> System.out.println("Lambda Expression: o botao foi pressionado"));
janela.pack();
janela.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
janela.setVisible(true);
}
class AcaoBotao implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Inner class: O Botao foi pressionado!");
}
}
public static void main(String[] args) {
new TesteBotoes().testeBotoes();
}
}
.