Padrões de Projeto Alcides Calsavara http://www.ppgia.pucpr.br/~alcides Referências Bibliográficas Design Patterns: Elements of Reusable Object-Oriented Software. Erich Gamma e outros (GoF), Addison-Wesley, 1995. Patterns in Java: a catalog of reusable design patterns illustraded in UML, Volume 1. Mark Grand, John Wiley & Sons, 1998. Projeto de Software: da programação à arquitetura (Software design: from programming to architecture). Eric Braude, John Wiley & Sons, 2004. Design Pattern (Padrão de Projeto) Descrição de um problema recorrente, de forma genérica. Descrição de uma solução também genérica, que deve ser adaptada de acordo com o contexto em que o problema se manifesta. Objetivos de projeto Reusabilidade, flexibilidade e manutenibilidade reutilize projetos flexíveis mantenha o código em um nível geral minimize dependência de outras classes Robustez reutilize projetos seguros reutilize partes robustas Suficiência e correção modularize o projeto reutilize partes confiáveis Vantagens no uso de DP Evita a redescoberta de soluções Propicia o uso de soluções corretas Melhora a qualidade do software Melhora a confiabilidade do software Provê uma linguagem comum entre desenvolvedores Reduz o volume de documentação Economiza esforço e tempo de desenvolvimento e manutenção Conduz ao bom uso de orientação a objetos Exemplo de Problema Recorrente : Composição Em um sistema de arquivos, existem arquivos e pastas (diretórios), sendo que todo arquivo está contido em uma pasta e toda pasta pode conter arquivos e também outras pastas. Em um documento, existem caracteres e imagens como elementos básicos, e páginas, colunas, frames e linhas de texto como elementos compostos, sendo que todo elemento básico está contido em um elemento composto e todo elemento composto pode conter elementos básicos e também outros elementos compostos. Composição em Sistema de Arquivos ComponenteSistemaArquivos tamanho listar ( ) {abstract} Arquivo tipo listar ( ) Pasta listar ( ) 0..* Composição em Documento ElementoDocumento Caráter Imagem Documento Página 0..* ElementoCompostoDocumento Coluna Frame LinhaTexto Composição: Solução Genérica 1 (GoF) Component Client Operation() Add(Component) Remove(Component) GetChild(int) 0..* Leaf Composite Operation() Operation() Add(Component) Remove(Component) GetChild(int) Composição: Solução Genérica 2 (M. Grand) AbstractComponent 0..* operation() ConcreteComponent1 ConcreteComponent2 AbstractComposite operation() operation() operation() add(AbstractComponent) remove(AbstractComponent) getChild(int) ... ConcreteComposite1 ConcreteComposite2 operation() add(AbstractComponent) remove(AbstractComponent) getChild(int) operation() add(AbstractComponent) remove(AbstractComponent) getChild(int) ... Categorias de padrões (GoF) CRIACIONAIS: criar uma coleção de objetos de maneira flexível ou restrita. ESTRUTURAIS: representar uma coleção de objetos relacionados. COMPORTAMENTAIS: captar comportamento em uma coleção de objetos. Padrões de projeto criacionais Fábrica Fábrica Abstrata Protótipo Objeto Unitário Padrões de projeto estruturais Composto Decorador Adaptador Fachada Padrões de projeto comportamentais Comando Iterador Interpretador Observador Gabarito Documentação de um DP (Mark Grand) Nome Sinopse Contexto Forças Solução Conseqüências Implementação Uso na API de Java Exemplo de código Padrões relacionados Características de padrões de projeto Pontos de vista: Estático: modelo de classes (do que são feitos) Dinâmico: diagrama de seqüência ou de estados (como funcionam) Níveis: Abstrato: descreve o núcleo do padrão Concreto: descreve as particularidades de um caso Padrões Fundamentais Delegation (Delegação) Interface (Interface) Marker Interface (Interface de Marcação) Immutable (Imutável) Proxy Padrão Delegation (Delegação) Permite estender e reusar a funcionalidade de uma classe através da escrita de classes adicionais com funcionalidades a mais que usam instâncias da classe original, a fim de prover a funcionalidade original. Padrão Delegation – Exemplo usando herança Padrão Delegation – Exemplo usando herança múltipla Padrão Delegation – Exemplo usando delegação Padrão Interface Mantém uma classe que usa dados e serviços providos por instâncias de outras classes independente dessas através do acesso a tais instâncias obrigatoriamente através de uma interface. Padrão Interface – Exemplo sem usar interface Padrão Interface – Exemplo usando interface Padrão Interface – Exercício Inclua um terceiro tipo de impressora (ImpressoraCanon) no diagrama de impressoras e sistema. Padrão Marker Interface (Interface de Marcação) Usa interfaces que não declaram qualquer método ou variável (atributo) para indicar propriedades semânticas de uma classe. Funciona muito bem para classes utilitárias que precisam determinar alguma coisa sobre objetos, sem entretanto precisar assumir que esses sejam instâncias de alguma classe em particular. Padrão Marker Interface – Exemplo sem usar Padrão Marker Interface – Exemplo usando Padrão Marker Interface – Exercício Inclua a interface de marcação Comprimível para a hierarquia de classes de documentos. Padrões de projeto criacionais Fábrica Fábrica Abstrata Protótipo Objeto Unitário Padrão Factory Method (Fábrica) Permite que uma classe genérica (escrita para ser reusada) instancie outras classes sem que seja dependente de tais classes, isto é, sem que faça menção explícita a essas. A classe genérica se mantém independente das classes que instancia através da delegação para um outro objeto da escolha de qual classe instanciar e somente refere-se ao objeto então criado através de uma interface comum. Permite criar objetos desejados utilizando métodos que retornam os objetos. Padrão Factory Method – Geral Padrão Factory Method – Exemplo Padrão Abstract Factory (Fábrica Abstrata) Kit ou Toolkit Permite a criação de instâncias de um conjunto de classes abstratas relacionadas a partir de respectivo um conjunto de classes concretas. Pode ser muito útil quando se precisa trabalhar com uma variedade de entidades externas complexas. Permite criar famílias coordenadas de objetos em tempo de execução, escolhidos a partir de um conjunto de estilos. Padrão Abstract Factory - Geral Padrão Abstract Factory Exemplo Padrão Prototype (Protótipo) Permite criar os objetos de um tipo clonando um protótipo. Permite um objeto criar objetos customizados sem saber suas classes exatas e sem saber detalhes sobre como fazer tal criação. Funciona através do fornecimento de objetos protótipos para um objeto que então solicita de tais protótipos a criação de cópias deles próprios. Padrão Protótipo: Geral Padrão Protótipo: Exemplo Padrão Singleton (Objeto Unitário) Assegura que uma classe tenha exatamente uma instanciação, acessível por toda aplicação. Garante que apenas uma e somente uma instância de uma certa classe é criada. Todos os objetos que usam uma instância da classe usam a mesma instância. Padrão Singleton - Geral Padrão Composite (Composição) Recursive Composition Pattern Permite representar uma árvore de objetos tal que o acesso seja uniforme. Permite construir objetos complexos através de uma composição recursiva que define uma árvore de objetos. Todos os objetos são acessados de maneira consistente e homogênea, pois todos possuem uma superclasse ou uma interface comum. Padrão Composite (Composição) Há um objeto complexo que deve ser decomposto numa hierarquia “parte-todo” de objetos. Deseja-se minimizar a complexidade numa hierarquia parte-todo de objetos através da minimização do número de tipos diferentes de filhos que precisam ser explicitamente conhecidos dos objetos da árvore. Façade (Fachada ) Fornece uma interface para um pacote de classes. Regula a comunicaçãoa com os objetos de um pacote (componente). Clientes interagem com uma única classe de um pacote. A estrutura Fachada está na forma de delegação. Permite gerenciar arquiteturas de software envolvendo grandes números de classes. Fachada: Geral Fachada: Exemplo Padrão Iterator (Iterador) Define uma interface que declara métodos para o acesso seqüencial aos objetos de uma coleção. Uma classe (cliente) que acessa a coleção somente através de uma interface desse tipo permanece independente da classe que implementa a interface. Padrão Iterador: Geral Padrão Iterador: Exemplo Padrão Command (Comando) Encapsula comandos em objetos tal que é possível controlar sua seleção e seqüenciamento, enfileirá-los, desfazêlos (undo ), isto é, manipulá-los de forma geral. Permite tornar a execução de operações mais flexível (ex: desfazer). Padrão Comando – Geral Padrão Comando - Exemplo Padrão Observer (Observador) Permite que objetos registrem dinamicamente suas dependências de outros objetos. Um objeto especial notificará os objetos dependentes sempre que os objetos dos quais dependem sofrerem alteração de estado. Permite atualizar um conjunto de objetos quando um certo objeto sofrer modificação. Padrão Observador - Geral Padrão Observador - Exemplo Padrão Adapter (Adaptador) Permite que uma aplicação utilize funcionalidades externas. Uma classe Adapter implementa uma interface conhecida dos clientes e permite acesso a instâncias de uma classe não conhecida dos clientes. Um objeto Adapter provê a funcionalidade prometida por uma interface sem fixar a classe que de fato implementa a interface. Padrão Adapter - Geral Padrão Adapter – Exemplo 1 Padrão Adapter – Exemplo 2 Decorator (Decorador) Permite adicionar e remover responsabilidades de uma classe em tempo de execução (dinamicamente). Alternativa flexível a generalização/especialização para extensão de funcionalidade. Decorator - Geral Decorator – Exemplo 1 Decorator – Exemplo 2 Padrão Strategy (Estratégia) Encapsula algoritmos relacionados em classes que são subclasses de uma classe comum. Permite a seleção de algoritmo variar por objeto e também no decorrer do tempo. Padrão Strategy – General Padrão Strategy – Exemplo 1 Padrão Strategy – Exemplo 2 Interpreter (Interpretador) Permite analisar sintaticamente uma expressão. Interpreter - Geral Interpreter - Exemplo Padrão Guarded Suspension Suspende a execução de uma chamada de método até que uma pré-condição seja satisfeita. Exemplo: Guarded Suspension class Conta { private double saldo; public synchronized void saque(double v) { while ( saldo < v ) { try { wait( ); } catch ( InterruptedException e ) { } } saldo = saldo – v; } public synchronized void deposito(double v) { saldo = saldo + v; notifyAll( ); } } Padrão Producer-Consumer Coordena a produção e o consumo assíncronos de objetos de informação. Exemplo: Producer-Consumer class Queue { private Vector data = new Vector( ); // fila de objetos synchronized public void put( Object obj ) { data.add( obj ); // insere o objeto na fila notify( ); } synchronized public Object get( ) { while ( data.size( ) == 0 ) // enquanto fila vazia { try { wait( ); } catch ( InterruptedException e ) { } } Object obj = data.elementAt( 0 ); // primeiro da fila data.removeElementAt( 0 ); return obj; } }