Artigo no estilo Mentoring
Mentoring:

Padrões de projeto comportamentais são extremamente úteis para criar softwares mais fáceis de serem mantidos, evoluídos, promovendo reutilização de código, uso de boas práticas de programação orientada a objetos, redução de custos com manutenção, já que permitem que alterações em um determinado sistema de software não quebrem outras funcionalidades, reduzindo a dependência entre classes, através do uso exaustivo de abstrações. Dessa forma, são úteis para serem aplicados em projetos de média e larga escala.

Este artigo abordará os padrões de projeto comportamentais: Chain of Responsibility, Command, Iterator, Mediator e Memento. Estes padrões ajudam a tornar sistemas de software mais fáceis de serem mantidos e evoluídos. Se uma aplicação precisa ser modificada em um determinado momento, seja para manutenção corretiva ou evolutiva para atender novas funcionalidades, este processo precisa ser feito de forma disciplinada, correta, usando padrões, já que o custo da manutenção representa um porcentual elevado comparado ao custo total do sistema. Com isso, o uso de padrões de projeto, ou Design Patterns, torna um software mais fácil de ser mantido em longo prazo, reduzindo custos ou otimizando o retorno do investimento, apesar de exigir um maior esforço em um primeiro momento, durante o design do projeto a ser realizado.

Este artigo apresenta os padrões de projeto das classes comportamentais e implementações de exemplos práticos reais em Delphi.

Serão tratados os padrões GoF, destinados a resolver problemas com operações, comunicação e o relacionamento (no sentido de chamar métodos e passar dados) entre objetos complexos, que comprometem a reutilização de módulos do sistema e aumentam o acoplamento.

No final do artigo, apresento ainda mais um padrão estrutural, o Proxy, de forma a complementar os padrões vistos na edição anterior (Adapter, Bridge, Composite, Decorator e Facade).

Chain of Responsibility

A intenção deste padrão é evitar o acoplamento do remetente de uma solicitação ao seu receptor, ao dar a mais de um objeto a oportunidade de tratar essa solicitação. Encadear os objetos receptores, passando a solicitação ao longo da cadeia até que um objeto a trate.

Nesse padrão cada objeto receptor possui uma lógica descrevendo os tipos de solicitação que é capaz de processar e como passar adiante aquelas que requeiram processamento por outros receptores. A delegação das solicitações pode formar uma árvore de recursão, com um mecanismo especial para inserção de novos receptores no final da cadeia existente.

Dessa forma, fornece um acoplamento mais fraco por evitar a associação explícita do remetente de uma solicitação ao seu receptor e dar a mais de um objeto a oportunidade de tratar a solicitação.

Um exemplo da aplicação desse padrão é o mecanismo de herança nas linguagens orientadas a objeto: um método chamado em um objeto é buscado na classe que implementa o objeto e, se não encontrado, na superclasse dessa classe, de maneira recursiva.

Como benefício do uso do padrão, ele permite determinar quem será o objeto que irá tratar a requisição durante a execução. Cada objeto pode tratar ou passar a mensagem para o próximo na cadeia. Dessa forma, o acoplamento é reduzido, dando ainda flexibilidade adicional na atribuição de responsabilidades a objetos.

Participantes:

· Handler – define uma interface para tratar solicitações;

· ConcreteHandler - trata de solicitações pelas quais é responsável; pode acessar seu sucessor;

· Cliente – inicia a solicitação para um objeto ConcreteHandler da cadeia;

Vamos a um exemplo prático no Delphi. A Listagem 1 mostra um framework de classes que representa as classes participantes do padrão.

É um pequeno sistema de autenticação de usuários, sendo que temos três classes que realizam esse processo de autenticação: Formulário, Server e BancoDados. De fato, em um sistema real, normalmente realizamos a operação de autenticação em várias camadas do nosso sistema.

Primeiramente, no formulário de login, onde fazemos uma verificação (validação) básica, por exemplo, se o usuário não deixou os campos necessários para a autenticação em branco, ou se os formatos correspondem a uma máscara ou expressão regular (que pode ser um JavaScript em uma aplicação Web por exemplo), enfim, uma validação simples.

No servidor de aplicação, podemos ter uma validação a nível de código Delphi, mais complexa, que possa validar por exemplo várias tentativas de login vindas de um mesmo cliente. Ou ainda, processar validações mais complexas.

E finalmente, a checagem final que é feita no banco de dados, autenticando o usuário de acordo com a existência ou não das credenciais em uma determinada tabela. Note que cada camada da solução se encarrega de fazer um tratamento para as informações de usuário e senha, feito isso, repassa (ou não) esse tratamento para um sucessor na cadeia de responsabilidade, até que seja totalmente tratada ou que a cadeia seja finalizada (não exista mais um sucessor).

No exemplo, estamos fazendo um diferente tratamento em cada classe (para fins didáticos é um tratamento simples), de forma que encadeamos as chamadas entre formulário, server e banco de dados, criando uma cadeia.

O segredo do padrão fica por conta do participante Handler (manipulador), que é uma abstração para um participante da cadeia. Um objeto privado interno chamado _sucessor aponta para o objeto seguinte na cadeia, sendo este configurado através do método setSucessor.

Então, o método Autenticar de cada Handler (formulário, server e banco de dados) precisa, após fazer sua própria autenticação, decidir se passará o processo para o membro seguinte na cadeia, testando o objeto _sucessor. Como ele é abstrato e possui um método virtual abstrato que é implementado por todos os membros (handlers), o polimorfismo se encarrega de chamar o método concreto adequado em tempo de execução.

Por exemplo, se ...

Quer ler esse conteúdo completo? Tenha acesso completo