Login Registre-se

Home > Artigos > Engenharia de Software >

Padrões de projeto e OO: Um exemplo prático de utilização

Publicado por jesuino em 08/03/2010 - 11.560 visualizações



comentários: 13

Objetivo: Através de um exemplo simples tentaremos mostrar como padrões de projeto e conceitos de OO ajudam solucionando.

Origem: Nosso exemplo começa com um post, aqui mesmo no JavaFree, discutindo sobre campos de textos formatado, no qual Simu sugere uma solução diferente da comum para (ou não)permitir determinados caracteres em um campo de texto. Até então, eu tinha conhecimento somente do JFormmatedTextField para esta função. A solução de Simu utilizava ER , permitindo maior filtragem dos caracteres entrados no campo e, ainda, permitia muito mais! Com foco somente na filtragem simples (Campos somente numéricos, ou outros), decidi brincar um pouco com esta solução. Para ver este post clique aqui

Breve visão do problema: Nosso objetivo é encapsular a solução proposta pelo Simu e fornecer a funcionalidade de formatação caractere a caractere com ER para campos de texto sem muito impacto em projetos já existente e com facilidade para projetos futuros. Conforme o post, Simu gera uma nova instância de DocumentFilter e sobrepõe dois métodos: insertString e replace. De forma inteligente, ele sugere duas soluções de formatação, a primeira (comentada) com uma verificação se o texto confere com uma Expressão Regular, e a segunda é substituir todo texto que não esteja de acordo com sua ER por “”, assim limitamos a entrada a certas faixas de caracteres, como números. Se estiver perdido, releia o tópico aqui.

O código para o descrito acima se encontra abaixo:


Nosso objetivo, conforme já dito, é ocultar a complexidade do código acima e permitir que essa mesma forma de formatação seja aplicada a projetos existentes sem impacto(na verdade, com o mínimo de impacto).

Primeiro encapsular a criação do DocumenteFilter: Um problema que iremos encontrar é codificar, toda vez que formos usar está técnica, os métodos que foram sobrepostos, assim, a forma mais simples é implementar uma classe que herda de DocumentFilter e implementa os métodos uma vez. Assim não teremos que ficar implementando novamente os métodos.
Olhe abaixo:


Perfeito! Bem, quase perfeito. Se você for pensar bem, o filtro, no nosso caso, somente tem a função de aplicar a ER. O fraco acoplamento entre os objetos é uma boa prática de padrão de projeto reutilizável (GoF), e é esse nosso objetivo. Mas como diminuir a ligação entre a criação desse objeto e quem o utiliza? Que tal uma fábrica de filtros? Assim, o documento plano poderá somente pedir que um novo filtro seja fabricado. Alguns casos comuns, ainda, você pode até encapsular certos tipos de ER para os filtros. Então vamos fazer uma fábrica para os tipos de filtros para números e letras, assim iremos encapsular certos filtros, não sendo necessária o conhecimento de ER de quem usa o filtro, nesses dois casos comuns.
Acompanhe a implementação da nossa fábrica:

Wow! Simples não? Temos nossa fábrica! Observe que utilizamos a sobrecarga do método criar, permitindo uma outra forma de criação. Isso é importante no padrão Fábrica. Podemos gerar diversos tipos de um determinado objeto, personalizados, sem nem mesmo mexer no objeto original, ou seja, fabricamos objetos literalmente.
Vamos ver como ficaria se fossemos usar o que temos até agora em um programa:

Bem interessante, mas vamos pensar em problemas tentando prever situações de projeto.
1 - Um probleminha, o PlainDocument terá que ter seu DocumentFilter configurado a cada vez que for usado;
2 - Se você quiser mudar o comportamento do seu campo de texto (invés de filtrar para a entrada de números, filtrar para letras, por exemplo) você terá que utilizar outro PlainDocument, e atribuir o filtro... Ou seja, a classe que usar terá que ter conhecimento do algoritmos de implementação de diversos métodos, não estamos encapsulando direito.

Solução do 1- Criar uma classe que herde de PlainDocument: Não é uma boa solução, pois assim estaremos ligando fortemente todas classes filhas a classe mãe, isso nos dará problemas futuros na implementação. Outro problema é a geração de diversas instâncias que podem ser geradas. Bem, momento de apresentar uma das regras mas utilizadas nos padrões de projetos reutilizáveis:

Prefira composição a herança de classes.

Guarde essa palavra chave: Composição, pois ainda não encontramos uma solução para nosso problema de ocultar essas responsabilidades sem afetar possíveis modificações
Nota sobre modificações: Quem pensa que nunca irá modificar um projeto está enganado! Ainda mais tratando-se de GUI!

