Tópicos Avançados de Orientação a Objetos Sumário – 1 Introdução O objeto da disciplina Tópicos Avançados de Orientação a Objetos é explorar técnicas de design avançado em orientação a objetos, como o uso de Componentes e Design Patterns. Também são exploradas novas tecnologias e metodologias empregadas no desenvolvimento de aplicações OO, como Web services e XP Programming. Opcionalmente recursos avançados de programação OO, como multithreading e collections, são também abordados. Como um requerimento do design avançado de aplicações OO, a linguagem UML é revista com ênfase 2 Paradigma de OO A medida que as aplicações tornaram-se mais complexas as linguagens de programação tradicionais mostraram-se insuficientes para modelar situações cada vez mais complexas (estruturas de dados, tipos de novos etc.). A orientação a objeto aparece então como um poderoso instrumento de modelagem em que qualquer entidade, como um cliente, uma conta ou estruturas de dados como uma pilha ou um grafo, podem ser representados em termos do nome da entidade, suas propriedades e seus comportamentos. Esse é o paradigma da orientação a objeto. As linguagens que suportam esse paradigma [1], como SmallTalk, C++ e Java, trazem uma série de características comuns como: Encapsulamento do objeto, Herança, Polimorfismo, Definição de tipos do usuário, Comunicação dos objetos através de mensagens, Essas características dão às linguagens de programação OO (LPOO) um forte potencial para o reaproveitamento de código (pense por exemplo nos recursos de herança e polimorfismo) e o desenho de soluções robustas (pense por exemplo nos recursos de encapsulamento e herança). A possibilidade de reuso de código e de soluções robustas (menor manutenção de código) está diretamente ligada a redução no custo do desenvolvimento de aplicações. Isso pode ser refletido na forma de redução de prazos e custos de desenvolvimento ou no aumento da qualidade das aplicações. [1] Existem muitos outros paradigmas de linguagens como as linguagens funcionais (LISP). Curva de aprendizado OO e aplicações mais complexas O uso de linguagens OO, entretanto, não trouxe em um primeiro momento (entenda-se anos 80) uma redução no custo das aplicações. Destacam-se dois fatores: 1. As linguagem OO apresentam uma curva de aprendizado bastante maior que o das linguagens procedurais comuns (Pascal, Cobol, C etc.) limitando os benefícios da LPOO à capacidade do programador ou projetistas utilizar os seus recursos. 2. À medida que as LPOO foram sendo introduzidas as aplicações também tornaram-se mais complexas (em boa parte viabilizado pela OO) com interfaces mais sofisticadas ao usuário (interfaces gráficas, web etc.) e tipos de dados novos (imagens, XML, sons etc.). Com isso a redução do custo é minimizada: o custo da linha de código é menor, mas passamos a criar mais linhas de código. Exercícios 1. Relacione e explique as vantagens de LPOO. Alguma desvantagem com relação a linguagens procedurais tradicionais como Pascal e C? 2. Por que SmallTalk é considerada uma linguagem puramente orientada a objetos? 3. O desenvolvimento de aplicações OO é mais complexo. Justifique. 3 Desenvolvimento de aplicações corporativas O desenvolvimento de aplicações corporativas requer produtividade (menores custos e maior qualidade). Essa produtividade buscada em termos de: Menor tempo de desenvolvimento Menor quantidade de recursos Aplicações de fácil manutenção Aplicações fáceis de testar Aplicações com menor quantidade de linhas de código Aplicações tolerantes a falha (um aspecto de qualidade) etc. Existem também uma série de aspectos que permitem prover soluções de forma produtiva em uma empresa: Reuso de aplicações e código previamente existente Interoperabilidade entre diferentes plataformas (hardware e software) Suporte a transações etc. O uso de uma LPOO [1] não é suficiente para garantir a maior parte dessas características no desenvolvimento de aplicações corporativas. Às primeiras vamos chamar de características do projeto OO e as demais de requerimentos não-funcionais (termos empregados aqui livremente apenas para facilitar a exposição). [1] Isso não é nenhum demérito para as LPOO. Muitos desses objetivos são objetivos secundários de uma linguagem e devem ser atingidos por outros meios. Aplicações corporativas e OO Como obter boas características de projeto e atender requerimentos não-funcionais no desenvolvimento de aplicações com LPOO? A idéia é que produzir aplicações corporativas requer mais que uma linguagem de programação. O desenvolvimento de uma aplicação corporativa é um projeto complexo, com diversas fases (levantamento, design, codificação, testes etc.), e uma linguagem de programação tem influência limitada no resultado de todas essas fases. Vejamos somente o da fase de codificação. Codificação e IDE’s IDE é a sigla de Integrated Development Environment. São exemplos de IDE o Visual Studio da Microsoft, o Eclipse para Java e o JBuilder da Borland. Embora possamos desenvolver uma aplicação em Java ou C++ empregando apenas um simples editor de texto como o notepad e um compilador, esses ambientes fornecem uma série de facilidades para o desenvolvimento de código de forma profissional: Editor com recursos específicos para linguagem Identação de código Help de sintaxe Numeração de linhas Avanço e retrocesso de alterações Organização do código (classes, interfaces etc.) etc. Ferramentas para buscas e comparação de fontes Árvore de objetos (“Explorer”) Compilador Sinalização das linhas com erro Organização dos objetos para compilação (ordem, agrupamentos etc. “makefile”) Geração de código para deploy de aplicações Geração de componentes (stub, proxy e skeleton files) Ferramentas para depuração Ferramentas de documentação Poderosos contrutores de interface gráficas (“GUI Builder”) Além de integração com inúmeros plugins: Interfaces de acesso a bancos de dados Ferramentas de design de código (como ferramentas para UML) Geradores de código Ferramentas de refatoração de código Ferramentas de versionamento de fontes De todos esses recursos basta pensarmos nos contrutores de interface gráficas (“GUI Builder”) para vermos o quanto a produtividade, e estamos falando somente da fase de codificação, dependente de muitos outros recursos além da linguagem empregada (imagine-se programando um único elemento gráfico como um botão de ”OK” em uma linguagem como C++ ou Java sem o uso de um IDE). Produzindo aplicações OO com produtividade Concluímos então que para produzirmos aplicações OO com produtividade precisamos empregar uma série de técnicas e recursos que garantam as qualidades da LPOO e que ainda expandam seus benefícios. Aqui, selecionamos algumas dessas técnicas e recursos que parecem ser importantes para o desenvolvimento produtivo de aplicações e valem ser exploradas em maior detalhe: Design avançado A garantia de qualidades como reusabilidade e extensibilidade das LPOO dependem muito mais das técnicas de análise e definição do projeto (design) que das linguagens ou ferramentas que são empregadas. De fato, sob esses aspectos as linguagens e ferramentas em geral oferecem recursos muito semelhantes ente si. Técnicas avançadas de design incluem uso extensivo da UML (a linguagem padrão para o design de aplicações OO); técnicas e boas práticas para definição das soluções do projeto: que entidades formarão classes? Que tipo de relacionamento melhor expressa uma situação problema? Quando empregar uma técnica de delegação de classes? Essas são algumas perguntas que fazem parte do que podemos chamar uma metodologia de design; por último encontramos algumas técnicas mais avançadas que visam oferecer boas soluções de design como os Padrões de Projeto (Design Patterns). Metodologias de desenvolvimento Metodologias tradicionais de desenvolvimento como a análise estruturada ou a análise funcional nem sempre trazem os melhores resultados no desenvolvimento de aplicações OO. De fato, metodologias tradicionais partem, em geral, de modelos de dados (como o modelo entidade-relacionamento) levando a soluções fortemente centradas a dados. Recentemente diversas metodologias vêm surgindo em resposta à necessidade do rápido desenvolvimento de aplicações OO. Algumas metodologias envolvem diretamente o design da aplicação e ferramentas, como a programação orientada a aspectos, outras podem envolver a prática do desenvolvimento colaborativo, como a prática do Extremming Programming (XP Programming). Distribuição e interoperabilidade de objetos Um objeto em C++ ou Java é um objeto instanciado na memória de uma única instância de processamento (um computador) e, na ausência de recursos adicionais, somente pode ser compartilhado por programas e objetos que residam na mesma máquina e baseados na mesma linguagem. Isso é uma forte limitação no desenvolvimento de soluções de software uma vez que muitas vezes encontramos código pronto para solução de problemas mas que pode, entretanto, estar desenvolvido em um linguagem diferente e executar sistemas diferentes do resto da solução. A isso denominamos interoperabilidade. Outra necessidade é a de as aplicações (e portanto os objetos) serem distribuídas. Soluções genericamente chamadas de componentes endereçam tanto a interoperabilidade como a distribuição de objeto e vem sendo oferecidas pelos principais plataformas de aplicações como o J2EE e .Net. Outros recursos Os itens acima não esgotam o conjunto de recursos empregados no desenvolvimento produtivo de aplicações pelas empresas, mas parecem ser aqueles que mais exploram aspectos suficientemente gerais para serem estudados. Outras necessidades são por exemplo o versionamento de código, soluções para o deployment das aplicações e soluções para a persistência de objetos. Exercícios 1. O que é refatoração? 2. Procure identificar as facilidades indicadas para os IDE’s em algum IDE que você utilize. 3. Procure justificar a necessidade ferramentas de versionamento de código no desenvolvimento de aplicações corporativas. 4. Das técnicas que você conhece para o desenvolvimento de aplicações cite aquelas que você acha inadequadas no desenvolvimento OO e explique por que. 5. Cite as vantagens de termos objetos distribuídos nas aplicações. 6. Cite soluções encontradas para a persistência de objetos. 4 Modelagem (ver [Booch, 1999] capítulo 1) A base de um projeto (design) de aplicação é a modelagem. A modelagem é uma simplificação da realidade e os modelos possibilitam entendermos melhor as aplicações que desenvolvemos [1]. Construímos modelos sempre que um sistema é bastante complexo que não possa ser inteiramente compreendido como um todo. Podemos entender a modelagem como uma estratégia de redução da complexidade que divide um problema que não pode ser tratado como um único todo. Objetivos da modelagem 1. Ajuda a visualizar um sistema existente ou que queremos construir. 2. Permitem especificar a estrutura e o comportamento de um sistema. 3. Fornecem uma guia para a construção das aplicações. 4. Documentam as decisões de projeto. Princípios de modelagem 1. A tipo de modelo empregado tem uma forte influência em como o problema é tratado e na forma da solução (um modelo de dados leva a soluções centradas em dados!). 2. Todo modelo pode ser expresso com diferentes níveis de precisão ou detalhamento. 3. Os melhores modelos são conectados a realidade. 4. Nenhum único modelo é completamente suficiente. Qualquer sistema não trivial é melhor representado através de um pequeno conjunto de modelos mais ou menos independentes. Modelagem OO A modelagem ou o projeto de aplicações OO tem os mesmos objetivos e segue os mesmos princípios acima, gerais para qualquer modelagem. Devemos ter em mente, pelo primeiro princípio, que o modelo OO deve levar a soluções diferentes de uma solução, por exemplo, orientada a dados. Por exemplo, o encapsulamento, é uma característica que deve ser mais presente em aplicações com origem em modelos OO. Exercícios 1. Considere o modelo [velocidade_final = velocidade_inicial + aceleração * tempo]. Você reconhece neste modelo os princípios da modelagem? 2. Identifique no modelo ER (Entidade-Relacionamento) os objetivos e os princípios da modelagem. 3. Cite características que são mais enfatizadas em aplicações que tem origem em modelos de dados (modelo ER e relacional) e modelos OO. 4. O modelo ER e relacional são modelos diferentes? Explique. 5. Relacione ao menos 3 benefícios da modelagem de aplicações. [1] O desenho de aplicações é uma modelagem no domínio da solução, o que difere do domínio do problema. 5 UML, Introdução (ver [Booch, 1999] capítulo 2) O propósito da UML (Unified Modeling Language) é o de: Visualizar Especificar Construir Documentar versões etc. Apresenta graficamente os elementos da aplicação Define a estrutura o comportamento dos elementos da aplicação Fornece uma guia para análise e codificação das aplicações Documenta requerimentos, arquitetura, código, testes, protótipos, Para sistemas orientados a objeto, sendo empregada principalmente em sistemas intensivos em software [1]. Building Block, “Lego” A UML é uma linguagem constituída de elementos básicos que podem, por sua vez, serem combinados de modo a formar novos elementos. Esses building blocks podem ser de 3 tipos: Elementos, Relacionamentos e Diagramas. Elementos Estruturais componentes, nós Comportamentais Elementos de agrupamento Elementos de anotação Classes, interfaces, colaborações, casos de uso, Mensagens, estados de objetos Pacotes Notas Relacionamentos Dependência (<<include>>, <<extend>>~, <<import>> são alguns estereótipos de dependência) Associação (composição, agregação e associações com navegabilidade são casos especiais) Generalização (o termo empregado para herança e especializações na UML) Realização (empregado especialmente na realização de interfaces) Diagramas Diagrama de classes Diagrama de Objetos (Diagramas de Interação) Diagramas de Seqüência Diagramas de Colaboração Diagramas de Estado Diagramas de Atividades Diagramas de Componentes Diagramas de Distribuição Níveis e diferentes visões dos diagramas Dependendo do nível de detalhe os diagramas da UML podem ser: Conceituais, de Especificação (Desenho) ou de Implementação (Codificação). Por oferecer diferentes visões de um mesmo sistema a UML apresenta uma visão Ortogonal das aplicações. Ciclo de vida de desenvolvimento de software A UML é independente do ciclo de vida de desenvolvimento de software empregado. Entretanto a UML se beneficia de métodos de desenvolvimento: Direcionados pelos Casos de Uso Centrados na arquitetura Interativos e Incrementais Não sendo de forma alguma um processo seqüencial e linear de desenvolvimento (ver figura abaixo). Exercícios 1. Identifique os diagramas Estruturais, Comportamentais e de Arquitetura da UML. 2. Dê exemplos de classes com diferentes níveis de detalhe e que correspondam a modelos Conceituais, de Especificação (Desenho) ou de Implementação (Codificação). 3. Defina e explique o aspecto ortogonal da UML. 4. Quais as 5 visões oferecidas pela UML em um projeto de software? R. Visão do caso de uso; visão de desenho (design ou projeto); de processo; de implementação; e de distribuição (deployment). 5. Explique a frase sobre o ciclo de vida de desenvolvimento de software empregado com a UML: “Não sendo de forma alguma um processo seqüencial e linear de desenvolvimento”. 6. Explique como a fase de teste (“Test”) no ciclo de vida pode ocorrer na fase de elaboração de um projeto. 6 UML, Hello World! (ver [Booch, 1999] capítulo 3) Os diagramas UML embora possam ser empregados para modelagem de classes de dados têm o propósito de modelarem classes mais genéricas. Chamaremos essas classes de classes programa. Import java.awt.Graphics; Class HelloWorld extends java.applet.Applet { Public void paint (Graphics g) { g.drawString(“Hello, World!”, 10, 10); } } Exercícios 1. Identifique na classe HelloWorld métodos (declarados e empregados); herança; objetos e parâmetros. 2. Aplicações em Java podem ser de 2 tipos: aplicativos e applets. Diferencie esses dois tipos de aplicações Java. 7 UML, Casos de Uso (ver [Booch, 1999] capítulo 16) Geral Casos de Uso Atores Caso de uso Relacionamentos dependência; generalização; Dependência Diagramas estruturais Agentes externos ao sistema Representam um funcionalidade autocontida Associações Atores-Casos de Uso; Estereótipos: <<include>>; <<extend>> Fluxo de Eventos Para cada caso de uso do diagrama deve haver uma descrição textual com os seguintes elementos: Nome do caso de uso Atores participantes Condições de entrada (Condições que precisam ser satisfeitas antes do caso de uso ser iniciado) Fluxo de eventos principais Fluxo de eventos secundários Condições de saída (Condições que precisam ser satisfeitas depois do caso de uso estar completado) Requerimentos especiais Cenários Os cenários são instâncias de casos de uso e devem ser empregados quantos cenários forem necessários para que se complemente a descrição dos casos de uso. Alguns tipos de cenários úteis: Cenários “As-is” Cenários futuros Cenários para avaliação Cenários para treinamento Requerimentos não funcionais Os requerimentos não funcionais, como considerações de performance e de segurança do sistema, não são exatamente parte dos diagramas UML mas devem fazer parte do levantamento e da descrição de qualquer sistema (ver exercício abaixo). Boas práticas Identificando Atores: Identifique grupos de usuários: a) suportados pelo sistema em seu trabalho; b) grupos que executam as principais funções do sistema e funções secundárias; c) interações do sistema com agentes externos de hardware ou software. Identificando Cenários: Identifique grupos de usuários que criam, modificam, acessam e removem os dados. Identifique as tarefas que cada grupo de usuários deseja executar. Identifique a frequencia e a validade das informações dadas e fornecidas pelo sistema. Identificando Casos de Uso: Lembre-se os casos de uso são estruturais e, portanto, descrevem grandes estruturas ou partes do sistema. Na descrição, seja dos diagramas, fluxo de eventos ou cenários, deve-se empregar sempre a linguagem do usuário. Exercícios 1. Forneça uma lista de ao menos 10 possíveis requerimentos não funcionais de sistemas. 2. Diferencie e dê exemplo das dependências do tipo <<include>> e <<extend>> em casos de uso. 3. Casos de uso devem fazer uso da linguagem do usuário. Justifique. 4. Os cenários são sempre construídos antes da criação dos diagramas de casos de uso. Justifique. 5. Não existe nenhum sentido de fluxo ou sequencia nos diagramas de casos de uso. Justifique. 6. Os diagramas de casos de uso substituem o diagrama de contexto de outras práticas de modelagem. Justifique. 8 UML, Diagramas de classe (ver [Booch, 1999] capítulos 8, 9, 10) Uma classe simples Uma classe simples em Java com um a único atributo e uma única operação. public class Employee { private int empID; public double calcSalary(){ ... } } Atributos [visibilidade] nome [multiplicidade] [:tipo] [=valor inicial] [{outras propriedades}] + public [0..5] *item frozen # protected int changeable - private String addOnly O atributo ainda pode ser sublinhado para indicar escopo de classe no lugar de escopo de instância. Operações [visibilidade] nome [ (lista de parâmetros) ] [: tipo de retorno ] [{outras propriedades}] isQuery sequential concurrent (lista de parâmetros) := [direção] nome : tipo [=valor inicial] in out inout Principais relacionamentos Dependência public class Employee { public void calcSalary(Calculator c) { ... } } Associação simples e navegabilidade public class Employee { private TimeCard _tc[]; public void maintainTimeCard() { ... } } Agregação public class Employee { private EmpType et[]; public EmpType getEmpType() { ... } } Composição public class Employee { private TimeCard tc[]; public void maintainTimeCard() { ... } } Generalização public abstract class Employee { } public class Professor extends Employee { } Realização public interface CollegePerson { } public class Professor implements CollegePerson { } Um exemplo completo Diagrama de Objetos Diagrama de Objetos correspondente ao Diagrama de Classes do exercício 1. :TreeMap topNode :TreeMapNode - itsKey = "Martin" nodes[LESS] nodes[GREATER] :TreeMapNode :TreeMapNode - itsKey = "Bob" - itsKey = "Robin" nodes[LESS] nodes[GREATER] nodes[LESS] :TreeMapNode :TreeMapNode :TreeMapNode :TreeMapNode - itsKey = "Alan" - itsKey = "Don" - itsKey = "Paul" - itsKey = "Sam" nodes[GREATER] Abbott`s Rules As regras abaixo são heurísticas bastante simples que podem ser empregadas para modelagem de aplicações OO à partir dos discursos que a descrevem. Parte do discurso Substantivos próprios Substantivos comuns Verbos com sentido de Fazer Ser Ter Obrigação Adjetivo, adjetivados Componente do modelo Objeto Classe Exemplos Anna, Shell Aluno, Empresa, Funcionário Operação Generalização Agregação “Constraints” Atributo Cria, submete, seleciona É um tipo de, é outro, é um Tem, consiste de, inclui Deve, precisa Descrição do incidente, nota do aluno, custo da ação Boas práticas Tenha foco na estrutura do sistema. Foque em um aspecto estrutural do sistema, empregando mais de um diagrama se for necessário descrever mais que um aspecto. Represente somente elementos que são essenciais para descrever um aspecto. Isto é especifique somente os aspectos que você deseja que sejam garantidos na aplicação. Empregue nomes que comuniquem seu propósito. Tente não mostrar muitos relacionamentos. Em geral existem relacionamentos predominantes. Organize conjuntos de classes em pacotes. Exercícios 2 TreeMap + add(key, value) + get(key) topNode nodes TreeMapNode + add(key, value) + find(key) itsKey «interface» Comparable itsValue Object 1. Considere o diagrama de classes acima. Codifique as respectivas classes Java. public class TreeMap { TreeMapNode topNode = null; public void add(Comparable key, Object value) {…} public Object get(Comparable key) {…} } class TreeMapNode { private Comparable itsKey; private Object itsValue; private TreeMapNode nodes[] = new TreeMapNode[2]; public TreeMapNode(Comparable key, Object value) {…} public Object find(Comparable key) {…} public void add(Comparable key, Object value) {…} } 2. Além dos compartimentos para nome, atributos e operações, existe ainda um quarto compartimento para a representação de classes. Justifique. O quarto compartimento é empregado para descrevermos a responsabilidade da classe. 3. O que são e para que são empregados os cartões CRC? 4. Construa e codifique uma classe associativa e as classes relacionadas. 5. Defina e diferencie associações do tipo agregação e composição. 6. Crie exemplos de associações com diferentes multiplicidades e navegabilidades codificando as respectivas classes. 9 UML, Diagramas de Interação (ver [Booch, 1999] capítulos 18, 27) São diagramas que descrevem o comportamento de um sistema, seu comportamento ” inter-classes”. São de dois tipos: os diagramas de sequência que privilegiam a ordem cronológica em que ocorrem as mensagens e eventos do sistema; e os diagramas de colaboração, que privilegiam a representação da estrutura de classes que colaboram na execução de um dada tarefa (responsabilidade). Diagramas de Seqüência Diagramas de Colaboração Boas práticas A construção de diagramas de colaboração pode partir da organização já representada no diagrama de classes. Represente somente elementos que são essenciais para descrever um aspecto. Isto é especifique somente os aspectos que você deseja que sejam garantidos na aplicação. Exercícios 1. Converta o diagrama Sequência dado acima para um diagrama de Colaboração. 2. Converta o diagrama Colaboração dado acima para um diagrama de Sequência. 3. Podem aparecer mais de um objeto da mesma classe em um diagrama de Sequência? Justifique. 4. Para cada um dos diagramas dados construa o correspondente diagrama de classes. Comece identificando os métodos de cada classe e a navegabilidade. 5. Diagramas de seqüência ou de colaboração podem exibir mensagens simultâneas como as que ocorrem em processos paralelos (como processos multithreading)? Justifique sua resposta. 6. A ativação de objeto pode ocorrer mais que uma vez em um diagrama de seqüência? Justifique. 7. As mensagens em um diagrama de seqüência são mensagens síncronas, mensagens assíncronas são representadas por setas com a terminação parcial. Explique o que são mensagens síncronas e assíncronas. 10 UML, Diagramas de Estado (ver [Booch, 1999] capítulos 21, 24) São diagramas que descrevem o comportamento de um sistema do ponto de vista ” intra-classes”. Os diagramas de estado também podem ser empregados para representar estados mais genéricos e mais abstratos do sistema como “em produção” ou “em análise”. Boas práticas Empregue diagramas de estados para representações de estado do “sistema” (mais gerais) e não unicamente para “estados de objetos/classes”. Represente somente elementos que são essenciais para descrever um aspecto. Isto é especifique somente os aspectos que você deseja que sejam garantidos na aplicação. Algoritmos complexos devem fazer uso de diagramas de atividades. Exercícios 1. Elabore um diagrama de estados que represente um controlador de temperatura. Acima (abaixo) de uma determinada temperatura o sistema passa a esfriar (esquentar). O sistema também possui uma temperatura limite (mais alto e mais baixo) à partir do qual o sistema alarma e deixa de operar. 2. Cite aspectos que diferenciam um DFD de um diagrama de estados. 11 UML, Diagramas de Atividades, Pacotes e Componentes (ver [Booch, 1999] capítulos 12, 25, 29) Diagrama de Atividades requisitante : Leitor a biblioteca : Biblioteca Verifica disponibilidad e Vem requisitar [ disponível ] [ indisponível ] [ espera ] [ desiste ] Coloca em lista de : requisição [espera disponibilidade] espera Aguarda disponibilidad e disponível Vem levantar Recebe a publicaçã o Avisa o requisitant e Empresta a publicaçã o : requisição [espera levantamento] : requisição [espera devolução] Consulta a publicaçã o Devolve a publicaçã o Recolhe a publicaçã o Os diagramas de atividades são utilizados principalmente para: Descrever fluxos de processos do sistema Descrever algoritmos complexos Descrever processos paralelos : requisição [finalizada] Pacotes package BusinessObjects; public class Employee { } Dependência de pacotes package A; import B.*; public class SomeAClass { private ClassInB b; } GUI Client + OrderForm - WindowsGUI MacGUI + Client GUI «import» Estereótipos em Pacotes «system» «subsystem» «façade» «framework» «stub» «layer» Componentes representa o sistema completo representa uma parte independente de sistema constitui uma visão sobre outro pacote (não acrescenta funcionalidades, apenas apresenta de forma diferente) representa um conjunto de classes abstratas e concretas concebido para ser estendido, implementando a funcionalidade de um determinado domínio de aplicação pacote que serve como proxy para o conteúdo público de outro pacote pacote que representa uma camada horizontal do um sistema Estereótipos em componentes <<executable>> componente que pode ser executado num nó <<library>> biblioteca estática ou dinâmica <<database>> banco de dados <<table>> tabela de um banco de dados <<file>> arquivo contendo código fonte ou dados <<document>> documento genérico Boas práticas Represente somente elementos que são essenciais para descrever um aspecto. Isto é especifique somente os aspectos que você deseja que sejam garantidos na aplicação. Algoritmos complexos devem fazer uso de diagramas de atividades. Tente agrupar classes em pacotes que tenham uma unidade com <<subsistema>> Exercícios 1. Relacione as vantagens e desvantagens do uso da UML para representação de modelos de dados como o modelo relacional. Pense em aspectos do modelo relacional que não têm correspondente na UML. 2. Explique como diagramas de componentes e pacotes podem ser utilizados para organização do código e distribuição do aplicativo. 12 UML, Deployment, Patterns e Frameworks (ver [Booch, 1999] capítulos 26, 28, 30) Este tópico não será tratado em sala de aula mas deverá ser estudado pelo aluno como atividade complementar e obrigatória. Exercícios 1. Explique por que o deployment é um aspecto importante do desenvolvimento das aplicações OO e modelado através da UML (ver [Booch, 1999] capítulos 26, 30). 2. Defina e diferencie Patterns de Frameworks (ver [Booch, 1999] capítulo 28). 13 Design Patterns, Introdução MVC, Model-View-Controller O padrão Modelo-Visão-Controlador, embora constitua-se em um padrão, não é um dos design patterns padrão que aparecem em [Gamma, 1995], onde estão definidos os clássicos padrões do GoF. Entretanto o MVC aparece como uma introdução natural aos padrões de projeto. Princípios de Design Patterns Os padrões de projeto buscam seguir bons princípios de design de objeto. Dentre alguns desses princípios encontramos: Open Close Principle Dependency Inversion Principle Interface Sergregation Principle Single Responsibility Principle (delegação) Liskov's Substitution Principle Tipos de Design Patterns Os padrões de projeto podem ser divididos em padrões de Criação, Estruturais e Comportamentais, segundo o seu principal propósito. Padrões de Criação Singleton Factory Factory Method Abstract Factory Builder Prototype Object Pool Padrões Estruturais Adapter Bridge Composite Decorator Façade Flyweight Proxy Padrões Comportamentais Chain of Responsibility Command Interpreter Iterator Strategy Template Method Visitor Definindo Design Patterns A definição e descrição de um design pattern deve ser constituída ao menos de: Nome do padrão Problema, ou propósito Solução, muitas vezes aqui incluindo-se uma aplicação Conseqüências do padrão Caracterizando um Design Patterns As seguintes características devem estar presente em um design pattern: Descrever e justificar a solução para um problema concreto e bem definido A solução descrita deve ser comprovada previamente Descrever relações entre conceitos, mecanismos e estruturas existentes nos sistemas O problema tratado deve ocorrer em diferentes contextos Exercícios 1. Defina e exemplifique a técnica de projeto OO denominada delegação. 2. Apresente as principais vantagens do uso de interfaces. 3. Relacione os principais padrões de projeto GoF associados ao MVC. 4. Cite os principais propósitos dos padrões de projeto. Os padrões de projeto visam diretamente o reuso de soluções e compartilhar o conhecimento entre os projetistas de software. 14 Design Patterns, Singleton //--------------------------------------------------------------------------------------------package jandl.pattern.singleton; import java.sql.*; public final class DBConnection { // Referência para instância única private static Connection instance = null; // Construtor privado private DBConnection() throws ClassNotFoundException, SQLException { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); System.out.println("[Driver loaded]"); instance = DriverManager.getConnection("jdbc:odbc:Mamute"); System.out.println("[Connection done]"); } // Fornece acesso a instância única public static Connection getConnection() throws ClassNotFoundException, SQLException { if (instance == null) { // Lazy instantiation: só quando preciso new DBConnection(); } System.out.println("[Connection obtained]"); return instance; // Retorna instância única da conexão } // Encerra conexão adequadamente public static void shutdown() throws ClassNotFoundException, SQLException { if (instance != null) { instance.close(); instance = null; System.out.println("[Connection closed]"); } } } //--------------------------------------------------------------------------------------------package jandl.pattern.singleton; import java.sql.*; public class DBConnectionTest { public static void main(String a[]) throws Exception { Connection con; Statement stmt; // Obtém conexão con = DBConnection.getConnection(); stmt = con.createStatement(); int r = stmt.executeUpdate("INSERT INTO USERS VALUES('"+ a[0] + "','"+ a[1] +"')"); System.out.println(r + " row(s) affected"); stmt.close(); // Obtém (a mesma) conexão con = DBConnection.getConnection(); stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM USERS"); while (rs.next()) { System.out.println(rs.getString(1) + ":" + rs.getString(2)); } rs.close(); stmt.close(); // Encerra conexão DBConnection.shutdown(); } } Exercícios 1. Adapte o padrão Singleton para oferecer um ponto de acesso único a um pool de instâncias no lugar de uma única instância. 15 Design Patterns, FactoryMethod //--------------------------------------------------------------------------------------------package jandl.pattern.factorymethod; public interface IConverter { public double convert(double value); } //--------------------------------------------------------------------------------------------// Conversor Farenheit to Celsius package jandl.pattern.factorymethod; public class F2C implements IConverter { // Método definido na interface IConverter public double convert(double value) { return 5*(value - 32)/9; } } // Conversor Celsius to Farenheit package jandl.pattern.factorymethod; public class C2F implements IConverter { // Método definido na interface IConverter public double convert(double value) { return 9*value/5 + 32; } } //--------------------------------------------------------------------------------------------package jandl.pattern.factorymethod; public interface IConverterFactory { public IConverter createConverter(int inS, int outS); } //--------------------------------------------------------------------------------------------package jandl.pattern.factorymethod; public class ConverterFactoryImpl implements IConverterFactory { // constantes de escalas disponíveis public static final int CELSIUS=0, FARENHEIT=1, KELVIN=2; public static final String scales[] = { "Celsius", "Farenheit", "Kelvin" }; // método fábrica public IConverter createConverter(int in, int out) { switch(in) { case CELSIUS: switch(out) { case FARENHEIT: return new C2F(); case KELVIN: return new C2K(); } break; case FARENHEIT: switch(out) { case CELSIUS: return new F2C(); case KELVIN: return new F2K(); } break; case KELVIN: switch(out) { case CELSIUS: return new K2C(); case FARENHEIT: return new K2F(); } break; } return null; } } //--------------------------------------------------------------------------------------------package jandl.pattern.factorymethod; import java.awt.*; import java.awt.event.*; import java.text.*; import javax.swing.*; public class ConverterUI extends JFrame { private JComboBox chScIn, chScOut; private JTextField tfValIn, tfValOut; private ConverterFactoryImpl factory = new ConverterFactoryImpl(); private DecimalFormat df = new DecimalFormat("#####.00"); public ConverterUI() { super("ConverterUI"); Container c = getContentPane(); c.setLayout(new GridLayout(3, 3, 2, 2)); // adição dos componentes no ContentPane c.add(new JLabel()); c.add(new JLabel("In")); c.add(new JLabel("Out")); c.add(new JLabel("Scale")); c.add(chScIn = new JComboBox(factory.scales)); c.add(chScOut = new JComboBox(factory.scales)); c.add(new JLabel("Value")); c.add(tfValIn = new JTextField()); c.add(tfValOut = new JTextField()); // listeners e ajustes nos componentes tfValIn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doConvertion(); } }); tfValOut.setEditable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setResizable(false); } private void doConvertion() { IConverter conv = factory.createConverter( chScIn.getSelectedIndex(), chScOut.getSelectedIndex()); try { double value = df.parse(tfValIn.getText()).doubleValue(); if (conv!=null) { value = conv.convert(value); } tfValOut.setText(df.format(value)); } catch (Exception e) { tfValIn.selectAll(); tfValIn.requestFocus(); Toolkit.getDefaultToolkit().beep(); tfValOut.setText(""); } } public static void main(String a[]) { new ConverterUI().setVisible(true); } } Exercícios 1. Empregue o exemplo dado para criar uma aplicação que calcula a área de figuras com um quadrado ou um círculo criando instâncias específicas através do FactoryMethod. 16 Design Patterns, Façade //--------------------------------------------------------------------------------------------package jandl.pattern.facade; import jandl.pattern.singleton.*; import java.sql.*; import java.util.*; public class QueryFacade { private Connection con; // construtor public QueryFacade() { try { // usamos Singleton de conexão ao BD con = DBConnection.getConnection(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } // método que encapsula consulta public List executeQuery(String query) { try { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query); ResultSetMetaData rsmd = rs.getMetaData(); int colsCount = rsmd.getColumnCount(); List result = new LinkedList(); while (rs.next()) { String row[] = new String[colsCount]; for(int i=0, j=1; i<colsCount; i++, j++) { row[i] = rs.getString(j); } result.add(row); } rs.close(); stmt.close(); return result; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } // método que encapsula shutdown da conexão ao BD public void shutdown() { try { DBConnection.shutdown(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } //--------------------------------------------------------------------------------------------package jandl.pattern.facade; import java.util.*; public class QueryFacadeTest { public static void main(String a[]) { // instanciação e uso do Facade QueryFacade qf = new QueryFacade(); List result = qf.executeQuery("SELECT * FROM USERS"); // processamento dos dados Iterator i = result.iterator(); StringBuffer data = new StringBuffer(); int c; while (i.hasNext()) { String row[] = (String[])i.next(); for(c=0; c<row.length-1; c++) { data.append(row[c]); data.append(","); } data.append(row[c]); data.append("\n"); } // exibição e finalização System.out.println(data.toString()); qf.shutdown(); } } //--------------------------------------------------------------------------------------------- Exercícios 1. Com base no código apresentado crie uma outra aplicação (livre) do padrão Façade. 17 Design Patterns, Proxy Este tópico não será tratado em sala de aula mas deverá ser estudado pelo aluno como atividade complementar e obrigatória. //--------------------------------------------------------------------------------------------public class ProxyDisplay extends JxFrame { public ProxyDisplay() { super("Display proxied image"); JPanel p = new JPanel(); getContentPane().add(p); p.setLayout(new BorderLayout()); ImageProxy image = new ImageProxy(this, "elliott.jpg", 321,271); p.add("Center", image); setSize(400,400); setVisible(true); } //--------------------------------------------------------------------------------------------public ImageProxy(JFrame f, String filename, int w, int h) { height = h; width = w; frame = f; tracker = new MediaTracker(f); img = Toolkit.getDefaultToolkit().getImage(filename); tracker.addImage(img, 0); //watch for image loading imageCheck = new Thread(this); imageCheck.start(); //start 2nd thread monitor //this begins actual image loading try{ tracker.waitForID(0,1); } catch(InterruptedException e){} } //--------------------------------------------------------------------------------------------public void run() { //this thread monitors image loading //and repaints when the image is done try{ Thread.sleep(1000); while(! tracker.checkID(0)) Thread.sleep(1000); } catch(Exception e){} repaint(); } //--------------------------------------------------------------------------------------------public void paint(Graphics g) { if (tracker.checkID(0)) { height = img.getHeight(frame); //get height width = img.getWidth(frame); //and width g.setColor(Color.lightGray); //erase box g.fillRect(0,0, width, height); g.drawImage(img, 0, 0, frame); //draw image } else { //draw box outlining image if not loaded yet g.drawRect(0, 0, width-1, height-1); } } //--------------------------------------------------------------------------------------------- Exercícios 1. Estude o propósito, a solução e a implementação Java do padrão Proxy (acima um exemplo da implementação). A fonte/referência para este exercício é livre. Booch, G., Rumbaugh, J., Jacobson, I. The Unified Modeling Language User Guide, Addison-Wesley, 1999. Knoernschild, K. Java™ Design: Objects, UML, and Process, Addison-Wesley, 2001. Jandl, P.Jr. Padrões de Projeto em Java, revista Mundo Java nº 6, 2004, http://www.mundojava.com.br/NovoSite/6codigos.shtml. Bruegge, B., Dutoit, A.H. Object-Oriented Software Engineering: Conquering Complex and Changing Systems, Prentice-Hall, 2000. Gamma, E., Helm, R., Johnson, R., Vlissides, J. Design Patterns: elements of reusable object-oriented software, Addison-Wesley, 1995. Cooper, J.W. The Design Patterns: Java Companion, Addison-Wesley, 1998. Kantek, A. Uma Introdução a Objetos Distribuídos, revista Mundo Java nº 6, 2004, http://www.mundojava.com.br/NovoSite/6codigos.shtml.