• Nenhum resultado encontrado

A SUPERCLASSE HANDLE

No documento INTRODUÇÃO AO SOFTWARE MATLAB (páginas 60-65)

Nos últimos exemplos, utilizou-se o mecanismo de herança para definir uma Subclasse da classe handle, sendo esta última uma classe já definida no MATLAB. Fazendo isso se informa ao ambiente MATLAB que a classe que se está definindo é uma classe que trabalha por referência, e não por valor (padrão). A diferença básica entre as duas formas (valor e referencia) é análoga a qualquer outra linguagem de programação. Quando se trabalha por referência, apenas o identificador do objeto é passado em uma operação. Para entender esse processo, observe os dois exemplos, onde classes idênticas são criadas para posterior manipulação.

classdef testeRef < handle properties variavel end end classdef testeValor properties variavel end end

Perceba que a classe testeRef é uma sublcasse da classe handle, trabalhando, portanto, por referência. Já a classe testeValor, por não ser uma classe derivada de handle, trabalha por valor.

Após salvar devidamente as classes, execute a avalie a saída do código abaixo: A = testeRef; A.variavel = 10; B = testeValor; B.variavel = 10; a = A; b = B; a.variavel = 20; b.variavel = 20; A B

Perceba que a linha a.variavel = 20; implicou em uma mudança no objeto A, enquanto a linha b.variavel = 20; não alterou a propriedade variável do objeto B. Esse mecanismo é o mesmo que a indireção utilizando ponteiros em C, onde as variáveis eram alteradas não por modificação direta de seu valor, mas por meio de uma operação utilizando ponteiros.

Esse é o motivo pelo qual a definição de uma função como no próximo exemplo não acarreta uma modificação em um objeto que trabalha por valor.

classdef testeValor properties

variavel end methods

function altera(obj, valor) %poderia ser [] = altera...

obj.variavel = valor; end

end end

2

AULA 15 – HANDLE E EVENTOS

clear all close all clc B = testeValor; B.altera(100); B.variavel

Não modificam o valor da variável, uma vez que obj, no método, tem seu valor copiado, e não é uma referencia. Para que o método realmente modifique o objeto quando da utilização do método, a função definida na classe deve retornar o objeto modificado.

function obj = altera(obj, valor) obj.variavel = valor;

end

E é por isso que classes que trabalham por referência não necessitam retornar o objeto, uma vez que o objeto declarado como argumento do método já contém o identificador do objeto a ser manipulado, sendo, portanto, o próprio objeto e não uma cópia.

EVENTOS

Na orientação ao objeto, um evento é uma transmissão de informação unidirecional de um objeto para outro1, informando algo que aconteceu. Normalmente essa informação é enviada a todo o ambiente (broadcasted), e apenas os objetos que estiverem prestando atenção a certo evento reagirão a essa informação. Nesse sentido, o objeto que notificou o evento é chamado de objeto fonte (source object), e os objetos que respondem ao evento são chamados de ouvintes (listners).

Os eventos podem acarretar modificações em objetos ou ainda desencadear uma série de operações. Tais operações usualmente são implementadas em funções chamadas de callback.

Um objeto que define um evento deve ser instanciado de uma subclasse da classe handle, obrigatoriamente, uma vez que os ouvintes recebem uma referencia do objeto fonte da interrupção.

Como um Evento é Modelado e Tratado Ao se utilizar eventos, segue-se o seguinte fluxo de operações:

1. Uma classe define um nome para um determinado evento.

2. Após instanciado um objeto dessa classe que define um evento, se define um ou mais ouvintes para o evento. Esses ouvintes cuidarão se o evento será acionado. Geralmente a função addlistener() é utilizada para criar tais ouvintes, onde uma função é definida para responder ao evento.

3. Uma chamada a algum método do objeto fonte ativa o evento. O evento é ativado pela chamada a notify().

4. Ouvintes percebem o evento e executam funções (call-backs) em resposta ao evento.

1

Para compreender o processo, segue um exemplo simples. A classe testeEvento possui apenas uma propriedade: variavel. Além disso, a classe define dois eventos, que serão ativados quando o valor de variavel for negativo ou quando o valor de variavel for maior do que 100. Observe como os eventos são definidos e como eles são ativados utilizando a função notify().

classdef testeEvento < handle properties variavel end events varNegativa varGrande end methods

function obj = testeEvento %não é necessário abrir ()

%contrutor obj.variavel = 0; end function set.variavel(obj,valor) obj.variavel = valor; if obj.variavel > 100 notify(obj,'varGrande') end if obj.variavel < 0 notify(obj,'varNegativa') end end end end

