Dev (Back & Front)ARTIGO

Programação orientada a eventos e lambda function em ASP/VBScript

Olá amigos do iMasters. Neste artigo vou falar um pouco sobre alguns temas que julgo atuais e importantes para todo programador moderno. Entretanto os exemplos serão escritos em ASP/VBScript+JScript, pois assim sou coerente com o propósito dos artigos que me propuz a escrever, que é apresentar o ASP como um ambiente extremamente versátil e eficiente. Segue abaixo a relação dos artigos:

  1. ASP, uma tecnologia mal interpretada.
  2. Programação orientada a eventos e lambda function em ASP/VBScript.
  3. Linguagens: Baseada em Objetos e Orientada a Objetos.
  4. Orientação a Objetos em VBScript “Hackers way”.
  5. Scripting Components, os “Às” na manga.
  6. Caching, conceito de DRY(Don’t Repeat Yourself) aplicado ao ASP.

Se você estiver lendo um artigo meu pela primeira vez, recomendo enfaticamente que leia os anteriores primeiro, pois estou tentando levá-los à compreensão de uma grande abstração, contando uma pequena parte da idéia por vez. Colocarei links para os temas anteriores para facilitar o acesso, mas utilizem CTRL+Click, pois não há links de um artigo anterior para seus sucessores.

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:

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:

  1. Utilizando-se apenas VBScript.
  2. 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:

<%<br /><br />class CustomEvent<br />   <br />    public classType<br />    public classVersion<br />   <br />    public Owner<br />    private Handlers<br />    public Arguments<br />   <br />    private sub Class_initialize()<br />        classType    = typename(Me)<br />        classVersion = "1.0.0"<br />       <br />        set Handlers  = Server.createObject("Scripting.Dictionary")<br />        set Arguments = Server.createObject("Scripting.Dictionary")<br />    end sub<br />   <br />    private sub Class_terminate()<br />        Handlers.removeAll()<br />        Arguments.removeAll()<br />        set Handlers  = nothing<br />        set Arguments = nothing<br />    end sub<br />   <br />    public sub addHandler(fn)<br />        select case typename(fn)<br />            case "JScriptTypeInfo"<br />                set Handlers.item(fn.toString()) = fn<br />            case else<br />                set Handlers.item(fn) = getRef(fn)<br />        end select<br />    end sub<br />   <br />    public sub removeHandler(fn)<br />        set Handlers.item(fn) = nothing<br />        Handlers.remove(fn)<br />    end sub<br />   <br />    public sub fire()<br />        dim fn : for each fn in Handlers<br />            Handlers.item(fn)(Me)<br />        next<br />    end sub<br />   <br />    public function revealArguments()<br />        revealArguments = ""<br />        dim arg : for each arg in Arguments<br />            revealArguments = revealArguments & ", " & arg<br />        next<br />        revealArguments = mid(revealArguments, 3)<br />    end function<br />   <br />end class<br /><br />%><br /><script language="javascript" runat="server"><br />function lambda(f) {<br />    if(/^function\s*\([ a-z0-9.$_,]*\)\s*{[\S\s]*}$/gim.test(f)) {<br />        eval("f = " + f.replace(/\r/g, '').replace(/\n/g, ''));<br />        return f;<br />    } else {<br />        return function() {};<br />    }<br />}<br /></script>

Você pode criar eventos nas suas classes como no exemplo de classe abaixo, que será utilizada nos exemplos 1 e 2:

<%<br /><br />class ClassWithEvents<br />    public classType<br />    public classVersion<br />   <br />    public onComplimentBefore' [1]<br />    public onComplimentAfter<br />   <br />    private sub Class_initialize()<br />        classType    = typename(Me)<br />        classVersion = "1.0.0"<br />       <br />        set onComplimentBefore = new CustomEvent : set onComplimentBefore.Owner = Me' [2]<br />        set onComplimentAfter = new CustomEvent : set onComplimentAfter.Owner = Me<br />    end sub<br />   <br />    private sub Class_terminate()<br />        set onComplimentBefore = nothing' [3]<br />        set onComplimentAfter = nothing<br />    end sub<br />   <br />    public sub compliment(firstname, lastname, nickname)<br />        onComplimentBefore.Arguments.item("firstname") = firstname' [4]<br />        onComplimentBefore.Arguments.item("lastname")  = lastname<br />        onComplimentBefore.Arguments.item("nickname")  = nickname<br />       <br />        call onComplimentBefore.fire()' [5]<br />        Response.write("Method compliment called." & vbNewline)<br />        call onComplimentAfter.fire()<br />    end sub<br />   <br />end class<br /><br />%>

Exemplo 1

Utilizando a classe que possui eventos para criar objetos com eventos em VBScript:

<code><pre><%<br /><br />sub ev_onComplimentBefore(ev)' [6]<br />    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)<br />end sub<br /><br />sub ev_onComplimentAfter(ev)<br />    Response.write("Event onComplimentAfter has been fired" & vbNewline)<br />end sub<br /><br />dim CwE : set CwE = new ClassWithEvents<br />call CwE.onComplimentBefore.addHandler("ev_onComplimentBefore")' [7]<br />call CwE.onComplimentAfter.addHandler("ev_onComplimentAfter")<br />call CwE.compliment("Fabio", "Nagao", "nagaozen")<br />set CwE = nothing<br /><br />%></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:

  1. Definindo os eventos.
  2. Inicializando os eventos como tipo CustomEvent e atribuindo um ponteiro à instância da classe através da propriedade Owner.
  3. Liberando os eventos criados na etapa 2 da memória.
  4. Definindo os argumentos do evento.
  5. Avisando o sensor que ocorreu um evento.
  6. Configurando ações para um determinado evento.
  7. 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:

<%<br /><br />dim CwE : set CwE = new ClassWithEvents<br />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') }"))<br />call CwE.onComplimentAfter.addHandler(lambda("function(ev){ Response.write('Event onComplimentAfter has been fired') }"))<br />call CwE.compliment("Fabio", "Nagao", "nagaozen")<br />set CwE = nothing<br /><br />%>

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.

é matemático do IME-USP, técnico em processamento de dados da ETFSP (atual CEFETSP). É o atual chefe de desenvolvimento da LojComm Internet e programador desde 1996. Criador do AXE(ASP Xtreme Evolution) MVC Framework e de vários widgets de web 2.0 como iCarousel, iMask, fValidator e iFisheye. Contribuidor de vários projetos open source: GTKSourceView-2.0, GEdit, Mootools e outros menos populares. Blog: http://zend.lojcomm.com.br/

Ver perfil