Solução do 2 - Delegar a ação para uma classe que obedeça um padrão: Não é difícil de notar que para conseguir um comportamento esperado teremos que delegar nossa responsabilidade para alguém( no caso desse documento, a responsabilidade é gerar um PlainDocument). Esse alguém deverá seguir alguma regra, para que a classe que utilize-se dele possa reconhecer, ou seja deverá implementar uma interface. Assim, teremos um método de criação de PlainDocument com polimorfismo perfeito! Pois terá comportamentos que dependerão de quem implementará a interface, através da delegação de responsabilidade da criação de documentos.

Ao invés da solução, encontramos mais problemas, no entanto, identificamos pontos chaves para procurar uma solução que responda aos dois problemas: O padrão Estratégia, que atráves da composição de uma interface, consegue delegar uma responsabilidade as classes concretas que implementam a mesma, assim, cada classes concreta pode responder de uma forma, ou seja, polimorfismo! O que se adapta perfeitamente ao nosso problema, já que queremos criar documentos de forma variável, e que a implementação criação não seja do conhecimento do programa que utiliza. Em outras palavras, essa parte do código não deve ser de conhecimento de quem chama, estamos encapsulando:



Observe que ocorre para o PlainDocument o mesmo que ocorreu para o DocumentFilter, não precisamos conhecer a instância, simplesmente precisamos de um PlainDocument para nossa necessidade, seja ela criar campos filtrados para números, ou campos filtrados para letras e ainda para uma ER específica.
Assim, temos que incorporar a fábrica a nossa estratégia, já que: a ação que deverá ter caráter de polimorfismo será a mesma de criação, por isso estamos combinando as duas.

Mãos a massa, implementar o padrão Estratégia: Chega de papo e vamos para a implementação do padrão Estratégia junto com uma fábrica, uma mistura dos dois. Vamos chamar de Fábrica Estratégica, bem sugestivo :). Relembrando o que foi dito acima, o Estratégia delega uma ação para uma interface, que dependendo de sua implementação, responde de uma forma. Assim, temos que ter uma interface com nossa ação, que no caso é a de criar PlainDocuments.
A nossa interface é simples:


A interface sozinha não faz nada por enquanto, precisamos de classes que implemente! Só que antes, temos um problema a considerar. A cada chamado do método getDocumentoPlano, iremos retornar uma instância nova, sendo que para cada objeto de classe que implementa a interface teremos diversas instâncias de PlainDocument, nesse caso, nosso padrão gerou um problema escondido dentro da solução. Isso é comum acontecer, e o programador/analista deve estar sempre atento a isso, pois emboora pareça perfeito o esquema, ele poderia gerar lentidão em função de ocupação de memória de instâncias não usadas. Uma simples verificação permite que isso seja evitado.
Abaixo, iremos implementar três classes concretas de PlainDocument, uma para geração de PlainDocument para números, outra para letras e mais uma para geração de filtro de acordo com a ER que o usuário enviar:

Somente números:



Somente Letras:



Com a Expressão Regular passada pelo usuário:



Assim, o uso no programa se resumiria a isso:




Isso parece uma fábrica... E é uma fábrica, mas foi implementada com Estratégia.
Por que?
-Simplesmente porque podemos adicionar novos métodos a interface e nas classes concretas para novas funcionalidades;
-Você poderá mudar de tipo de filtro sem mexer em nada no código, somente instanciando uma atributo do tipo iDocument para uma classe concreta.
-Adicionar novas classes concretas não impacta em nada no seu projeto, você simplesmente gera a classe concreta que implementa a interface e já pode usar no seu código. Para mudar o que já tem pronto implicaria em somente algumas linhas de código modificadas.

Na verdade são várias as vantagens, e várias as implementações, sejam certas ou erradas. Depende de sua necessidade e habilidade.

Agora qual o próximo passo? Chegamos ao nosso objetivo, estamos usando a solução do Simu de forma simples, somente basta gerar um jar de nossas classes e incluir em nosso projeto.

Enfim, o resultado:
Uma classe de teste para brincar. Observe bem a utilização da interface. A nova classe concreta que aparece uma classe implementando o a API toda para teste.


Mas uma sugestão para o próximo passo é gerar classes concretas que herdam de JTextField e que já tenham tipos de formatação configuradas! O problema será que temos que ocultar o método setDocument do JTextField que será gerado, afinal já iremos configura-lo para o tipo que se deseja gerar, então nesse ponto entrará a utilização de novos padrões e mais conhecimento para quem está aprendendo!

Meu projeto final ficou com essa cara:
0

Se você ler e entender esse artigo, terá uma nova visão para criação de soluções, não só quem estuda, mas principalmente quem está em ambiente produtivo.
Sugestão, comprem e leiam o livro do Gof






Download:  Projeto eclipse.JPG
Size:  23 KB



comentários: 13