Design Patterns: Observer em Ruby

Posted on October 25, 2009

1


Integração é um dos maiores desafios na construção de um sistema, cada
alteração causada por determinada parte do código pode causar mudanças
no sistema com um todo. Em uma planilha, ao alterar uma célula, vários
outros objetos podem mudar, como uma atualização de uma conta ou
recriar um gráfico. Manter esse tipo de integração não é simples, e alguns
padrões são voltados para diminuir esse tipo de acoplamento.

O Rails já implementa uma forma de atacar esse problema no próprio
Active Record, fazendo callbacks na classe ActiveRecord::Observer.

O código abaixo tenta demostrar uma implementação e solução desse problema.
Temos uma classe Funcionário e queremos manter avisados
todos os objetos do sistema que se interessem em modificações nele.

Picture 1

Agora vamos criar a classe Pagamento. Essa classe imprime um log a cada
vez que o salário do funcionário for alterado. Para isso, vamos laterar o
initialize da classe Funcionario para receber também um obejeto do tipo
Pagamento. Cada vez que o salário for alterado, agora chamanos o método
update do pagamento.

Picture 2

Mas e quando mais uma classe precisar ser informada das alterações no Funcionário ?
O código acima deixa as classe muito acompladas. Uma forma de diminnuir essa dependência pode ser criar uma lista de “observadores” ao criar o funcionário. Dessa forma poderiamos ingormar todos os objetos da lista quando uma alteração no funcionário for feita. Apenas precisamos aqui estabelecer uma interface, ou seja, todos os objetos que estão observando o funcionário terão o método update para serem notificados.

Picture 3

Agora podemos facilmente adicionar um novo observador, veja:

Picture 4

Nossa implementação agora está boa, mas e quando precisarmos observar um outro objeto do sistema ? Precisariamos então copiar a mesma idéia nesse outro objeto.

Um objeto que está sendo observado é chamado de Subject. Podemos resolver isso criando uma classe Subject que contém os métodos que adicionamos no Funcionario.

Picture 5

E usar herança nos objetos considerados Subject:
Picture 6

HERANÇA DE NOVO ! ? ! ?

alone

Vamos dar preferência a composição ao invés da herança visando diminuir o acoplamento entre nossas classes. Para isso vamos alterar a classe Subject para um Módulo.
Utilizando o módulo Subject como mixin melhoramos a implementação:

Picture 7

Essa é exatamente a idéia da classe Observer encontrada na documentação do Ruby:

Picture 8

Portanto podemos incluir o próprio Observer do Ruby:

Picture 9

Melhorando o Observer usando blocos.

O método add_observer do Ruby não aceita blocos, mas podemos melhorar e solucionar isso aceitando blocos no módulo Subject que tinhamos criado anteriormente, chegando a uma implementação bem interessante de Observer:

Picture 10

Mais…
Entrevista com os autores do famoso livro do Gof, 15 anos depois de lançado:

Se você tem alguma sugestão para melhorar o código acima comente ou crie um fork no github.

Posted in: oo, ruby