Meus amigos deveriam ver isto:
InfoQ: Responsive Design (Kent Beck)
(infelizmente, sem tempo para escrever a respeito...)
Páginas
Showing posts with label designpattern. Show all posts
Showing posts with label designpattern. Show all posts
Tuesday, October 25, 2011
Saturday, January 17, 2009
TDD em cantigas de roda - parte 2
Na semana passada comecei a falar sobre abordar outros tipos de situação, não diretamente relacionadas com programação, através de testes, fazendo uma espécie de exercício de abstração.
Na última quarta, no Dojo Rio, eu conversei com o pessoal sobre o assunto. Ao invés de ter sido o aquecimento, como proposto pelo Falcão, foi apenas um bate-papo após a nossa retrospectiva. Como eu imaginava, estranharam um pouco, porém acho que consegui passar a idéia num tom sério o bastante para não me ignorarem :D
Voltando ao problema iniciado na parte 1, parei justamente após escrever um teste exercitando a presença de objetos em namespaces diferentes. Por enquanto temos o seguinte, com o último teste falhando:
Notem que eu adicionei o objeto
Agora vamos criar uma interface para disparar e capturar eventos. Primeiro vou definir um
Foi um passo bem longo. Tirei a idéia central daqui, porém no caso em questão todos os seres tem que ouvir os eventos de todos os outros, enquanto no exemplo da Wikipedia diversos Listeners ouvem eventos de apenas um único Subject.
Basicamente, o que aconteceu agora é que ao adicionar um ser (Gato, Dona Chica, etc) a uma vila este ser sabe a que vila ele pertence.
Assim sendo, quando um ser disparar um evento, ele avisa a vila, que por sua vez propaga o evento para todos os seres nela contidos.
Rodando os testes, temos uma supresa: nosso segundo teste
Por que será? Bem, uma coisa é certa, usando testes automatizados nós ganhamos a segurança de caminhar sem medo, pois estamos protegidos das regressões. O teste protege o código, e o que muitos não percebem: o código protege o teste!
O que aconteceu não é que o código está errado, e sim o teste. Desde que inventamos o conceito de vila, um gato e uma dona chica precisam estar na mesma vila para que um ouça o outro. Logo, precisamos consertar o primeiro teste para ficar bem parecido com o segundo:
E finalmente estamos com tudo passando!
Tem mais um detalhe, o que acontece se quem berrar for a
Para ficar mais claro, não vou fazer a
Note que eu utilizei uma nova forma de adicionar seres a uma vila, e com isso precisamos refatorar:
E, para minha surpresa, os testes passaram! Isso quer dizer que tem algo de errado, pois eu esperava que o teste falhasse. Bem, não demorou muito para ver que eu não estava definindo a vila
Agora sim o teste falhou, menos mal :D
Vamos melhorar nossa comunicação de eventos, podemos passar a informação de quem está disparando o evento. Desta forma, podemos distinguir entre berros de gatos e cachorros...
E ajustamos para passar
Testes passando!
Podemos então refatorar. O que me incomoda bastante é a duplicação de código — as classes
Então:
Testes OK.
Já estou bastante satisfeito em saber que, quando o gato berrar, dona Chica irá certamente se admirar...
Mas ainda quero poder atirar o pau no gato, sem causar sua morte...
Este post já está demasiadamente longo, então é melhor continuar depois.
Na última quarta, no Dojo Rio, eu conversei com o pessoal sobre o assunto. Ao invés de ter sido o aquecimento, como proposto pelo Falcão, foi apenas um bate-papo após a nossa retrospectiva. Como eu imaginava, estranharam um pouco, porém acho que consegui passar a idéia num tom sério o bastante para não me ignorarem :D
Voltando ao problema iniciado na parte 1, parei justamente após escrever um teste exercitando a presença de objetos em namespaces diferentes. Por enquanto temos o seguinte, com o último teste falhando:
# -*- coding: utf-8 -*-
u"""
paunogato.py - Implementação usando TDD da cantiga de roda "Atirei o Pau
no Gato", seguindo a idéia de Dojo Lúdico proposta por
Jorge Falcão.
Referências:
* http://lifeatmymind.blogspot.com/2009/01/tdd-em-cantigas-de-roda-parte-1.html
* http://lifeatmymind.blogspot.com/2009/01/tdd-em-cantigas-de-roda-parte-2.html
by Rodolfo Carvalho
2009/01/08
"""
import unittest
class DonaChica(object):
admirada = False
class Gato(object):
def berrar(self):
DonaChica.admirada = True
class Vila(object):
def adicionar(self, obj):
pass
class TestAtireiOPauNoGato(unittest.TestCase):
def test_admiracao(self):
dona_chica = DonaChica()
gato = Gato()
self.assertFalse(dona_chica.admirada)
gato.berrar()
self.assertTrue(dona_chica.admirada,
"A dona Chica nao se admirou com o berro do gato!")
def test_admiracao_com_outro_gato(self):
dona_chica = DonaChica()
gato = Gato()
outro_gato = Gato()
vila = Vila()
vila.adicionar(dona_chica)
vila.adicionar(gato)
self.assertFalse(dona_chica.admirada)
outro_gato.berrar()
self.assertFalse(dona_chica.admirada,
"A dona Chica admirou-se com o berro de outro gato!")
if __name__ == "__main__":
unittest.main()
Notem que eu adicionei o objeto
Vila
e seu método adicionar
(vazio).Agora vamos criar uma interface para disparar e capturar eventos. Primeiro vou definir um
Ser
, que seria uma abstração acima de DonaChica
e Gato
, e ajustar as heranças:class Ser(object):
def __init__(self):
self._vila = None
def definir_vila(self, vila):
self._vila = vila
def notificar(self, evento):
print self, "recebeu o evento <%s>" % (evento,)
class DonaChica(Ser):
def __init__(self):
super(DonaChica, self).__init__()
self.__admirada = False
@property
def admirada(self):
return self.__admirada
def notificar(self, evento):
super(DonaChica, self).notificar(evento)
if evento == "berro":
self.__admirada = True
class Gato(Ser):
def berrar(self):
if self._vila is not None:
self._vila.notificar("berro")
class Vila(object):
def __init__(self):
self._seres = []
def adicionar(self, ser):
ser.definir_vila(self)
self._seres.append(ser)
def notificar(self, evento):
for ser in self._seres:
ser.notificar(evento)
Foi um passo bem longo. Tirei a idéia central daqui, porém no caso em questão todos os seres tem que ouvir os eventos de todos os outros, enquanto no exemplo da Wikipedia diversos Listeners ouvem eventos de apenas um único Subject.
Basicamente, o que aconteceu agora é que ao adicionar um ser (Gato, Dona Chica, etc) a uma vila este ser sabe a que vila ele pertence.
Assim sendo, quando um ser disparar um evento, ele avisa a vila, que por sua vez propaga o evento para todos os seres nela contidos.
Rodando os testes, temos uma supresa: nosso segundo teste
test_admiracao_com_outro_gato
passa, mas sofremos uma regressão — o test_admiracao
falhou!Por que será? Bem, uma coisa é certa, usando testes automatizados nós ganhamos a segurança de caminhar sem medo, pois estamos protegidos das regressões. O teste protege o código, e o que muitos não percebem: o código protege o teste!
O que aconteceu não é que o código está errado, e sim o teste. Desde que inventamos o conceito de vila, um gato e uma dona chica precisam estar na mesma vila para que um ouça o outro. Logo, precisamos consertar o primeiro teste para ficar bem parecido com o segundo:
def test_admiracao(self):
dona_chica = DonaChica()
gato = Gato()
vila = Vila()
vila.adicionar(dona_chica)
vila.adicionar(gato)
self.assertFalse(dona_chica.admirada)
gato.berrar()
self.assertTrue(dona_chica.admirada,
"A dona Chica nao se admirou com o berro do gato!")
E finalmente estamos com tudo passando!
Tem mais um detalhe, o que acontece se quem berrar for a
dona_chica
? Ela não pode ser admirar com o próprio berro, mas sim com o berro de um gato!Para ficar mais claro, não vou fazer a
dona_chica
berrar, mas sim adicionar um novo ser na brincadeira, um cachorro. Se um cachorro berrar, bem, a dona_chica
continua no mesmo estado de não-admiração...class Cachorro(Ser):
def berrar(self):
if self._vila is not None:
self._vila.notificar("berro")
...
def test_admiracao_com_um_cachorro(self):
dona_chica = DonaChica()
gato = Gato()
cachorro = Cachorro()
vila = Vila()
vila.adicionar(dona_chica, gato, cachorro)
self.assertFalse(dona_chica.admirada)
cachorro.berrar()
self.assertFalse(dona_chica.admirada,
"A dona Chica admirou-se com o berro do cachorro!")
Note que eu utilizei uma nova forma de adicionar seres a uma vila, e com isso precisamos refatorar:
class Vila(object):
def __init__(self):
self._seres = []
def adicionar(self, ser, *outros_seres):
ser.definir_vila(self)
self._seres.append(ser)
if outros_seres:
self._seres.extend(outros_seres)
...
E, para minha surpresa, os testes passaram! Isso quer dizer que tem algo de errado, pois eu esperava que o teste falhasse. Bem, não demorou muito para ver que eu não estava definindo a vila
outros_seres
. O refactoring ficou assim:def adicionar(self, *seres):
for ser in seres:
ser.definir_vila(self)
self._seres.append(ser)
Agora sim o teste falhou, menos mal :D
Vamos melhorar nossa comunicação de eventos, podemos passar a informação de quem está disparando o evento. Desta forma, podemos distinguir entre berros de gatos e cachorros...
class DonaChica(Ser):
...
def notificar(self, evento, origem):
super(DonaChica, self).notificar(evento, origem)
if evento == "berro" and isinstance(origem, Gato):
self.__admirada = True
E ajustamos para passar
self
quando cachorros e gatos berram:class Gato(Ser):
def berrar(self):
if self._vila is not None:
self._vila.notificar("berro", self)
class Cachorro(Ser):
def berrar(self):
if self._vila is not None:
self._vila.notificar("berro", self)
Testes passando!
Podemos então refatorar. O que me incomoda bastante é a duplicação de código — as classes
Cachorro
e Gato
tem o mesmo código! Não vejo razão para um ser qualquer não poder gritar... a não ser que seja de uma subclasse Mudo(Ser)
...Então:
class Ser(object):
...
def berrar(self):
if self._vila is not None:
self._vila.notificar("berro", self)
class Gato(Ser):
pass
class Cachorro(Ser):
pass
Testes OK.
Já estou bastante satisfeito em saber que, quando o gato berrar, dona Chica irá certamente se admirar...
Mas ainda quero poder atirar o pau no gato, sem causar sua morte...
Este post já está demasiadamente longo, então é melhor continuar depois.
Wednesday, January 7, 2009
TDD em cantigas de roda - parte 1
Bem, resolvi implementar minha própria cantiga usando TDD.
Pode parecer estranho, mas é apenas uma variação do que o Falcão fez e que eu comentei no post anterior.
Vamos então começar com a cantiga, "Atirei o Pau no Gato":
Olhada com os olhos certos, essa cantiga infantil de aparência simples, que vem sendo ensinada de forma alterada para evitar a extinção dos gatos domésticos, pode ser uma bela fonte para análise e discussão. Possivelmente minha abordagem não será a melhor, porém essa não é a idéia. É apenas a minha expressão de como encarei as coisa no ônibus voltando para casa...
O primeiro teste:
E... hmm... precisamos de uma implementação para fazer os testes passarem! Então surge o primeiro "problema". Eu já escrevi os outros testes que acredito serem pertinentes a cantiga, mas não tinha escrito nada de implementação.
E o primeiro estalo foi: "o berro do gato tem que disparar um evento e a DonaChica é listener... quando o gato berra ela admira-se".
Como fazer essa comunicação? Veja que legal, chegamos num ponto — ainda bem no início da discussão — em que podemos aplicar um padrão de projeto (design pattern), o Observer pattern. Mais uma vez deixo claro que está é minha implementação, e faço o que quiser com ela: é minha!!!
Ah, mas... ok, antes de fazer algo super bonito, que tal fazermos o teste passar?
E rodando os testes: verde!
Eu não gosto desse código, alterar um atributo de classe não faz sentido, já que só uma instância de
Podemos explicitar isso fazendo mais um teste.
Queremos que possam existir diversas Donas Chicas espalhadas pelo mundo, ou em diferentes cidades, ou mesmo em dimensões distintas. O mesmo vale para os gatos... nada de singletons. Logo, para possibilitar diversos "namespaces" pensei em, ao invés de criar mais uma nível de objeto, e enfiar o
Então vamos adicionar a
E como esperávamos, vermelho nos testes!
Vamos implementar:
Mas não agora, em breve!
Hora de dormir.
Pode parecer estranho, mas é apenas uma variação do que o Falcão fez e que eu comentei no post anterior.
Vamos então começar com a cantiga, "Atirei o Pau no Gato":
Atirei o pau no ga-to-to
mas o ga-to-to
não morreu-rreu-rreu
dona chica-ca
adimirou-se-se com o berro
que o gato deu
miiiiaaaaaaauuuuuuuu.
Olhada com os olhos certos, essa cantiga infantil de aparência simples, que vem sendo ensinada de forma alterada para evitar a extinção dos gatos domésticos, pode ser uma bela fonte para análise e discussão. Possivelmente minha abordagem não será a melhor, porém essa não é a idéia. É apenas a minha expressão de como encarei as coisa no ônibus voltando para casa...
O primeiro teste:
class TestAtireiOPauNoGato(unittest.TestCase):
def test_admiracao(self):
dona_chica = DonaChica()
gato = Gato()
self.assertFalse(dona_chica.admirada)
gato.berrar()
self.assertTrue(dona_chica.admirada)
E... hmm... precisamos de uma implementação para fazer os testes passarem! Então surge o primeiro "problema". Eu já escrevi os outros testes que acredito serem pertinentes a cantiga, mas não tinha escrito nada de implementação.
E o primeiro estalo foi: "o berro do gato tem que disparar um evento e a DonaChica é listener... quando o gato berra ela admira-se".
Como fazer essa comunicação? Veja que legal, chegamos num ponto — ainda bem no início da discussão — em que podemos aplicar um padrão de projeto (design pattern), o Observer pattern. Mais uma vez deixo claro que está é minha implementação, e faço o que quiser com ela: é minha!!!
Ah, mas... ok, antes de fazer algo super bonito, que tal fazermos o teste passar?
class DonaChica(object):
admirada = False
class Gato(object):
def berrar(self):
DonaChica.admirada = True
E rodando os testes: verde!
Eu não gosto desse código, alterar um atributo de classe não faz sentido, já que só uma instância de
DonaChica
, no caso dona_chica
, ficou admirada.Podemos explicitar isso fazendo mais um teste.
Queremos que possam existir diversas Donas Chicas espalhadas pelo mundo, ou em diferentes cidades, ou mesmo em dimensões distintas. O mesmo vale para os gatos... nada de singletons. Logo, para possibilitar diversos "namespaces" pensei em, ao invés de criar mais uma nível de objeto, e enfiar o
gato
e a dona_chica
lá dentro, seria interessante ter uma Vila (vamos supor que nossa Dona Chica mora em uma, e lá existe ao menos um gato). A Vila nada mais é que o gerenciador de eventos, fazendo então a junção do gato e da dona Chica em um ambiente em que um pode interagir com o outro, e outros gatos, outras donas chicas, simplesmente não afetam/ são afetados.Então vamos adicionar a
dona_chica
e o gato
a uma Vila e descrever o que acontece caso um gato externo berre:def test_admiracao_com_outro_gato(self):
dona_chica = DonaChica()
gato = Gato()
outro_gato = Gato()
vila = Vila()
vila.adicionar(dona_chica)
vila.adicionar(gato)
self.assertFalse(dona_chica.admirada)
outro_gato.berrar()
self.assertFalse(dona_chica.admirada,
"A dona Chica admirou-se com o berro de outro gato!")
E como esperávamos, vermelho nos testes!
Vamos implementar:
Mas não agora, em breve!
Hora de dormir.
Subscribe to:
Posts (Atom)