Archive for category Técnicas de programação
Google Developer Day – Questão 5
Publicado por javiani em Idéias, Javascript, New school, Técnicas de programação em outubro 17, 2010
E aí galera, tranquilo?
Eu queria postar sobre o DevQuiz do Google faz um tempinho, em particular a questão número 5 que era um pouco mais complexa, mas não achei correto publicar a minha solução enquanto as inscrições ainda rolavam, mas agora que já elvis, gostaria de publicá-la aqui.
A história na verdade começou quando na Agência, ao saber do questionário, a galera de Interface começou a resolver as questões, cada um usando a sua lógica. No final, alguns fizeram e compartilharam suas soluções.
Eu achei interessante isso, porque saiu um pouco da rotina meio “Fábrica de Pastel” e começamos a discutir as soluções propostas de cada um, percebemos que pensamos de forma muito diferente, não pior, não melhor, mas diferente.
Como o tempo contava na hora de responder as perguntas, não se pode criticar o código final seja lá qual for o problema, se de resolução, performance, design ou qualquer outro…
E como defensor ferrenho do Javascript, entusiasta, estudante e escritor do assunto ( já que tenho o blog ) acertei na mosca a resposta, certo?
BÉEEEEEEEEEE! FAIL!
Meu script foi considerado o mais dificil de se entender ( o que é muito subjetivo ) e no final das contas não funcionou retornando a resposta certa =\.
DAMN!!!
Maaasssss felizmente acabei aprendendo coisas interessantes nos códigos alheios, o que valeu a brincadeira.
Claro, após revisto os erros, reescrevi o código envolvendo as funções soltas em um objeto e preservei TODA a lógica, removendo as redundâncias.
O primeiro código fail foi esse:
var double_n = function(string){
return !!string.match(/1{2}|2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|9{2}|0{2}/g)
}
var soma_n = function(string){
var cont = 0
string.replace(/\d/g, function(d){
cont += +d
return d
})
return !(!!(!cont % 2))
}
var last_first = function(string){
if (string.charAt(0) == string.charAt(string.length - 1))
return false
return true
}
//Telefone é o textarea com os números
var array = $('#telefone').val().split(/\n/)
//O último elemento vinha vazio por causa do último \n
array.pop()
var contador = 0
$.each(array, function(i){
if (double_n(array[i]) && soma_n(array[i]) && last_first(array[i]))
contador++
})
console.log(contador)
Meu erro foi a mania que tenho de querer me certificar de que o retorno de uma condição retorne um valor booleano de fato ao invés de um Truthy ou Falsy. Ou seja, num retorno de string vazia, tenho a mania de transformar antes em um boolean tipo: !!”"
Com isso, errei ali no retorno da soma_n, na precedência dos operadores unários.
Mas acabei aprendendo com esse erro e além disso aprendi a melhorar minha RegExp verbosa, por uma bem mais simples na solução do líder técnico da área.
Aí vai o código reescrito com as modificações necessárias, removi algumas redundâncias, mas a lógica se mantém a mesma:
var Resolution = {
of :function(array, cont){
var el = null
var i = 0
with( this ) while( el = array[i++] )
cont += if_hasnt_dup(el) &&
if_sum_isnt_mod2(el) &&
if_last_isnt_first(el) ? 1:0
return cont
},
if_sum_isnt_mod2 :function(string){
var cont = 0
string.replace(/\d/g, function(d){ cont += +d })
return !(cont % 2)
},
if_hasnt_dup :function(string){
return !(!!string.match(/(\d)\1/g))
},
if_last_isnt_first :function(string){
return !(string.charAt(0) == string.charAt(string.length - 1))
}
}
alert( Resolution.of(arr, 0) )
Eu usei um with ali, vocês sabem que é horrível e que jamais devemos usar o with, mas eu coloquei ali para dificultar ainda mais o código rsrsrs.
Para quem programa em Javascript, a resolução do problema foi bem rápida, uma porque a própria linguagem permite isso a outra porque era só botar o código no firebug e executar! Que foi o que eu fiz e imagino que muitos outros fizeram também.
Também fiz em Ruby, usando a mesma lógica que usei em Javascript:
require 'jcode'
def sum_isnt_mod2?(string)
cont = 0
string.each_char { |c| cont+= c.to_i }
(cont%2).zero?
end
def hasnt_dup?(string)
!(string =~ /(\d)\1/)
end
def last_isnt_first?(string)
!(string[0,1] == string[-1,1])
end
def answer (arr,count)
arr.each do |v|
count += 1 if sum_isnt_mod2?(v) && hasnt_dup?(v) && last_isnt_first?(v)
end
count
end
puts answer(array, 0)
Em ambas as linguagens, resolvi considerando os números de telefones como strings ao invés de números, porque achei muito mais fácil.
Você que está lendo o blog agora, resolveu a questão? Acertou ? Errou ?
Posta sua solução nos comentários, acertando ou não tenho certeza que há algo para aprender no código, sempre há…
Se resolveu usando outra linguagem de programação, MELHOR AINDA, poste para mostrarmos as diferenças das linguagens, seria muito interessante.
Obrigado mais uma vez pela atenção galera, um grande abraço!
[Update]
A Carmen Sachetto que faz o Curso de Ciência da Computação comigo no Mackenzie, fez esta solução em Scheme!!
Muito foda, olha só:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; No peculiar condado de Compiladorshire, todos os telefones têm 6 dígitos. ;
; A companhia telefônica estabelece as seguintes regras sobre os números: ;
; Não pode haver dois dígitos consecutivos idênticos, porque isso é chato; ;
; A soma dos dígitos tem que ser par, porque isso é legal; ;
; O último dígito não pode ser igual ao primeiro, porque isso dá azar. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; função principal ;
; entrada: uma lista com listas ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (Principal lista)
(if (null? lista) '()
(if (= (Verifica (car lista)) 0) (cons (car lista) (Principal (cdr lista)))
(Principal (cdr lista)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Verifica se um numero de telefone é valido ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (Verifica lista)
(if (= (Iguais (cdr lista) (car lista))
(Epar (somaElem lista 0))
(PrimUlt (cdr lista) (car lista) (car lista))
0) 0
(if (not (= (Iguais (cdr lista) (car lista))
(Epar (somaElem lista 0))
(PrimUlt (cdr lista) (car lista) (car lista))
0) ) 1)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; verifica se ultimo elemento é igual a primeiro ;
; se ultimo elemento for igual ao primeiro, retorna 1, senão retorna 0 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (PrimUlt lista primeiro anterior)
(if (null? lista) (comparaElem primeiro anterior)
(PrimUlt (cdr lista) primeiro (car lista))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; compara dois elementos ;
; se forem iguais, retorna 1 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (comparaElem a b)
(if ( = a b) 1
(if (not ( = a b)) 0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; soma elementos de uma lista ;
; retorna soma ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (somaElem lista soma)
(if (null? lista) soma
(somaElem (cdr lista) (+ soma (car lista)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; verifica se um número é par ou não ;
; se for par retorna 0, senão retorna 1 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (EPar numero)
(if (= (remainder numero 2) 0) 0
(if (not (= (remainder numero 2) 0)) 1)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; verifica se há dois dígitos consecutivos idênticos ;
; se houver, retorna 1 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (Iguais lista anterior)
(if (null? lista) 0
(if (= anterior (car lista)) 1
(Iguais (cdr lista) (car lista)))))
Bem legal né ?!?!?!
Paginação em Javascript – Parte 2 Final
Publicado por javiani em Boas práticas, Design Pattern, Javascript, New school, Técnicas de programação em setembro 5, 2010
Olá galera, esse post é a segunda parte sobre paginação em Javascript, dessa vez vou mostrar a segunda forma que havia comentado no post anterior sobre como fazer a paginação.
Na forma anterior, lembram, a paginação tinha como função pegar os elementos e manipulá-los por conta própria, sendo que você como desenvolvedor bastaria passar alguns parâmetros para a classe, ela se encarregaria de fazer tudo sozinha.
É claro que não é a melhor classe do mundo, nem a mais eficiente, para pequenos propósitos ela funciona muito bem, mas não é um bom código por exemplo para um grande número de páginas. Não sei se perceberam, mas o script toda hora, ou melhor, cada vez que é clicado em uma nova página, ele remove todos os nodos das páginas, e remove também todos os nodos da página atual e depois reescreve-os, criando tudo de novo e fazendo o append no documento.
Isso é extremamente dispendioso, extremamente lento no que diz respeito ao dom. Vocês vão perceber isso se passarem um “size” de tamanho muito grande, gerando muitas páginas. Ao clicar numa página que não a atual, vão perceber que o script demora muito tempo pra ser executado, porque precisa apagar todos aqueles nodos e gerá-los de novo. O DOM é muito lento, e da forma como é feita na jsPagination, o torna mais lento ainda.
Há ainda um outro problema, que tenho percebido de uns tempos pra cá no desenvolvimento dos meus scripts. Existe um erro grande ali no design do script. Se é um script que faz paginação, ele só deveria fazer a lógica da paginação. O que você vai fazer com as páginas e como vai montá-las é um outro problema.
A medida que o script cresce ele se torna mais complexo e quanto mais complexo ele fica, mais difícil de dar manutenção e maiores são as chances de surgirem bugs, é inevitável.
Então comecei a “atomizar” mais minhas “classes” fazendo com que elas apenas resolvam o problema que elas precisam resolver e deixar o resto na mão de outra classe. Menos é mais, quanto mais modularizado o código, melhor.
Neste post vou resolver apenas o segundo problema da classe, ela ainda vai adicionar e remover os elementos como fazia anterioremente, mas agora ela fará parte de uma outra classe de implementação, podendo ser melhorada ou estendida independentemente da classe de paginação em questão.
Então a classe atomizada, que apenas contém a lógica da paginação ficou assim:
;(function(namespace){
var Interface = {
actions : function(){
this . pages = function(){
throw new Error('Implemente o método pages em sua classe/objeto')
}
this . list = function(){
throw new Error('Implemente o método list em sua classe/objeto')
}
}
}
var Paginator = {
Class : function( target ){
var json
Interface.actions.apply( this )
this.ready = null
this.open = function(options){
if(!json) json = options
else for(var i in options) json[i] = options[i]
var pages =
Math.floor( json.size / json.show ) +
( ( json.size % json.show ) ? 1 : 0 )
json.page = json.page ? json.page : 1
this.begin? this.begin() :null
for( var i = 1; i <= pages; i++ )
this.list.call? this.list.call(this, i, json) :null
for(
var i = ( (json.page-1 ) * json.show );
i< ( ( json.page-1 ) * json.show ) + 1 + ( json.show - 1 );
i++
){
if( i > json.size - 1 )
break
this.pages.call? this.pages.call(this, i, json) :null
}
this.ready? this.ready() :null
}
}
}
namespace['Paginator'] = Paginator.Class
})(window)
Então essa classe só faria chamadas aos métodos que implementam a população de elementos html. O método open ao ser chamado configuraria as opções passadas, e chamaria os métodos públicos :
this.begin() : Método que é chamado logo antes dos outros métodos da paginação.
this.list() : Método que será chamado n vezes, sendo n o número de páginas.
this.pages() :Método que é chamado n vezes, sendo n o número de elementos à serem mostrados.
this.ready(): Quando tudo estiver sido chamado, ou seja, o ready é um callback.
Os métodos list e pages são chamados no escopo da classe de paginação e com dois argumentos, o primeiro um inteiro contendo o índice da paginação e o segundo contendo as opções passadas na chamada do método open, mantendo as opções privadas.
Então agora, como e onde a paginação vai colocar os elementos, não é mais o cargo do Paginator, é cargo de alguma outra classe que vai herdar Paginator ou compor a mesma.
A classe estando atomizada, ela fica bem mais fácil de ser entendida, mais fácil de dar manutenção, a lógica não fica misturada e código menos emaranhado.
A otimização, implementação de plugins e a forma como serão adicionados os elementos fica a cargo de uma outra classe como eu havia comentado anteriormente, através de herança ou composição.
Eu fiz um código para provar essa parte toda teórica, preferi usar composição para reproduzir o funcionamento da classe antiga que postei na primeira parte do post. Fiz um sigleton que será responsável por instanciar o Paginator, e fazer todo o trabalho com os nodos de inserção e remoção. É claro que o código ficou bem menor do que o antigo código do jsPaginator, mostrado no post anterior, isso não se deve apenas pela atomização da classe, mas porque eu não implementei os plugins que haviam na classe antiga e porque agora estou usando jQuery =), não tenho tanto tempo livre como tinha antigamente então preciso ser mais produtivo, caso contrário postaria um conteúdo novo a cada 5 meses =/.
Então, para finalizar o post e deixá-los em paz, aqui vai o singleton que havia comentado:
;(function(){
var Paginate = {
pages :null,
content :null,
temp_pages :null,
instance :null,
callback :null,
initialize : function( paginacao, content ){
this.pages = $(paginacao)
this.content = $(content)
this.temp_pages = this.pages.eq(0).clone()
this.start()
return this
},
start : function(){
var self = this
var paginator = new Paginator
this.instance = paginator
paginator . begin = function(){
self.content.empty()
self.temp_pages.empty()
}
paginator . pages = function(i, json){
self.content.append( $('<p />').html(i+1) )
}
paginator . list = function(i, json){
var self_paginator = this
var el = $('<a />').attr('href', '#').html(i)
.click(function(){
json.page = i
self_paginator.open()
})
if( json.page == i )
el.addClass('selected')
self.temp_pages.append( $('<li />').append(el) )
}
paginator . ready = function(){
self.pages.each(function(){
$(this)
.empty()
.append( self.temp_pages.children().clone( true ) )
})
self.callback? self.callback(self) :null
}
},
open : function(options){
this.callback = options.callback? options.callback :null
this.instance.open(options)
}
}
// Fim do Singleton
Paginate
.initialize('.paginacao', '#pagina')
.open({
size : 50,
show : 10,
callback : function(){}
})
})()
Eu acho que o Singleton Paginate está bem simples, não vou perder muito tempo explicando como ele funciona, acho que dá pra entender. Se alguém tiver alguma dúvida ou não entendeu alguma parte, poste no comentário, que eu explico =). Ah, críticas também são muito bem vindas.
Abraço galera, obrigado pela atenção e até o próximo post. o/
Paginação em Javascript – Parte 1
Publicado por javiani em Do baú, Idéias, Javascript, Técnicas de programação em agosto 22, 2010
Meus queridos, devido à falta de tempo que estou tendo e um pouco de preguiça, decidi prorrogar o post da parte 2 de MVC em javascript. Eu acho muito interessante esse assunto e não posso expor minha idéia assim de qualquer jeito.
Antes, gostaria de compartilhar um código de paginação em Javascript. Gosto muito desse código porque foi o primeiro projeto que desenvolvi em Javascript quando era apenas um garoto na linguagem. Na época, num compromisso pessoal, decidi que iria sem olhar referencias na internet e sem me poluir com códigos prontos, desenvolver uma “Função” ( na época eu desenvolvia funções rsrs ) que pudesse fazer uma paginação, toda feita em Js.
Então, desenvolvi o algoritmo para a paginação por conta própria, da qual me orgulho até hoje, e depois de muito tempo, refatorei o código pela primeira vez e implementei algumas coisas de forma mais atual no que diz respeito ao desenvolvimento orientado à objetos em Js e etc.
Esta primeira refatoração fiz há algum tempinho, então é claro que é necessário uma nova revisão do código, porque ele está em algumas partes redundante e precisa de melhorias.
Decidi quebrar este post em duas partes, porque fiz uma nova versão deste código usando todos os princípios atuais e usando conceitos novos que adquiri com minha experiência na linguagem. Na segunda parte vou mostrar essa nova forma de fazer e seus prós e contras em contraste com essa versão mais antiga.
De qualquer forma na versão 1.3.2, ele em duas vezes me salvou a vida e de forma perfeita não me deixou na mão em nenhum momento. Simples, prático, rápido, me ajudou em duas aplicações bem complexas e por isso merece estar aqui.
Vamos lá à explicação de como funciona.
Sua idéia é bem simples, ele pega duas listas no html, uma que contem a classe jsP-pages, de onde vai embutir as páginas da paginação, e a outra que contem a classe jsP que contém a sua lista de itens paginados.
Se esses dois elementos não existirem ele os cria.
Então vamos ao código:
var pagination = new jsP.Class('content')
pagination . open({
$ : options,
size : pages.length,
show : 3,
iterate: function(x){
$('<li />').appendTo(this).load( pages[x] )
},
callback: function(){}
})
Então, o objeto é instanciado passando como string o id do objeto pai onde guardará a paginação, um container e ele contem apenas um método público que é o open.
O método open recebe um json com algumas opções para o desenvolvedor, são elas:
$ : um curinga, que guarda as opções dos plugins.
size: O tamanho total da quantidade da informação à ser mostrada.
show: A quantidade de informação à ser mostrada por vez na página.
iterate: A função que é chamada n vezes de acordo com o size e o show passados anteriormente, e é nessa função que você como desenvolvedor vai decidir o que será feito.
O iterate recebe um valor inteiro como argumento que são os índices dos elementos que fazem parte do conteúdo da paginação e é chamado no escopo do elemento html de classe “jsP”.
No exemplo acima, eu tenho um array contendo a url das páginas em ajax que desejo carregar. Então, se o array tiver um length igual à 6, por exemplo, haveriam duas páginas e o iterate ia ser chamado 3 vezes na primeira página e 3 vezes na segunda, variando o x de 0 à 2 no nosso caso.
O jQuery ali é bem simples, para cada página ele irá criar x li’s cujo conteúdo é carregado por ajax na url do array pages.
Importante saber é que se você tem 50 páginas, o jsPagination vai chamar apenas o iterate da página atual. Então se você clica na página 2, ele chamará o iterate de novo, x vezes. Aí está o ponto forte da classe, e até hoje eu me espanto, porque a lógica foi bem desenvolvida sendo na época em que estava engatinhando no ramo da programação. Mesmo com uma experiência maior de hoje, pouquíssimas coisas tenho desenvolvido com tamanha inteligência na lógica, claro, relativamente falando.
Voltando, o callback é uma função que é chamada no momento que a página atual da paginação é chamada e é opcional, deixei vazio ali apenas para lembrar que ele existe.
Como havia falado, existe o curinga “$” onde você configura os plugins do jsPaginator, que tem por padrão 3 plugins:
map: que recebe uma funcão que é executada para cada elemento listado na página atual.
currency: responsável por colocar uma classe na listagem de páginas da página atual. Pode ser passado um booleano true, onde o jsPagination vai colocar uma classe padrão, ou pode ser passado um json contendo uma chave “classe” e um valor do tipo string contendo o nome da classe desejado pelo programador.
nextPrev: responsável por adicionar mais dois links na lista das páginas da paginação, próximo e anterior.
Ele pode ter apenas um booleano com valor true ou false para ser usado. Ou pode receber um json contendo 5 atributos:
prev : A string que aparecerá na listagem indicando a página anterior. por default é “Anterior”.
next : A string que aparecerá na listagem indicando a página posterior. por default é “Próximo”.
clPrev : A string com o nome da classe do elemento prev. por default é “anterior”.
clNext : A string com o nome da classe do elemento posterior. por default é “proximo”.
hidePages: Recebe um booleano que define se os números das páginas devem ser ocultos, apenas permanecendo o next e prev.
Bom, sabendo tudo isso, já dá pra mostrar o resto do código de exemplo que mostrei lá em cima no começo do post, para fazer mais sentido:
var pages = [
"pages/conteudo1.htm",
"pages/conteudo2.htm",
"pages/conteudo3.htm",
"pages/conteudo4.htm",
"pages/conteudo5.htm"
]
var options = {
map:function(element){
$(element)
.css({
'border': '1px solid red',
'width' : '300px',
'height': '50px',
'margin': 'auto'
})
},
currency: true,
nextPrev: { hidePages: false}
}
var pagination = new jsP.Class('content')
pagination . open({
$ : options,
size : pages.length,
show : 3,
iterate: function(x){
$('<li />').appendTo(this).load( pages[x] )
},
callback: function(){}
})
Viram? o size seria o tamanho do array, show mostra a quantidade de elementos que quero mostrar e iterate é o corpo da função que irá criar os elementos dinamicamente =). Acho que nesse código ficou mais clara a forma como se usa o curinga, é apenas um objeto contendo opções dos plugins. Coloquei este objeto em uma variável chamada options para não poluir a chamada do método da classe.
É bem simples de ser usado…
Embora ele desempenhe bem o seu papel, ele é lento, pois mexe no DOM o tempo todo, removendo o conteúdo das duas listas e adicionando novo conteúdo sempre que é paginado.
Mostrarei como contornar este problema na próxima parte do post.
Bom, vou deixar aqui no post a última versão da classe, que está na 1.3.2.
jsPagination.zip
E para quem quer ver um exemplo prático,tem um aqui:
/jsPagination
Neste link usei um Css para a galeria de imagens, o Css Hoverbox Image Gallery que vi neste post do Danilo no TidBits.
No demo, o jsPagination ainda está numa versão mais antiga, mas suas chamadas e métodos funcionam da mesma forma, mantendo a retrocompatibilidade. Então, se achar melhor entender o código do exemplo prático, fique à vontade =).
Mas pegue o código fonte daqui que é melhor, vai estar sempre atualizado =).
Um grande abraço.
Design Pattern Observer em Javascript
Publicado por javiani em Design Pattern, Javascript, OOP, Técnicas de programação em maio 22, 2010
E aí galerinha do mal, é nóis again.
É engraçado que sempre ao se começar um post eu fico horas só pensando na saudação rsrss. É difícil inovar.. mas ainda que seja repetitivo é melhor que aquele “Bom dia” que alguns colocam né ? Caray, quem garante que a criatura que vai ler, estará lendo de manhã ? =/
Deixando de lado as viadagens, vamos ao que nos interessa de verdade que é Javascript na veia irmão.
Saber sobre os Design Patterns e onde aplicar é o que diferencia os programadores homens dos programadores meninos. Às vezes não é tão trivial identificar qual design pattern usar para um determinado problema e não é possível saber todos decor.
E é por isso que estou postando bastante sobre esse assunto, para termos uma referência na nossa linguagem sobre isso.
Então, o que é um Observer e quando usar ?
Tá de pé? Então senta.
O padrão Observer é um padrão do tipo comportamental, no qual um objeto chamado “Subject” contém uma lista de dependentes, chamados Observers. Os Subjects então notificam para esta lista de Observers automaticamente quando algum estado modifica, geralmente chamando um de seus métodos.
É geralmente usado para implementar sistemas distribuidos com delegação de eventos.
Fonte: Wikipédia
Este padrão usa alguns conceitos de outros design patterns, como Interfaces e Visitor. Lembram que eu falei pra estudar bastante sobre Interfaces pois todos os outros design patterns utilizam este conceito ? Este padrão utiliza interfaces e utiliza também uma idéia do padrão Visitor que se chama inversão de métodos. O Visitor não é pré-requisito para entender o Observer, mas é interessante saber que muitos design patterns utilizam conceitos de outros para se compor.
Basicamente é isso, quando temos um objeto que ao ser alterado uma propriedade dele, outros objetos precisam saber que este objeto foi alterado e precisam invocar um método deles assim que isso acontecer.
Em Javascript o Observer pode ser amplamente usado, porque lidamos com eventos mais do que qualquer coisa em Javascript.
É bastante necessário por exemplo, quando temos uma tela que é dinâmica a nível de Js, com paginação e tal. Quando por exemplo vamos para a próxima página da paginação, criamos novos elementos de acordo com novos dados e estes elementos perdem todos os eventos que os outros tinham na página anterior. Temos que ficar atualizando os eventos dos objetos quando nossa tela é alterada. O Pattern Observer pra isso é uma mão na roda.
Mas eu vou dar um exemplo bem simples, talvez não muito usual, mas para ser facilmente fixado e absorvido por vocês meus caros leitores.
Primeiro, nosso diagrama de classes do Pattern.
![]()
Agora nosso problema.
Temos dois objetos, Tela e Ponto, onde o objeto Tela será responsável por mostrar o objeto Ponto na tela. Porém o objeto Ponto tem um estado, que é a posição dele na tela, ele tem um atributo x e um atributo y que são suas coordenadas.
Sempre que eu alterar uma das suas coordenadas ( x ou y ) o objeto Tela precisa saber que isso aconteceu e atualizar sua posição na tela.
Portanto, vamos resolver esse problema usando o Pattern Observer.
Como o objeto Tela precisará observar o objeto Ponto, então será este nosso Observer. Então Ponto será nosso Subject.
Usando nosso “Javiani way” de construir Classes, o padrão Observer é construido da seguinte maneira:
var Interface = {
Observer : function(){
this.update = function(o){
throw new Error('Implemente Observer.update()')
}
},
Subject : function(){
var observers = {}
this.add_observer = function(o){ observers[o] = o }
this.rem_observer = function(o){ delete observers[o] }
this.notify = function(){
for(var x in observers)
observers[x].update(this)
}
}
}
Lembrando que Observer e Subject são interfaces, não classes. Portanto, teremos duas classes, Tela e Ponto, na qual cada uma delas IMPLEMENTARÁ um tipo de interface.
A interface acima Observer, terá um método obrigatório, que será o update, que fará alguma coisa com o objeto passado como argumento para o método. Este método será chamado pelo objeto Subject, notificando o Observer que um estado do Subject foi alterado.
A interface Subject, terá uma lista contendo todos os observers que um objeto Subject terá. E também terá 3 métodos. Um para adicionar um observer à lista, outro pra remover um observer da lista, e outro para passear sobre a lista e avisar à todos os seus Observers registrados que alguma coisa foi alterada, chamando o método update dos Observers passando o objeto que foi alterado. No nosso caso este objeto seria um objeto do tipo Ponto.
Vamos as classes Tela e Ponto então.
Classe ponto com um setter/getter para os pontos x e y. Ela implementará a interface Subject e notificará aos seus Observers que este ponto foi alterado no momento em que for setado um valor novo para x ou para y:
var Ponto = {
Class : function(){
Interface.Subject.apply( this )
var x = 0;
var y = 0;
this.x = function(i){
if(i){
x = i
this.notify()
}
else return x
}
this.y = function(i){
if(i){
y = i
this.notify()
}
else return y
}
},
New : function(){ return new this.Class() }
}
Agora a Classe Tela, que implementará Interface.Observer, terá um método update obrigatório que será chamado pelo Subject, mandando o subject como argumento.
var Tela = {
Class : function(object){
Interface.Observer.apply(this)
var html = object
this.desenha = function(o){
this.desenha[o.constructor].apply(this, arguments)
}
this.desenha[ Ponto.Class ] = function(o){
html.style.left = o.x() + 'px'
html.style.top = o.y() + 'px'
}
this.update = function(o){ this.desenha(o) }
},
New : function(object){ return new this.Class(object) }
}
Chamando um pouco a atenção à forma como montei a Tela, primeiro implementei a interface Observer, o construtor da classe Tela recebe como parâmetro um objeto html e o guarda numa variável privada. Esta classe tem um método sobrecarregado this.desenha. Por que imagino que este objeto Tela não desenhará apenas um ponto mas outros elementos futuramente. ( Se não conhece esta forma de overload, veja o artigo anterior à este )
Então o método sobrecarregado desenha que recebe um ponto, apenas definirá a posição em pixel, no atributo Css left, e top considerando que este objeto html esteja em posição absoluta ou relativa.
O método update será chamado pelo objeto Ponto assim que se alterar qualquer uma das suas coordenadas, isso será feito pelo método notify.
Pronto, agora vamos instanciar os objetos e fazer o padrão Observer funcionar:
window.onload = function(){
var dot = document.getElementById('dot')
var tela = Tela.New(dot) // Ou new Tela.Class(dot)
var ponto = Ponto.New() // Ou new Ponto.Class
ponto.add_observer(tela)
ponto.x(490)
ponto.y(80)
}
Aí está, pegamos um elemento html na tela, o passamos como argumento para o construtor de Tela e instanciamos o objeto tela.
Logo depois disso instanciamos o objeto ponto, adicionamos um observer à ele, que será o objeto tela.
Assim que setarmos um novo valor para x ou para y, o método notify em ponto será chamado, este fará uma chamada do método update de todos os observers registrados passando o objeto ponto como argumento.
O objeto tela ao ser notificado que houve uma alteração em ponto, desenhará na tela a nova posição do ponto. =)
Sacaram ?
Deixei disponível um exemplo deste código, abram-o no firefox de preferência, e setem um novo valor para o ponto, para x ou para y, ou para os dois no console. Deixei públicos os objetos tela e ponto.
Ahh, para não perder o costume de Cientista, aqui vai uma funçãozinha pra fazer o ponto animado =)
var t = 0.0
var fx = function(x, r, t){ return (x + r*Math.cos(t/2)) }
var fy = function(x, r, t){ return (x + r*Math.sin(t/2)) }
setInterval(function(){
if( t == 12 ) t = 0
ponto.x( fx(screen.width/2, 40, t) )
ponto.y( fy(screen.height/2 - 180, 40, t) )
t+=.5
}, 100)
Só colar no firebug e executar =D.
Um grande abraço galera, valeu!
Overload em Javascript
Publicado por javiani em Design Pattern, Idéias, Javascript, OOP, Técnicas de programação em abril 17, 2010
E ae gente, é nóis de novo =).
Eu queria falar desta vez sobre a técnica conhecida como overloading ou sobrecarga de métodos mas em javascript. Sabemos que a linguagem até a versão 1.5 não oferece isso por padrão mas oferece uma série de coisas que nos ajudam a contornar essa falta.
O que seria uma sobrecarga ou um overloading? Em Ciência da Computação, overloading é uma “feature” da linguagem que nos permite criar uma série de métodos de mesmo nome mas que se diferem uma das outras pelo número de parâmetros, pelo tipo dos parâmetros ou pelo seu retorno, ou os três ao mesmo tempo.
Se você quisesse fazer um método setter e getter, você estaria extremamente inclinado à fazer isso:
string getNome(){ return nome; }
void setNome(string n){ nome = n }
Não há nada errado no código acima, mas não estaríamos usando as técnicas de programação OO se fizessemos isso. Um problema que vejo nessa implementação é que sua aplicação tende a ficar com muitos métodos de nomes diferentes, aumentando consideravelmente a curva de aprendizado de um programador terceiro que pretende usar esse código. Ao invés de ter que aprender 10 nomes de métodos, o programador teria de aprender 20, utilizando como base o esquema feito acima.
Uma maneira elegante então seria algo do tipo:
string nome(){ return Nome; }
void nome(string n){ Nome = n; }
Desta forma você só precisaria conhecer o método “nome”. Se você não passar nada ele te retorna o nome. Se você passar alguma coisa, significa que você está querendo “setar” o nome.
Em C++ e Java é possível fazer o que fiz acima sem maiores preocupações. A própria linguagem oferece esse recurso para que possa sobrecarregar os métodos.
Mas e em Javascript ?
Eu já vi várias e excelentes implementações de sobrecarga em vários blogs brasileiros e gringos, mas o que me incomodava nessas implementações é que sempre usavam algum iterador para resolver o problema. Sempre tinha um “for” ali ou algo do tipo. Seguindo a mesma filosofia dos outros posts, pretendo usar um método de sobrecarga que utilize os próprios recursos da linguagem, assim como fiz com Herança e Classes em um post mais antigo.
Como fazer?
Sabemos que o compilador ou interpretador precisa diferenciar os métodos de mesmo nome de duas formas. Primeiro a quantidade de parâmetros que passamos para a função. A segunda é pelo tipo de parâmetro que passamos.
Eu escolhi neste método usar um ou outro, não misturar os dois ao mesmo tempo porque até agora nunca precisei usar este tipo de sobrecarga em Javascript, então é código escrito sem necessidade. Então mãos a massa.
Sobrecarga – Número de parâmetros
Mais uma vez vou utilizar o método “apply” do Js para resolver essa questão, como a linguagem mexe muito com prototipos e construtores, nada mais inteligente do que usar esse método de escopo. A idéia que tive foi usar uma função como uma função e ao mesmo tempo como um objeto que segura suas próprias propriedades.
var Person = {
Class: function(){
//Private
var name = ''
//Public
this.name = function(){
return this.name[ arguments.length ].apply( this, arguments )
}
this.name[0] = function(){ return name }
this.name[1] = function(n){ name = n }
this.name[2] = function(a, b){ name = a + " " + b }
}
}
Aqui eu crio uma função ( método ) com nome “name” e crio mais 3 propriedades dela que são: 0, 1 e 2. São números, mas ainda sim são propriedades do método name. O que a função principal vai fazer é ver quantos parâmetros ela tem e vai chamar a sua propriedade correspondente, que nada mais é que outro método que vai ser executado no mesmo contexto que a name e terá os mesmos parâmetros que a name.
Logo:
var Edu = new Person.Class
Edu .name() // ''
Edu .name('Eduardo')
Edu . name() // "Eduardo"
Edu . name('Eduardo', 'Ottaviani')
Edu . name() // "Eduardo Ottaviani"
Não é porque eu tive essa idéia não, mas eu considero esta a forma mais elegante de sobrecarga de operadores até agora. Talvez não seja a mais eficiente, não testei sua velocidade em relação as outras técnicas. Se for mais lenta, creio que não seja perceptível.
Mas e na hora de sobrecarregar a função através do tipo do parâmetro passado? Como fazer?
Sobrecarga – Tipo de parâmetro
Bom, os métodos existentes até agora consistem em checar o objeto através do typeof, então faz-se um switch onde caso o objeto seja uma string faz uma coisa, se for número faz outra etc.
Há duas formas de saber o que é um objeto, uma é usar o typeof que considero ruim.
Ruim porque ela não retorna exatamente o que deseja. Um caso clássico é dar um typeof []. Retornaria um “object”. Um array é um objeto mas não é apenas um objeto, ele é um objeto do tipo Array.
A outra forma é olhar seu construtor. Esta sim é uma excelente forma de se saber qual o tipo de um objeto. Por que? Porque Javascript define os construtores como as “Classes” dos objetos, portanto uma string é definida por um construtor String, um número é definido por um construtor Number e assim por diante. Em javascript, é o construtor que define o tipo do objeto.
Quando você entende isso, você entende tudo sobre a mecânica da linguagem Javascript, e percebe que há muito mais tipos na linguagem do que pensava.
Por exemplo: (Ruim)
typeof new Date() // object typeof new Person.Class // object typeof [] // object
Exemplo : ( Bom )
new Date().constructor == Date // true var edu = new Person.Class; edu.constructor == Person.Class // true [].constructor == Array // true
Viram? é o construtor que define o tipo do objeto, então mesmo o Person.Class é um tipo, e pode ser definido como o tipo “Person.Class” e não um object.
Dada essa explanação sobre os tipos, como eu faria para implementar um overload de tipos ?
Desta forma:
var Person = {
Class: function(){
this.type = function(){
return this.type[
arguments[0].constructor
].apply(this, arguments)
}
this.type[String] = function(){
alert('String')
}
this.type[RegExp] = function(){
alert('Regex')
}
this.type[Person.Class] = function(){
alert("Person")
}
}
}
A lógica aqui é a mesma da lógica de sobrecarga por quantidade de argumento, só que desta vez eu armazeno no método type outros métodos como propriedades e crio uma relação de tipo entre eles.
Ao executar o método type() passando um objeto como parâmetro, essa função tentará chamar uma propriedade com o valor de construtor igual ao do objeto do parâmetro. Achando esta propriedade, ela será executada no mesmo contexto e usando os mesmos argumentos que a função type mãe.
O interessante é que você acaba descobrindo que há muito mais tipos de objetos no Js, porque usando o typeof você não consegue diferenciar todos eles.
Olha só:
var Edu = new Person.Class
Edu . type ("Eduardo") // alert - "String"
Edu . type (/Eduardo/) // alert - "Regexp"
Edu . type (Edu) // alert - "Person"
Eu poderia também sobrecarregar para um outro método que reconheça uma Date(), usando o mesmo princípio dos outros métodos.
É possível ainda chamar os métodos de tipo ou número diretamente, sem precisar chamar o pai:
Edu . type[RegExp]() Edu . name[2](a, b)
Sinceramente eu prefiro mil vezes usar os recursos da própria linguagem à usar uma função a parte que itere sobre os objetos, faça comparações ou coisas do tipo. Eu estudei alguns tipos de sobrecarga de blogs gringos, caso vocês não tenham gostado da minha forma, eu recomendo usar esses outros métodos de sobrecarga:
John Resig (jQuery) : http://ejohn.org/blog/javascript-method-overloading/
http://webreflection.blogspot.com/2010/02/javascript-overload-patterns.html
É isso galera. Até o próximo post. Comentários são muito bem vindos.
Aqueleabrassss
