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:
- ASP, uma tecnologia mal interpretada.
- Programação orientada a eventos e lambda function em ASP/VBScript.
- Linguagens: Baseada em Objetos e Orientada a Objetos.
- Orientação a Objetos em VBScript “Hackers way”.
- Scripting Components, os “Às” na manga.
- 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:
- 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.