Em sua forma mais básica, notify() deve receber em seu primeiro argumento o objeto fonte da interrupção, e seu segundo argumento informa qual interrupção foi ativada.

Define-se agora a função de resposta ao evento. Essas funções obrigatoriamente devem possuir dois argumentos: o primeiro argumento é o objeto fonte do evento e o segundo é informações a respeito do evento ativado. Por padrão, a informação é passada através de um objeto que contém o nome e o objeto fonte do evento (mais detalhes no exemplo).

Assim, duas funções são criadas: funcNegat e funcGrande. Observe o que cada uma dessas funções faz.

function [] = funcNegat(objFonte, infoEvento) fprintf('## Evento disparado! ')

fprintf('Variável é Negativa!');

fprintf('\nO valor da variável é %f\n',objFonte.variavel); fprintf('\nNome do evento: %s\n',infoEvento.EventName); disp('Fonte do Evento:')

disp(infoEvento.Source);

disp('---')

4

AULA 15 – HANDLE E EVENTOS

function [] = funcGrande(objFonte, infoEvento) fprintf('## Evento disparado! ')

fprintf('\nVariável é Maior que 100!');

fprintf('O valor da variável é %f\n', objFonte.variavel); fprintf('\nNome do evento: %s\n',infoEvento.EventName); disp('Fonte do Evento:')

disp(infoEvento.Source);

disp('---')

end

Finalmente, pode-se criar o objeto e definir ouvintes para os eventos. Execute, para tanto, os comandos abaixo:

clear all close all clc

oo = testeEvento;

addlistener(oo, 'varGrande', @funcGrande); %definindo ouvintes

addlistener(oo, 'varNegativa', @funcNegat); oo.variavel = 1; oo.variavel = 20; oo.variavel = 100; oo.variavel = 100.1; oo.variavel = 99.991; oo.variavel = -0.1;

Perceba a sintaxe para se definir um ouvinte para o evento:

addlistener(objetoFonte,'nome_do_evento', @nome_callback)

A função addlistene cria um objeto que monitora o evento. Tal objeto monitora o evento 'nome_do_evento' do objeto objetoFonte, acionando a função nome_callback. Como a função foi definida como uma função de callback, automaticamente os dois argumentos padrão serão enviados (o objeto fonte e as informações, como explicado anteriormente). Perceba que a função é invocada através de uma referência para função (function handle). Logicamente, para que o exemplo funcione, os arquivos devem estar contidos na mesma pasta.

As funções de resposta podem ser métodos de classe ou ainda funções estáticas. Como exercício, tente entender a dinâmica envolvida no exemplo abaixo, mais complexo. Considerando as classes definidas:

classdef ex1 < handle

properties var end events opa end methods function ativa(obj) notify(obj,'opa'); end end methods (Static) function olha(objFonte,info) disp('olha só...') end end end classdef ex2 properties variavel end methods

function obj = ex2

obj.variavel = ex1; %cria uma instancia de ex1 addlistener(obj.variavel,'opa',@ex1.olha); %cria ouvinte... obj.variavel.var = 1; end function teste(obj) obj.variavel.ativa(); end end end

O método ativa()notifica a ocorrência do evento. Observe que o método estático olha da classe ex1 (definido como uma função de resposta a um evento) é chamado utilizando o nome da classe (ex1.olha). Perceba que o construtor da classe ex2 define um ouvinte e a função de resposta ao evento opa.

Execute as linhas abaixo na janela de comando e tente percorrer o caminho dos sinais, desde a definição do ouvinte até à resposta ao evento.

aux = ex2;

aux.variavel.var = 2; aux.variavel.ativa();

Perceba que as funções são chamadas e executas em resposta aos eventos disparados. Isso torna o código e o programa mais dinâmico e mais fiel ao mundo real, afinal, eventos são rotineiros em nossa vida. Além disso, vale lembrar que operações como pressionar uma tecla ou posicionar o mause sobre um botão são ações que usualmente disparam eventos quando se trabalha com interfaces gráficas. Portanto, saber modelar eventos, e principalmente implementar as funções de callback é fundamental quando se pretende construir aplicações de alto nível.

E

XERCÍCIOS

Exercício 1 Trabalhando com o exercício da aula anterior, defina eventos (e respectivos ouvintes e funções de resposta) para quando o saldo se em situações distintas (escolha situações triviais e reais). As funções de resposta devem informar que foram executadas apresentando uma mensagem na tela.

1 AULA 16 – ELEMENTOS GRÁFICOS

INTRODUÇÃO

No documento INTRODUÇÃO AO SOFTWARE MATLAB (páginas 60-65)