Padrões de projetos / Design Pattern – Parte 2
Introdução
Conhecer herança, polimorfismo e abstração não o fazem um bom desenvolvedor. Muitas pessoas já passaram pelos mesmos problemas que você está passando ou vai passar e saber utilizar este conhecimento é um início para o caminho das pedras.
Tem gente que pensa que saber fazer if, for, while já é suficiente para se dizer desenvolvedor. É necessário pensar de forma padronizada levando em consideração desempenho, portabilidade, fácil manutenção, fácil compreensão e muitos outros pontos que poderia ser um bom e grande post.
Este post visa iniciar os estudos nos padrões de projetos para obter uma visão geral. Eu sei que escrevo bastante, mas eu tento passar o máximo de informação em menos linhas possíveis. O que acontece é que existe muitas informações a serem passadas… mania de desenvolvedor, hehehehe.
Lembre-se: Um padrão não foi inventado do nada, foi pensado, modelado e utilizado inumeras vezes com resultado de sucesso. Se você estiver tendo problemas com isto, repense, talvez não esteja fazendo da forma correta.
Por onde começar?
Iniciaremos o estudo em cima dos 23 padrões de projetos clássicos do GoF. Não existirá uma sequência definida e sim os mais simples de compreender primeiro.
Abaixo segue a classificação dos 23 padrões segundo o GoF.
Interfaces: Adapter, Facade, Composite, Bridge
Responsabilidade: Singleton, Observer, Mediator, Proxy, Chain Of Responsibility, Flyweight
Construção: Builder, Factory Method, Abstract Factory, Prototype, Memento
Operações: Template Method, State, Strategy, Command, Interpreter
Extensões: Decorator, Interator, Visitor
Veremos, adiante, o que são, como funciona e como implementar estes padrões. Cada um deles resolvem um problema específico, é preciso entender porque, quando e como aplicá-los.
Objetivos dos 23 padrões de projetos
Abaixo veremos, de forma resumida, os objetivos de todos eles, segundo o GoF.
1. Adapter: Converter a interface de uma classe em outra interface esperada pelos clientes.
2. Façade: Oferecer uma interface única de nível mais elevado para um conjunto de interfaces de um subsistema.
3. Composite: Permitir o tratamento de objetos individuais e composições desses objetos de maneira uniforme.
4. Bridge: Desacoplar uma abstração de sua implementação para que os dois possam variar independentemente.
5. Singleton: Garantir que uma classe só tenha uma única instância, e prover um ponto de acesso global a ela.
6. Observer: Definir uma dependência um-para-muitos entre objetos para que quando um objeto mudar de estado, os seus dependentes sejam notificados e atualizados automaticamente.
7. Mediator: Definir um objeto que encapsula a forma como um conjunto de objetos interagem.
8. Proxy: Prover um substituto ou ponto através do qual um objeto
possa controlar o acesso a outro.
9. Chain of Responsibility: Compor objetos em cascata para, através dela, delegar uma requisição até que um objeto a sirva.
10. Flyweight: Usar compartilhamento para suportar eficientemente grandes quantidades de objetos complexos.
11. Builder: Separar a construção de objeto complexo da representação para criar representações diferentes com mesmo processo.
12. Factory Method: Definir uma interface para criar um objeto mas deixar que subclasses decidam que classe instanciar.
13. Abstract Factory: Prover interface para criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas.
14. Prototype: Especificar tipos a criar usando uma instância como protótipo e criar novos objetos ao copiar este protótipo.
15. Memento: Externalizar o estado interno de um objeto para que o objeto possa ter esse estado restaurado posteriormente.
16. Template Method: Definir o esqueleto de um algoritmo dentro de uma operação, deixando alguns passos a serem preenchidos pelas subclasses.
17. State: Permitir a um objeto alterar o seu comportamento quanto o seu estado interno mudar.
18. Strategy: Definir uma família de algoritmos, encapsular cada um, e fazê-los intercambiáveis.
19. Command: Encapsular requisição como objeto, para clientes parametrizarem diferentes requisições, filas, e suportar operações reversíveis.
20. Interpreter: Dada uma linguagem, definir uma representação para sua gramática junto com um interpretador.
21. Decorator: Anexar responsabilidades adicionais a um objeto dinamicamente.
22. Iterator: Prover uma maneira de acessar elementos de um objeto agregado seqüencialmente sem expor sua representação interna.
23. Visitor: Representar uma operação a ser realizada sobre os elementos de uma estrutura de objetos.
Soluções para problemas comuns que os padrões de projeto evitam
Especificação explicita de classe na criação de objetos: O sistema está preso a uma implementação específica.
Solução: criar objetos indiretamente com Abstract Factory, Factory Method ou Prototype.
Dependência em operações específicas: O sistema só tem uma forma de satisfazer uma requisição.
Solução: evitar ações “hard-coded” com Chain of Responsibility ou Command.
Dependência em plataforma de hardware ou software: O software precisa ser portado a outras plataformas.
Solução: limitar dependências com Abstract Factory ou Bridge.
Dependência em representações ou implementações de objetos: Clientes que sabem como um objeto é implementado, representado ou armazenado podem precisar serem alterados se o objeto mudar.
Solução: isolar cliente com Abstract Factory, Bridge, Memento ou Proxy.
Dependências de algoritmo: Mudanças de algoritmo são freqüentes. Objetos que dependem de um algoritmo precisam mudar quando o algoritmo mudar.
Solução: isolá-los com Builder, Iterator, Strategy, Template Method ou Visitor
Forte acoplamento: Classes fortemente acopladas são difíceis de reusar, testar, manter, etc.
Solução: enfraquecer o acoplamento com Abstract Factory, Bridge, Chain of Responsibility, Command, Façade, Mediator ou Observer.
Extensão de funcionalidade através de subclasses: Herança é difícil de usar na sua forma correta e a composição dificulta compreensão.
Solução: usar padrões que implementam bem herança e composição como Bridge, Chain of Responsibility, Composite, Decorator, Observer ou Strategy.
Incapacidade de alterar classes convenientemente: Classes inaccessíveis, incompreensíveis ou difíceis de alterar.
Solução: usar Adapter, Decorator ou Visitor.
Tipos de software
Vamos entender as prioridades de padrões de projetos nos tipos comuns de softwares.
Aplicações: Softwares especialistas de modo geral.
Prioridades: reuso interno, manutenção e extensão.
Toolkits, APIs, bibliotecas: Conjunto de classes reutilizáveis de propósito geral. Não impõem design.
Prioridade: amplo reuso de código.
Frameworks: Dita a arquitetura da aplicação. Requer que usuário aprenda o framework e inclua código e configuração.
Prioridade: amplo reuso de design.
Geralmente são fortemente baseados em padrões. Quem conheçe os padrões entende o framework mais facilmente.
Conclusão
O caminho inicial é grande, eu sei, mas é necessário iniciar de algum lugar. Existem padrões de projeto sendo criados o tempo todo, é necessário entender os clássicos antes de se aventurar nos mais novos.
Existem muitos problemas já resolvidos, não vale a pena perder tempo inventando uma forma “rápida” de resolver, vamos usar o conhecimento dos mais experientes.
Referência
Erich Gamma et al. Design Patterns: Elements of Reusable
Object-oriented Software. Addison-Wesley, 1995.
METSKER, Steven John. Design Patterns Java Workbook. Addison-Wesley, 2002.
Abraços a todos e até a próxima.
“A maioria das pessoas tem êxito porque se determinou a tê-lo. Gente medíocre às vezes consegue um sucesso notável simplesmente porque não sabe quando parar.” George Allen, treinador de futebol americado.