Design Patterns: Template Method e Strategy em Ruby

Posted on October 22, 2009

5


É interessante conhecer como outros programadores pensaram para solucionar casos que costumam se repetir durante o desenvolvimento de software. Alguns casos recorrentes foram catalogados em 1994 pela GoF, no livro sobre Design Patterns. Vamos ver como aplicar alguns casos em Ruby.

Considere um sistema complexo onde em uma determinada parte do código existe a necessidade de uma variação. As vezes esse núcleo pode fazer uma coisa enquanto outra vez é necessário fazer outra. Para exemplificar melhor, vamos usar uma classe “Relatorio”. Nosso relatório mostrará um conteúdo inicialmente em HTML.
fig1

Vamos deixar de lado a questão do html na classe agora, apenas para efeito de análise do código.

Nesse momento nossa classe gera apenas o relatório em um formato, mas voltando ao problemas que estamos resolvendo, temos a necessidade de criar o mesmo relatório porém em um formato diferente, por exemplo pdf ou texto puro mesmo.
O que fazer agora que é necessário um relatório em formato diferente ?
Bem…(rs)..uma abordagem poderia ser:
fig2

Bonito nao ?🙂
Para solucionar esse problema de forma orientada a objetos poderiamos criar uma classe abstrata que define o comportamento de um relatório, ou seja, que define que um relatório de ter um head, um body, um footer, etc. Mas como criar uma classe abstrata em Ruby ? Embora não exista a palavra chave reservada “abstract” o conceito permanece presente na linguagem. Vejamos:
fig3

A classe Relatorio agora possui os métodos que definem um relatório. Obviamente esses métodos podem ser invocados. A solução para isso normalmente é lançar uma exception nesses métodos não implementados.
Agora que temos nossa classe abstrata, podemos criar subclasses de Relatorio que contém a implementação de cada um dos tipos, por exemplo para HTML:
fig4

E a implementação para Texto:
fig5

Agora para usar nossos relatórios podemos fazer:
fig6

Esse é o Template Method, um dos patterns que deram origem ao famos livro de Design Patterns do GoF.
Mas…um dos princípios mais discutidos em engenharia de software nãe era …”Prefira composição ao invés de Herança” ?

HERANÇA!?!?!?!

esqueceram-de-mim

Compor e delegar são formas muito mais recomendadas de construir o design de uma aplicação. O que podemos fazer para melhorar nossa solução seguindo essas idéias ?
Primeiramente vamos parar de usar a classe relatório como uma superclasse, vamos refatorar o código e criar uma superclasse chamada “Formatter”. Essa classe servirá para pensarmos em composição. Nossas implementações de relatórios agora ficam assim:

fig7

Agora podemos começar a pensar em composição. De volta a classe Relatorio, agora o “initalize” receberá um Formatter. Fica mais fácil imprimir um relatório agora, veja:

fig8

Agora para usar nossos relatórios podemos fazer:
fig9

Executar um núcleo diferente agora depende de como nosso objeto foi composto. O acoplamento agora é bem menor. Essa é a idéia do Strategy, tiramos a herança e favorecemos a composição, onde todos os objetos suportam
suportam a mesma interface, mesmo que não exista a palavra chave reservada em ruby.

Mas da pra melhorar um pouco ainda, não ?
Repare no método “imprime_relatorio” da classe Relatorio. Esse método passa o titulo e o texto
para a implementação do relatório. Podemos melhorar isso passando uma referência do próprio objeto
Relatório com self!

E pra melhorar um pouco mais e utilizar uma das características importantes do Ruby, podemos passar o conteúdo
do relatório através de blocos, tirando de vez a herança!
fig10

Por mais que algumas palavras chaves da orientação a objetos não sejam explicitas no ruby, os conceitos estão muito presentes.
Se você tiver alguma sugestão para melhorar o código acima, por favor, comente ou crie um fork do gist do Diego Carrion no github.

Posted in: oo, ruby