Programação orientada a eventos
A programação orientada a eventos, também conhecida como programação baseada em eventos, é um paradigma de programação, isto é, um estilo fundamental de se programar, no qual a execução do programa é afetada por eventos(geralmente detectados por sensores).
Esta forma de se programar é, na minha humilde opinião, a base de todos os sistemas de UI(User Interface) sofisticados. Por exemplo, aquela barrinha do MAC OSX que todos adoram e suas similares:
RocketDock
Ela nunca abre uma janela solicitando “Vá com o mouse para cima de um ícone e clique”, ela simplesmente possui um sensor que avisa “Olha, programa, detectei que o mouse entrou na posição (x,y) da barra”, ao receber esta mensagem o programa faz o zoom e mostra o label dos ícones na posição. Este parece um exemplo bobo, mas acho que ilustra bem a diferença entre a programação em lote e a programação orientada a eventos.
É importante para um programador web conhecer a programação orientada a eventos, pois uma das características mais marcantes da Web 2.0 é a grande quantidade de widgets que fazem parte das chamadas RIA(Rich Application Interface) que são aqueles programas com telas que você pensa “Que legal isso aqui!”.
Uma outra coisa importante a ser notada é que os paradigmas de programação não são mutuamente exclusivos, isto é, uma linguagem, e por conseqüência um programa, pode suportar múltiplos paradigmas. Um programa pode ser puramente em lote, puramente orientado a eventos, ou conter trechos de ambos os paradigmas. Pode-se, portanto, criar programas meio orientado a objetos/procedurais, meio orientado a eventos/lote. Um arquiteto de software experiente saberá qual o melhor paradigma a ser seguido para construir um determinado feature do programa.
Apesar desta forma de se programar poder ser executada em qualquer linguagem de programação, ela é facilitada por linguagens com noções funcionais como JScript, Lisp (a segunda linguagem mais antiga da nossa história), Haskell entre outras. Nos exemplos a seguir mostrarei como programar:
- Utilizando-se apenas VBScript.
- Fazendo um bridge simples com JScript para utilizar a noção de lambda que ela possui (um bom exercícío aqui é utilizar a noção de lambda do Python para alcançar o mesmo objetivo).
Nota: O segundo exemplo é um dos motivos pelos quais julgo o ASP extremamente versátil, pois ele mostra uma aplicação do conceito exposto no primeiro artigo.
Função lambda
Para aqueles que ainda não foram apresentados, a função lambda é um feature interessante que nasceu com o Lisp e permite você definir e utilizar funções em tempo de execução. Esta é uma das características que facilitaram o desenvolvimento de AI(Artificial Intelligence) (pense em um algorítimo recursivo que gera, interpreta e executa funções para gerar, interpretar e executar funções e vai aprendendo alguma coisa importante durante o processo).
Mãos à massa
Utilizando-se a classe CustomEvent(versão sem comentários) que escrevi para o AXE(ASP Xtreme Evolution), mas que funciona out-of-the-box e tem licensa MIT:
<% class CustomEvent public classType public classVersion public Owner private Handlers public Arguments private sub Class_initialize() classType = typename(Me) classVersion = "1.0.0" set Handlers = Server.createObject("Scripting.Dictionary") set Arguments = Server.createObject("Scripting.Dictionary") end sub private sub Class_terminate() Handlers.removeAll() Arguments.removeAll() set Handlers = nothing set Arguments = nothing end sub public sub addHandler(fn) select case typename(fn) case "JScriptTypeInfo" set Handlers.item(fn.toString()) = fn case else set Handlers.item(fn) = getRef(fn) end select end sub public sub removeHandler(fn) set Handlers.item(fn) = nothing Handlers.remove(fn) end sub public sub fire() dim fn : for each fn in Handlers Handlers.item(fn)(Me) next end sub public function revealArguments() revealArguments = "" dim arg : for each arg in Arguments revealArguments = revealArguments & ", " & arg next revealArguments = mid(revealArguments, 3) end function end class %> <script language="javascript" runat="server"> function lambda(f) { if(/^function\s*\([ a-z0-9.$_,]*\)\s*{[\S\s]*}$/gim.test(f)) { eval("f = " + f.replace(/\r/g, '').replace(/\n/g, '')); return f; } else { return function() {}; } } </script>Você pode criar eventos nas suas classes como no exemplo de classe abaixo, que será utilizada nos exemplos 1 e 2:<% class ClassWithEvents public classType public classVersion public onComplimentBefore' [1] public onComplimentAfter private sub Class_initialize() classType = typename(Me) classVersion = "1.0.0" set onComplimentBefore = new CustomEvent : set onComplimentBefore.Owner = Me' [2] set onComplimentAfter = new CustomEvent : set onComplimentAfter.Owner = Me end sub private sub Class_terminate() set onComplimentBefore = nothing' [3] set onComplimentAfter = nothing end sub public sub compliment(firstname, lastname, nickname) onComplimentBefore.Arguments.item("firstname") = firstname' [4] onComplimentBefore.Arguments.item("lastname") = lastname onComplimentBefore.Arguments.item("nickname") = nickname call onComplimentBefore.fire()' [5] Response.write("Method compliment called." & vbNewline) call onComplimentAfter.fire() end sub end class %>
Exemplo 1
Utilizando a classe que possui eventos para criar objetos com eventos em VBScript:
<code><pre><%
sub ev_onComplimentBefore(ev)' [6]
Response.write("Event onComplimentBefore has been fired.
I was really expecting this method to say:
'Hello World " & ev.Arguments.item("firstname") & " " &
ev.Arguments.item("lastname") & " (" & ev.Arguments.item("nickname") & ")'" & vbNewline)
end sub
sub ev_onComplimentAfter(ev)
Response.write("Event onComplimentAfter has been fired" & vbNewline)
end sub
dim CwE : set CwE = new ClassWithEvents
call CwE.onComplimentBefore.addHandler("ev_onComplimentBefore")' [7]
call CwE.onComplimentAfter.addHandler("ev_onComplimentAfter")
call CwE.compliment("Fabio", "Nagao", "nagaozen")
set CwE = nothing
%></pre></code>
O código acima deve escrever na tela do seu navegador:
Event onComplimentBefore has been fired. I was really expecting this method to say: ‘Hello World Fabio Nagao (nagaozen)’
Method compliment called.
Event onComplimentAfter has been fired
Seguem abaixo as notas de rodapé [#] que deixei no código fonte:
- Definindo os eventos.
- Inicializando os eventos como tipo CustomEvent e atribuindo um ponteiro à instância da classe através da propriedade Owner.
- Liberando os eventos criados na etapa 2 da memória.
- Definindo os argumentos do evento.
- Avisando o sensor que ocorreu um evento.
- Configurando ações para um determinado evento.
- Configurando o sensor para observar um evento.
Exemplo 2
Ok, o exemplo 1 foi legal, mas a maioria das pessoas acostumadas com programação orientada a eventos vai reclamar daquelas subrotinas da nota [6]. O que é natural, pois aquilo é pouquíssimo intuitivo. Afinal de contas, se os eventos estão relacionados ao objeto e somente a ele, porque não os definir dentro do próprio objeto? Bom, isso é verdade, o problema é que o VBScript não possui nenhum método para fazer isso dentro do objeto. Aqui, novamente IMHO(In My Humble Opinion), as alternativas naturais seriam:
* ou extender a classe ClassWithEvents para outra classe que possuísse as definições das ações do evento e depois criar uma instância dessa nova classe, o que é impossível pela noção de objetos do VBScript;
* ou utilizar a noção de lambda function, que também não existe no VBScript.
Então, o que fazer? Se render às limitações expressivas da linguagem? Jamais! Afinal de contas, não adianta mudar de linguagem, pois qualquer linguagem sempre tem limitações, não é verdade?
A solução, dentro do ASP, é mais fácil do que se imagina. Você fala JScript? Python? Haskell? Nossa, o ASP também! Essas linguagens possuem uma capacidade expressiva funcional? Siiim! Pronto, resolvido o problema!
Explicando melhor: Para aqueles que ainda não perceberam, juntamente com a definição da classe CustomEvent, criei também uma função global em javascript (que é a mesma coisa que JScript) chamada lambda. Essa função recebe a definição de uma função, interpreta e retorna a função interpretada. Javascript é uma linguagem tão bonita que incorporou o conceito de lambda ao seu núcleo e portanto ela usa lambda sem ter uma palavra reservada para isso (diferentemente do Python). Esta função, dentro do ASP, não é global apenas para o JScript, ela é global para a aplicação inteira, isto é, você pode invocar ela do VBScript. Com essa nova função em mãos, podemos transformar o exemplo 1 no seguinte:
<%
dim CwE : set CwE = new ClassWithEvents
call CwE.onComplimentBefore.addHandler(lambda("function(ev){ Response.write('Event onComplimentBefore has been fired. I
was really expecting this method to say: \'Hello World ' + ev.Arguments.item('firstname') + ' ' +
ev.Arguments.item('lastname') + ' (' + ev.Arguments.item('nickname') + ')\'\r\n') }"))
call CwE.onComplimentAfter.addHandler(lambda("function(ev){ Response.write('Event onComplimentAfter has been fired') }"))
call CwE.compliment("Fabio", "Nagao", "nagaozen")
set CwE = nothing
%>
Que também imprime a mesma mensagem do exemplo 1 na tela do navegador. Convenhamos, ficou bem melhor agora, não? Muito mais intuitívo também.
Resumo da história
O paradigma de programação orientada a objetos aplicado juntamente com o de programação orientado a eventos, permite ao programador criar ambientes que se assemelham muito à forma que compreendemos os objetos e eventos no nosso mundo físico. Por exemplo, estamos acostumados a atravessar a rua quando o semáforo está verde e a parar quando está vermelho. Um programador, ao tentar modelar este ambiente, pode criar, por exemplo, as classes “Pedestre” e “Semáforo”. O “Pedestre” tem um método “.andarPara(local)” que diz para ele ir para algum local. Este método, possui um sensor “onSemáfaroVermelho” que aciona o procedimento “.parar”, quando o sensor do pedestre receber “onSemáfaroVerde” do “Semáforo”, então ele chama “.andarPara(local)” novamente.
RSS Feed
Twitter
Posted in
Tags: