UM ARCABOUÇO DE APOIO AO APRENDIZADO DE PADRÕES DE PROJETO NO CONTEXTO DE DISCIPLINAS DE...
-
Upload
igor-arcanjo -
Category
Documents
-
view
5 -
download
0
description
Transcript of UM ARCABOUÇO DE APOIO AO APRENDIZADO DE PADRÕES DE PROJETO NO CONTEXTO DE DISCIPLINAS DE...
Universidade Federal do Ceará
Centro de Tecnologia
Departamento de Engenharia de Teleinformática
Curso de Graduação de Engenharia de Teleinformática
UM ARCABOUÇO DE APOIO AO APRENDIZADO DE
PADRÕES DE PROJETO NO CONTEXTO DE DISCIPLINAS DE
PROGRAMAÇÃO ORIENTADA A OBJETOS
Igor Arcanjo Chaves
Fortaleza – Ceará
Junho/2012
UM ARCABOUÇO DE APOIO AO APRENDIZADO DE
PADRÕES DE PROJETO NO CONTEXTO DE DISCIPLINAS DE
PROGRAMAÇÃO ORIENTADA A OBJETOS
Projeto Final de Curso submetido à
Coordenação do programa de Graduação em
Engenharia de Teleinformática da
Universidade Federal do Ceará como parte
dos requisitos para a obtenção do grau de
Engenheiro de Teleinformática.
Autor: Igor Arcanjo Chaves
Orientador: Prof. Dr. José Marques Soares
Fortaleza – Ceará
Junho/2012
Título do Trabalho: UM ARCABOUÇO DE APOIO AO APRENDIZADO DE PADRÕES
DE PROJETO NO CONTEXTO DE DISCIPLINAS DE PROGRAMAÇÃO ORIENTADA
A OBJETOS
Autor: Igor Arcanjo Chaves
Defesa em: 29/06/2012 Nota Obtida: 9,7
Banca Examinadora
_________________________________________
Prof. Dr. José Marques Soares
Orientador
_________________________________________
Prof. Dr. Danielo Gonçalves Gomes
Universidade Federal do Ceará
_________________________________________
Prof. Dr. Antonio de Barros Serra
Instituto Federal de Educação, Ciência e Tecnologia do Ceará
Fortaleza, 29 de Junho de 2012
Dedico esta monografia aos meus pais, Tarcisio e Helena, pelo apoio
incondicional, aos meus irmãos, Breno e Tassia, que apesar da
distância sempre estiveram ao meu lado e à minha namorada,
Fernanda, que nos momentos mais difíceis me deu forças e inspiração
para continuar e jamais desistir dos meus objetivos.
“Que os vossos esforços desafiem as impossibilidades, lembrai-vos de
que as grandes coisas do homem foram conquistadas do que parecia
impossível.”
Charles Chaplin
AGRADECIMENTOS
Considerando este trabalho como o resultado de uma longa caminhada, o
agradecimento às pessoas que contribuíram para a minha formação não é uma tarefa fácil.
Para não correr o risco de cometer injustiças agradeço de antemão todas as pessoas que
passaram pela minha vida e que direta ou indiretamente me ajudaram nessa longa caminhada
em busca do sucesso.
Gostaria de agradacer também em particular algumas pessoas que tiveram participação
fundamental para a construção desse trabalho:
Ao meu professor e orientador, Prof. Dr. José Marques, pelos conhecimentos
transmitidos em sala de aula e também fora dela e pela força e dedicação na orientação deste
trabalho.
Ao GREat (Grupo de Redes de Computadores, Engenharia de Software e Sistemas),
em especial à Prof. Dra. Rossana Andrade e ao Reinaldo Braga, pela oportunidade ímpar que
tive ao ingressar no grupo e pelo total apoio e confiança depositada em mim durante todo o
tempo em que estive no grupo.
Aos amigos de faculdade pelos momentos de descontração dentro e fora de sala de
aula, pelas noites em claro estudando para provas e trabalhos da faculdade e pela forte
amizade que construímos durante essa longa caminhada.
A todos os professores do Departamento de Engenharia de Teleinformática pelo
aprendizado passado durante minha graduação.
À Universidade Federal do Ceará pela formação de nível superior de excelente
qualidade.
Resumo
Os processos modernos de engenharia de software, em especial as metodologias ágeis,
tem como principal objetivo não só a produtividade na construção de softwares, mas também
a sua produção com requisitos de alta qualidade, o que inclui características de reusabilidade e
expansibilidade. Umas das técnicas para se alcançar qualidade e produtividade é o uso de
padrões de projeto (ou design patterns). Em muitos programas acadêmicos, os padrões de
projeto só são estudados em disciplinas de Engenharia de Software, mais precisamente,
durante as fases associadas à codificação. Defende-se a inclusão de maneira objetiva do
estudo sobre padrões de projeto já em disciplinas de programação orientada a objetos, visto
que eles podem ser vistos como técnicas avançadas e consolidadas de construção de software
e representam um elemento de compreensão efetiva dos mecanismos de herança e
polimorfismo. Nesse sentido, este trabalho propõe um arcabouço simples para dar suporte ao
aprendizado de padrões de projeto em disciplinas de programação orientada a objetos. O
Arcabouço é apresentado através do desenvolvimento inicial de um conjunto de padrões de
projeto que são julgados importantes, podendo ser generalizado para futuras expansões. Para
cada um desses padrões, são descritas as suas principais características, a situação que
comumente requer o seu uso, além de sua implementação em Java um caso de exemplo.
Finalmente, é proposto um exercício para sedimentar o seu aprendizado, explorando-se, em
seguida, o reuso das classes e interfaces que fazem parte da estrutura do padrão a fim de
mostrar a facilidade de evolução da solução e ressaltar as características de baixo acoplamento
de suas partes.
Palavras-Chaves: Padrão de Projeto, Programação Orientada a Objetos, Engenharia
de Software, Qualidade de Software.
Abstract
Modern processes of software engineering, particularly agile methods, has its the main
goal not only the productivity in building software, but also its production with high quality
requirements, which includes characteristics of reusability and expandability. One of the
techniques to achieve quality and productivity is the use of design patterns. In many academic
programs, design patterns are only studied in the disciplines of Software Engineering, more
specifically during the phases associated with encoding. The text supports the inclusion of an
objective study on design patterns already in programming disciplines, especially in object-
oriented programming, since they can be viewed as advanced and consolidated programming
techniques, besides it represents an element of effective understanding of the mechanisms of
inheritance and polymorphism. Thus, this study aims to build a framework to support the
learning of design patterns in disciplines of object-oriented programming. The framework is
presented by developing a set of design patterns that are deemed important and can be
generalized for future expansion. For each of these patterns, it's describes its main
characteristics, the situation that usually requires its use, and its implementation in Java an
example case. Finally, we propose an exercise to consolidate their learning, exploring,
whenever possible, the reuse of classes and implementing interfaces involved in the expansion
of pattern in order to emphasize the fundamentals of low coupling provided by it.
Keywords: Design Patterns, Object Oriented Programming, Software Engineering,
Software Quality.
SUMÁRIO
1. Introdução .................................................................................................................................... 12
1.1. Motivação............................................................................................................................. 12
1.2. Objetivos .............................................................................................................................. 13
1.3. Organização do documento ................................................................................................. 14
2. Padrões de Projeto ....................................................................................................................... 15
2.1 Introdução .................................................................................................................................. 15
2.2 Elementos Indispensáveis de Programação Orientada a Objetos .............................................. 16
Herança ........................................................................................................................................ 17
Polimorfismo ................................................................................................................................ 18
2.3 Arcabouço para o Estudo de Padrões de Projeto ....................................................................... 20
2.4 Strategy ...................................................................................................................................... 23
Apresentação ............................................................................................................................... 23
Descrição com Exemplo ............................................................................................................... 23
Codificação do Exemplo ............................................................................................................... 26
Proposição .................................................................................................................................... 29
Extensão ....................................................................................................................................... 30
2.5 Abstract Factory ......................................................................................................................... 30
Apresentação ............................................................................................................................... 30
Descrição com Exemplo ............................................................................................................... 30
Codificação do Exemplo ............................................................................................................... 33
Proposição .................................................................................................................................... 37
Extensão ....................................................................................................................................... 37
2.6 Composite .................................................................................................................................. 37
Apresentação ............................................................................................................................... 37
Descrição com Exemplo ............................................................................................................... 38
Codificação do Exemplo ............................................................................................................... 39
Proposição .................................................................................................................................... 41
Extensão ....................................................................................................................................... 41
2.7 Padrões de Projeto em Construção ............................................................................................ 42
3. A Gênese do Arcabouço Proposto ................................................................................................ 43
4. Considerações finais ..................................................................................................................... 48
5. Referências Bibliográficas............................................................................................................. 50
6. Apêndice ...................................................................................................................................... 52
6.1. Apêndice A - Facade .................................................................................................................. 52
Apresentação ............................................................................................................................... 52
Descrição com Exemplo ............................................................................................................... 52
Codificação do Exemplo ............................................................................................................... 54
Proposição .................................................................................................................................... 55
Extensão ....................................................................................................................................... 56
6.2. Apêndice B - Singleton............................................................................................................... 56
Apresentação ............................................................................................................................... 56
Descrição com Exemplo ............................................................................................................... 56
Codificação do Exemplo ............................................................................................................... 57
Proposição .................................................................................................................................... 59
Extensão ....................................................................................................................................... 59
6.2. Apêndice C - Iterator ................................................................................................................. 59
Apresentação ............................................................................................................................... 59
Descrição com Exemplo ............................................................................................................... 60
Codificação do Exemplo ............................................................................................................... 61
Proposição .................................................................................................................................... 64
Extensão ....................................................................................................................................... 64
LISTA DE FIGURAS
Figura 1 – Exemplo de Classe e Objetos (UML). .................................................................... 16
Figura 2 – Exemplo de Herança. .............................................................................................. 18
Figura 3 – Exemplo de Polimorfismo....................................................................................... 20
Figura 4 – Arcabouço para o estudo de padrões. ...................................................................... 22
Figura 5 – Diagrama de Classes Inicial (UML) Strategy ......................................................... 23
Figura 6 – Classes RubberDuck e WoodDuck ......................................................................... 24
Figura 7 – Classes RubberDuck ............................................................................................... 25
Figura 8 – Diagrama de Classes Final (UML) Strategy ........................................................... 26
Figura 9 – Classes FlyBehavior.java, FlyWithWings.java e FlyNoWay.java ......................... 26
Figura 10 – Classes QuackBehavior.java, Quack.java, MuteQuack.java e Squeak.java ......... 27
Figura 11 – Classe Duck.java ................................................................................................... 28
Figura 12 – Classes MallardDuck.java, ReadheadDuck.java, WoodDuck.java e
RubberDuck.java ...................................................................................................................... 29
Figura 13 – Classe Application.java ......................................................................................... 29
Figura 14 – (a) Classes Button e Menu (b) Classe Application.java ........................................ 31
Figura 15 – (a) Classes WinButton e LinuxButton (b) Classe Application.java ...................... 31
Figura 16 – Diagrama de Classes (UML) Abstract Factory ..................................................... 33
Figura 17 – Classe GUIFactory.java I ...................................................................................... 34
Figura 18 – Classes WinFactory.java e LinuxFactory.java ...................................................... 34
Figura 19 – Classe Button.java ................................................................................................. 34
Figura 20 – Classes WinButton.java e LinuxButton.java ........................................................ 35
Figura 21 – Classes Application.java ....................................................................................... 36
Figura 22 – Classe GUIFactory.java II ..................................................................................... 36
Figura 23 – Diagrama de Classes (UML) Composite .............................................................. 39
Figura 24 – Classe ComponenteGrafico.java ........................................................................... 39
Figura 25 – Classes Linha.java e Texto.java ............................................................................ 40
Figura 26 – Classe Imagem.java............................................................................................... 41
Figura 27 – Enunciado da Atividade sobre Design Patterns .................................................... 44
Figura 28 – Diagrama de Classes (UML) Facade .................................................................... 53
Figura 29 – Classes Motor.java, Porta.java, Farol.java e Radio.java ....................................... 54
Figura 30 – Classe CarroFacade.java ....................................................................................... 55
Figura 31 – Exercício Proposto Facade .................................................................................... 56
Figura 32 – Diagrama de Classes (UML) Singleton ................................................................ 57
Figura 33 – Classe Singleton.java ............................................................................................ 58
Figura 34 – Classe Main.java ................................................................................................... 59
Figura 35 – Diagrama de Classes (UML) Iterator .................................................................... 61
Figura 36 – Classes Estrutura.java e Iterador.java ................................................................... 61
Figura 37 – Classes Fila.java e Lista.java ................................................................................ 62
Figura 38 – Classes IteradorLista.java e IteradorFila.java ....................................................... 63
Figura 39 – Classe Aplicacao.java ........................................................................................... 64
LISTA DE TABELAS
Tabela 1– Avaliação média dos alunos da turma de graduação para a atividade sobre padrões
de projeto na disciplina de Engenharia de Software (2012-1).................................................. 46
12
1. Introdução
1.1. Motivação
A definição de Engenharia de Software não é única. Apesar das distintas definições de
diversos autores, elas apresentam características de convergência. O IEEE (Institute of
Eletrical and Eletronics Engineers) [1] define engenharia de software como o estudo e
aplicação de uma abordagem sistemática, disciplinada e quantificável para o
desenvolvimento, operação e manutenção do software. De acordo com Ian Summerville [2] a
engenharia de software se relaciona com todos os apectos da produção do software, desde a
especificação do sistema até a manutenção que será feita após o software entrar em produção.
Os processos modernos de engenharia de software, em especial as metodologias ágeis,
tem como principal objetivo não só a produção de softwares com alta produtividade, mas
também com alta qualidade e que sejam reusáveis e expansíveis.
Qualidade de software é outro termo utilizado na engenharia de software na qual a
definição não é uma unanimidade. A NBR ISO 9000:2005 [3] define qualidade como o "grau
no qual um conjunto de características inerentes satisfaz a requisitos". Segundo a mesma
norma requisito é a "necessidade ou expectativa que é expressa, geralmente, de forma
implícita ou obrigatória". Dessa forma, pode-se inferir que a qualidade pode ser medida
através do grau de satisfação das pessoas envolvidas na realização de determinado serviço ou
produto, tornando a qualidade algo bem subjetivo, já que depende da interpretação de cada ser
envolvido e não somente do que está escrito ou expresso.
De acordo com Pressman [4] a qualidade de software precisa ser implementada e não
ficar somente na idéia. Ele diz que é preciso definir explicitamente o termo qualidade quando
o mesmo for utilizado, é preciso também criar um conjunto de atividades que irão ajudar a
garantir que a qualidade definida seja alcançada e, também, utilizar métricas para melhoraria
de processo de software aumentando, consequentemente, a qualidade do produto final.
Portanto, a busca da qualidade de software não é algo que deve se preocupar somente após o
código ser gerado, ela precisa ser uma atividade que seja aplicada ao longo do processo de
desenvolvimento de software.
Bruegge [5] sintetiza o conceito Engenharia de Software englobando diversas
características, definindo-o como “uma coleção de técnicas, metodologias e ferramentas que
dão suporte à produção de sistemas de alta qualidade, com um orçamento determinado e
13
dentro do cronograma estabelecido, considerando que mudanças nos requisitos e no ambiente
ocorrem frequentemente”.
Umas das técnicas para se alcançar qualidade e produtividade, imprimindo, ao mesmo
tempo, características que ofereçam maior suporte a mudanças, é o uso de padrões de projeto,
mais conhecido pelo termo em inglês design patterns. A idéia de projetar soluções a partir de
problemas já conhecidos, solucionados e documentados não é nova e não teve origem na
engenharia de software, o conceito de padrões, dentro dessa ótica, foi apresentada por um
arquiteto chamado Christopher Alexander [6] em 1977, no contexto de arquitetura. Segundo
ele cada padrão descreve um problema que ocorre repetidas vezes em nosso ambiente, para
então descrever a parte central da solução para esse problema que poderá ser utilizada
inúmeras vezes, sem nunca ter que fazê-lo duas vezes da mesma maneira.
Essa definição de padrão chamou a atenção da comunidade de engenharia de software
e em 1995 Erich Gamma, Richard Helm, Ralph Jonhson e Jonh Vlissides lançaram o livro
Design Patterns – elements of reusable object-oriented software [7]. Esse livro descreve 23
padrões de projetos sendo classificados, de acordo com os autores, em 3 categorias: padrões
de projeto de criação, estruturais e comportamentais. Segundo os autores a utilização de
padrões de projeto facilita o reúso de projetos e arquiteturas de software que tiveram êxito em
experiências anteriores. Os padrões também ajudam os desenvolvedores a modelar
alternativas que não comprometam o reúso, além de melhorar a documentação e,
consequentemente, a manutenção do software. De forma objetiva o padrão de projeto ajuda o
desenvolvedor a modelar o seu problema rápida e corretamente.
Os padrões de projeto representam soluções que foram generalizadas ao longo do
tempo para problemas de características recorrentes. Dessa maneira, justifica-se incluir, de
maneira objetiva, o estudo sobre padrões de projeto já em disciplinas de programação, em
especial, em programação orientada a objetos, visto que eles podem ser vistos como técnicas
avançadas e consolidadas de programação.
1.2. Objetivos
Sabendo da importância da utilização de padrões de projeto para a qualidade e
produtividade do desenvolvimento de software, este trabalho tem por objetivo propor um
arcabouço constituído por recursos técnicos de apoio ao ensino e aprendizagem para
introdução aos padrões de projeto em disciplinas de programação orientada a objetos.
14
Visa-se associar, de maneira efetiva, a teoria e a prática sobre o estudo de padrões de
maneira desvinculada de disciplinas de Engenharia de Software. O arcabouço inclui, para
cada um dos padrões, os seguintes pontos:
1) Introdução teórica ao padrão, identificando seus objetivos e benefícios;
2) Modelagem, através de diagrama de classes (UML) do padrão de projetos;
3) Descrição das classes envolvidas no padrão;
4) Implementação de um cenário simples com o uso do padrão;
5) Sugestão de exercício prático para um cenário diferente do apresentado, usando
o mesmo padrão.
Através desse arcabouço, pretende-se que o aprendizado e o ensino de orientação a
objetos, com premissas de baixo acoplamento, alta coesão e reusabilidade, se dê de forma
mais eficaz. Para isso, será sempre feita a contextualização por intermédio de exemplos
práticos, seguido da aplicação do padrão para aquele tipo de problema.
1.3. Organização do documento
Este trabalho está disposto da seguinte maneira: no primeiro capítulo, foram
apresentados uma breve introdução sobre engenharia de software e padrões de projeto seguido
da motivação deste trabalho, finalizando com a descrição de seus objetivos; no segundo
capítulo o conceito de padrão de projeto é descrito, em seguida discute-se o conceito de
programação orientada a objetos, logo após é proposto um arcabouço para o estudo de
padrões de projeto, para então apresentar os padrões nos moldes do arcabouço proposto; no
capítulo terceiro são descritas algumas dificuldades no aprendizado de padrões de projeto em
sala de aula; no quarto capítulo fazemos as considerações finais; na seção posterior temos as
referências bibliográficas; e, por fim, temos no apêndice, capítulo seis, padrões de projeto que
ainda encontram-se em construção nos moldes do arcabouço em questão.
15
2. Padrões de Projeto
2.1 Introdução
Gamma et al [7] definem que padrões de projeto “são descrições de objetos e classes
comunicantes que precisam ser personalizadas para resolver um problema geral de projeto
num contexto particular”. Eles surgiram como o resultado de experiências e soluções para
problemas recorrentes no desenvolvimento de software. Eles podem ser definidos, também,
como arquiteturas testadas para construir softwares orientados a objetos flexíveis e
sustentáveis [8].
O grupo conhecido como Gang of Four, formado por Erich Gamma, Richard Helm,
Ralph Johnson e John Vlissides, no livro lançado em 1995, descreve 23 padrões de projeto
agrupados em 3 categorias: criacionais, estruturais e comportamentais [7]. Ele ainda subdivide
cada categoria de padrão em padrões de classe ou de objeto.
Padrões de projeto criacionais estão relacionados com o processo de criação e
instanciação de objetos. Eles ajudam a projetar um sistema independente de como os objetos
são criados, compostos e representados. O padrão criacional de classe utiliza herança para
variar a forma como a classe é instanciada, enquanto o padrão criacional de objeto delega sua
instanciação para outro objeto.
Padrões do tipo estrutural fazem referência à forma como classes e objetos são
compostos para formar estruturas maiores. No padrão estrutural de classe, utiliza-se o
conceito de herança para compor implementações ou interfaces. Já nos padrões estruturais de
objeto são descritas formas de compor objetos para adquirir novas funcionalidades em tempo
de execução, o que não é possível através da composição estática de classes.
Padrões comportamentais descrevem a interação e atribuição de responsabilidades
entre objetos e classes. Eles não descrevem somente padrões de objetos ou classes mas
também padrões de comunicação entre eles. Esse tipo de padrão caracteriza os fluxos
complexos de controle que é difícil de acompanhar em tempo de execução. Os padrões
comportamentais de classe utilizam herança para distribuir comportamentos,
responsabilidades entre as classes, já os padrões de objeto utilizam a composição para fazer
essa distribuição.
Neste trabalho não temos como objetivo abordar todos os padrões de projeto, mas um
conjunto que seja suficientemente capaz de cobrir e exemplificar a estrutura do arcabouço
16
proposto. Assim, são expostos e discutidos os padrões criacionais Singleton e Abstract
Factory, os padrões estruturais Facade e Composite e os padrões comportamentais Iterator e
Strategy.
2.2 Elementos Indispensáveis de Programação Orientada a Objetos
Para estudar padrões de projeto, é necessário um conhecimento consistente do
paradigma de programação orientada a objetos. Esse paradigma baseia-se na análise, projeto e
desenvolvimento de sistemas a partir da utilização de objetos, que possuem atributos e
comportamentos, e se comunicam entre si, através de mensagens, colaborando para a
formação de sistemas. Esse paradigma é baseado nos seguintes conceitos: objeto, classe,
encapsulamento, herança e polimorfismo.
Os conceitos de classe e objeto estão intimamente ligados. O objeto pode ser definido
como uma entidade física, palpável ou mesmo conceitual, abstrata. Cada objeto tem suas
características, as quais chamamos de atributos, e comportamentos, que também podem ser
chamados de métodos. Existem objetos que possuem propriedades e comportamentos
similares, dando origem ao conceito de classe (de objetos). As classes definem um conjunto
de atributos e de comportamentos que são comuns a um conjunto de objetos. Enquanto um
objeto é uma entidade que executa uma função no sistema, uma classe define uma estrutura
comum aos tipos de objetos.
Na Figura 1 é apresentado um exemplo onde a classe Pessoa possui três atributos ,
nome, cpf e endereco, e três métodos, comer(), estudar(), dormir(). Tem-se também a
representação de dois objetos da classe Pessoa, identificados como joao e maria. A
convenção utilizada é inerente à Unified Modeling Language (UML).
Figura 1 – Exemplo de Classe e Objetos (UML).
O encapsulamento é um conceito que, como o próprio nome insinua, empacota, ou
seja, protege algumas propriedades do objeto de modificações que possam deixar o objeto em
17
estado inconsistente. Utilizando esse conceito obtemos vários benefícios, entre eles proteger
dados e operações, deixar o código mais legível e melhor estruturado, tornando a manutenção
menos custosa. Para dar suporte ao encapsulamento bem como à definição da interface
pública de um objeto (métodos e atributos expostos para outros objetos), a mairoria das
linguagens OO, como o Java, fornece quatro tipos de modificadores de acesso ou visibilidade:
public, private, protected e default.
Os atributos e métodos de um objeto ou classe são comumente chamados de membros
desse objeto ou classe. Desse modo, quando um membro de uma classe é definido como
public, significa que todos os objetos têm acesso a esse membro. Quando, por outro lado, esse
membro é definido como private, só quem tem acesso a esse membro é a própria classe. Os
modificadores protected e default são quase idênticos. Enquanto o membro default pode ser
acessado apenas pelas classes que pertencerem ao mesmo pacote, o membro protected pode
ser acessado também pelas suas subclasses, mesmo que suas subclasses não perteçam ao
mesmo pacote [9].
Herança
O conceito de herança é definido por [8] como uma forma de reutilizar o código
quando uma nova classe é criada, absorvendo membros de uma classe já existente,
economizando tempo durante o desenvolvimento do software.
Quando uma classe herda de uma outra classe existente, esta classe é chamada de
superclasse e aquela de subclasse. A subclasse herda as propriedades da superclasse, ou seja,
ela tem acesso aos atributos e métodos, dependendo dos modificadores de acesso, da
superclasse. Existem dois tipos de herança, simples e múltipla, na primeira uma classe só
pode herdar de somente uma superclasse diretamente, como é o caso do Java, já na herança
múltipla uma classe pode herdar mais de uma superclasse diretamente, um exemplo é a
linguagem C++.
Na Figura 2, é mostrado um exemplo onde as classes Estudante e Engenheiro são
subclasses da classe Pessoa. Desse modo, as subclasses herdam todos os atributos, pois eles
possuem modificadores de acesso protected, e todos os métodos da superclasse, pois possuem
modificadores de acesso public.
18
Figura 2 – Exemplo de Herança.
Polimorfismo
Polimorfismo, que vem do grego "muitas formas", permite que se utilizem objetos
pertencentes a classes relacionadas entre si pela relação de herança. Através de uma interface
única, o objetivo é o de tratar uma variedade de classes relacionadas de forma uniforme,
facilitando, por um lado, a especialização do comportamento e, por outro lado, simplificando
a adição de novas classes ao software sem requerer modificação nas classes pré-existentes, o
que imprime ao software características de baixo acoplamento.
O polimorfismo permite que o processamento de objetos que possuem a mesma
superclasse em uma hierarquia sejam tratados como se todas fossem objetos da superclasse
[8]. Utilizando o polimorfismo, pode-se projetar e implementar sistemas que são facilmente
extensíveis, ou seja, a adição de novas classes pode ser feita com pouca ou nenhuma
modificação, contanto que essas novas classes façam parte da hierarquia de herança do
sistema. Nota-se que polimorfismo está intimamente ligado à utilização de herança.
Entretanto, para que esse requisito seja cumprido de maneira correta, é necessário que a
hierarquia de classes atenda ao princípio de substituição de Liskov [10], que define que, se um
objeto do tipo S pode ser substituído em todos os locais onde um objeto do tipo T, é esperado,
então S é um subtipo de T. Se, em algum caso, essa substituição não resultar em um
comportamento correto e esperado, o reuso promovido pela herança foi utilizado de maneira
incorreta, modificando a semântica da interface comum encontrada na hierarquia de classes, e
o princípio de Liskov foi quebrado.
19
Na Figura 3 mostra-se um exemplo em Java utilizando conceito de polimorfismo. A
classe abstrata Animal que contém o método também abstrato display() é a superclasse das
classes concretas Dog e Cat que implementam o método herdado. A aplicação é representada
pela classe Application, onde, na linha 8, tem-se a instanciação de uma lista de objetos,
animalList, do tipo Animal. Ainda na aplicação, tem-se a instanciação de dois objetos, dog e
cat, do tipo Dog e Cat, respectivamente. Nas linhas 12 e 13 esses dois objetos são adicionados
na lista animalList. É criado, também na classe Application, o método showAnimals(), que
recebe como parâmetro um ArrayList<Animal>. Nesse método varrem-se todos os objetos e
chama-se, para cada um deles o método, display(). Note que, na linha 22, apesar de não saber
qual o tipo de animal, é chamado o método display() e será mostrado na tela o tipo
correspondente ao animal que ele representa.
É possível observar também que ao adicionar outra classe que herde de Animal, por
exemplo a classe Coelho, eu não precisarei modificar o código no método showAnimals(). A
única alteração será para colocar o coelho na lista de animais.
20
Figura 3 – Exemplo de Polimorfismo.
A aplicação dos conceitos de herança e polimorfismo, desenvolvidos através do
princípio de substituição de Liskov, são elementos de relevância indispensáveis à
compreensão e à utilização dos padrões de projeto.
2.3 Arcabouço para o Estudo de Padrões de Projeto
Como já mencionado anteriormente, a utilização de padrões de projeto é de extrema
importância no desenvolvimento de software de qualidade e com produtividade. Este trabalho
descreve os padrões de projeto com foco na introdução do seu aprendizado em disciplinas de
programação orientada a objetos. Utiliza-se um modelo que associa, de maneira efetiva, a
21
teoria e a prática na utilização dos padrões de projeto. O modelo proposto é formado por três
blocos principais: apresentação, desenvolvimento conceitual e desenvolvimento prático,
descritos em seguida:
Apresentação – Neste bloco é descrito formalmente o padrão de projeto e o
seu propósito, indicando-se como e onde ele pode ser utilizado, mas sem
maiores detalhamentos técnicos sobre implementação ou modelagem.
Desenvolvimento conceitual – Neste bloco são explorados os aspectos
conceituais e a motivação para usar o padrão estudado. Esse bloco subdivide-se
em duas partes.
o Descrição com exemplo – apresenta-se um problema técnico com o
exemplo e usa-se uma abordagem de tentativas intermediárias de
solução do mesmo, até chegar à estrutura efetivamente proposta pelo
padrão de projetos, mostrando, assim, os benefícios de sua utilização.
As estruturas são desenvolvidas com apoio em diagramas de classes
simplificados. Esses diagramas têm por objetivo enfatizar a solução do
padrão de projeto e não mostrar, necessariamente, todas as
propriedades das classes envolvidas. Não é obrigatória a utilização de
diagramas em UML, podendo-se utilizar outras formas de apresentação.
Entretanto, devido à representatividade e adequação dessa linguagem
de modelagem à Orientação a Objetos, seu uso é fortemente
recomendado. Eventualmente, os conhecimentos sobre diagramas de
classe podem ser desenvolvidos de maneira concomitante, caso isso não
tenha ocorrido ao longo do aprendizado do curso de orientação a
objetos.
o Codificação do exemplo – Nesta parte, é realizada e discutida a
codificação que materializa o exemplo desenvolvido na parte
precedente do bloco “desenvolvimento conceitual”. É mostrada e
discutida a implementação que realiza as relações apresentadas no
diagrama de classes apresentado. O código contendo os exemplos é
fornecido aos alunos, que podem executá-lo em seu ambiente de
programação e servirá de apoio para o “desenvolvimento prático”, que
consiste no último bloco do arcabouço.
22
Desenvolvimento Prático - Este bloco visa o desenvolvimento prático do
aluno através da solução para um ou mais problemas propostos. O bloco é
dividido em duas partes:
o Proposição – É feito o enunciado de um ou mais problemas que devem
ser diferentes do exemplo que foi apresentado no bloco
“desenvolvimento conceitual”. Evita-se, assim, a falsa sensação de
aprendizado, permitindo a consolidação do conhecimento acerca do
padrão em estudo.
o Extensão – Etapa que possibilita validar características de baixo
acoplamento e de reusabilidade da aplicação, propondo-se uma
modificação no enunciado proposto da parte anterior que deve ser
desenvolvido sem comprometer a estrutura proposta pelo padrão de
projetos em questão.
Figura 4 – Arcabouço para o estudo de padrões.
Na Figura 4, a estruturação seqüencial das seções de utilização do arcabouço é
apresentada pelas setas contínuas. Entretanto, usando-se as setas pontilhadas, procura-se
representar a não-linearidade do aprendizado, uma vez que, de qualquer ponto, pode-se
recorrer aos elementos constituintes das seções anteriores para auxiliar no desenvolvimento.
Note que, na estrutura apresentada na Figura 4, os blocos mais abaixo (em cinza) são
aqueles em que se desenvolve a prática laboratorial pelo aluno. Esses blocos representam dois
momentos particulares da aprendizagem de padrões de projeto. Nas próximas seções são
apresentados alguns padrões de projeto estruturados de acordo com o arcabouço proposto.
Embora os exemplos e os exercícios estejam sendo propostos em Java, o arcabouço proposto
Extensão
Apresentação
Descrição com Exemplo
Codificação do Exemplo
Proposição
Desenvolvimento conceitual
Desenvolvimento prático
23
não faz nenhuma restrição em relação à linguagem de programação ou tecnologia a serem
utilizados.
2.4 Strategy
Apresentação
O Padrão Strategy define uma família de algoritmos que são encapsulados em objetos
independentes dos objetos para os quais os algoritmos foram efetivamente concebidos.
Encapsular comportamentos associados a objetos pertencentes a uma hierarquia de
classes permite que os algoritmos variem independentemente dos clientes que o utilizam [7].
Além disso, a estrutura proposta pelo padrão Strategy possibilita efetuar a modificação do
comportamento de um objeto em tempo de execução.
Descrição com Exemplo
Para apresentar este padrão, é utilizado aqui o exemplo encontrado em [11], em que o
contexto é um jogo de computadores cujas personagens são patos de diferentes tipos. Os patos
do jogo possuem um conjunto de atributos e comportamentos semelhantes, mas podem
apresentar algumas especificidades entre si. Exploram-se os comportamentos de nadar e
produzir sons. Inicialmente, utilizando técnicas de orientação a objetos, propõe-se a definição
da superclasse abstrata Duck e, a partir dela, os demais tipos de patos são criados. O diagrama
de classes mostrado na Figura 5(a) exemplifica a especialização de Duck para apenas duas
subclasses.
Figura 5 – Diagrama de Classes Inicial (UML) Strategy
24
Supondo-se a necessidade de adicionar um novo comportamento para patos existentes
ou para novos patos que sejam agregados ao jogo, percebe-se que alguns deles podem voar.
Utilizando-de técnicas de orientação a objetos, propõe-se a criação do método fly() na
superclasse Duck, como mostra a Figura 5(b).
Figura 6 – Classes RubberDuck e WoodDuck
Entretanto, percebe-se que nem todos os patos podem voar. Por exemplo, patos de
borracha não voam. Uma alternativa inicial é sobrescrever o método fly() na classe dos patos
de borracha, RubberDuck, para modificar o seu comportamento em relação à ativação deste
método, como mostrado na Figura 6. Analisando-se a extensibilidade da solução, ao adicionar
outro tipo de pato, por exemplo, um pato de madeira, representado pela classe WoodDuck,
teremos que modificar novamente os métodos quack() e fly(), como mostrado na Figura 6.
Pode-se observar que essa solução possui várias desvantagens: (i) código duplicado entre
diferentes classes; (ii) alterações no comportamento em tempo de execução se tornam difíceis;
(iii) é difícil saber de antemão o comportamento de todos os patos.
Como generalizar o comportamento inserindo os métodos fly() e quack() na
superclasse Duck traz os problemas citados, explora-se outra alternativa com a utilização de
interfaces distintas para representar os dois comportamentos: Flyable e Quackable. Desse
modo, sugere-se que os patos que voam implementem a interface Flyable e,
consequentemente, o método fly(). Da mesma maneira, os patos que grasnam devem
implementar a interface Quackable e, consequentemente, o método quack(), como mostrado
no diagrama de classes da Figura 7. Embora, da perspectiva de um objeto, essa solução trate o
encapsulamento de uma maneira mais limpa, ela compromete a reutilização do código que
define cada um desses comportamentos. Imaginando um cenário em que diversos tipos de
pato apresentem o mesmo comportamento de voo, por exemplo, o mesmo código precisaria
ser replicado para todos os patos que o implementam. Além isso, uma alteração, mesmo que
pequena, no comportamento de vôo exigiria uma atualização em todas as classes que o
implementam, tornando a manutenção uma tarefa extremamente custosa e podendo gerar
inconsistência no sistema.
25
Figura 7 – Classes RubberDuck
Para solucionar os problemas citados, propõe-se o padrão de projeto Strategy. A
solução, mostrada na Figura 8, é o encapsulamento dos comportamentos em objetos distintos,
que são agregados como atributo dos objetos que os utilizam. Isso é feito por meio da criação
de interfaces para representar cada comportamento, nesse caso as interfaces FlyBehavior e
QuackBehavior. A partir delas são criadas classes concretas que implementam cada
comportamento: FlyWithWings, FlyNoWay, Quack, Squeak e MuteQuack. Observa-se que na
classe Duck, agora, tem-se como atributo dois objetos, um do tipo QuackBehavior e outro do
tipo FlyBehavior, que serão responsáveis pela realização do comportamento específico. A
classe apresenta, dessa forma, os métodos performFly() e performQuack(), no lugar dos
métodos fly() e quack(), e setQuackBehavior() e setFlyBehavior(). Estes últimos permitem
configurar o comportamento de vôo para cada pato de forma dinâmica. Note que a mudança
de comportamento ao longo da interação com jogos eletrônicos é uma característica comum
às personagens dos mesmos. No contexto aqui explorado, um pato poderia, por exemplo,
adquirir como bônus a habilidade de voar logo após uma passagem de fase do jogo.
26
Figura 8 – Diagrama de Classes Final (UML) Strategy
Codificação do Exemplo
Na Figura 9 temos a implementação da interface FlyBehavior e das classes concretas
FlyNoWay e FlyWithWings que implementam essa interface. Na Figura 10, temos a
implementação da outra interface, QuackBehavior, e das classes concretas Quack, Squeak e
MuteQuack que implementam essa interface.
Figura 9 – Classes FlyBehavior.java, FlyWithWings.java e FlyNoWay.java
27
Figura 10 – Classes QuackBehavior.java, Quack.java, MuteQuack.java e Squeak.java
Na classe abstrata Duck, Figura 11, temos a implementação dos métodos
performQuack() e performFly(). Neles são executados os comportamentos de grasnar e voar,
respectivamente, chamados através dos objetos quackBehavior e flyBehavior. Note que o
comportamento que será executado dependerá da instância desses objetos. Os métodos
setQuackBehavior() e setFlyBehavior() também são implementados, permitindo a mudança
de comportamento em tempo de execução.
28
Figura 11 – Classe Duck.java
Na Figura 12, temos as classes MallardDuck, WoodDuck, RedheadDuck e
RubberDuck que estendem a classe abstrata Duck. É importante observar que no construtor
dessas classes temos a instanciação dos objetos quackBehavior e flyBehavior, herdados da
classe Duck, de acordo com cada tipo de pato, o que estabelece a relação de agregação entre
os patos e os seus comportamentos. A agregação é “simples” visto que os objetos relativos
aos comportamentos persistem à eventual eliminação do pato que o utilize.
29
Figura 12 – Classes MallardDuck.java, ReadheadDuck.java, WoodDuck.java e RubberDuck.java
Na Figura 13, temos uma pequena aplicação, classe Application, onde mostramos o
funcionamento das classes discutidas anteriormente.
Figura 13 – Classe Application.java
Proposição
Um Sistema de Controle de Produção Científica (SCPC), em desenvolvimento para
uma instituição de ensino, permite a organização de diferentes tipos de documentos, tais como
monografias, artigos, relatórios e estatísticas. Possuindo os diferentes documentos um
conjunto de características em comum, para diversas funcionalidades do sistema, eles são
tratados de maneira indistinta. Entretanto, algumas operações, embora comuns a todos os
tipos de documento, apresentam especificidades, como é o caso da operação de codificação.
Relatórios e estatísticas são codificados em HTML, para serem apresentadas na interface de
30
navegadores Web, enquanto que artigos e monografias são codificados em PDF. Além disso,
alterando-se o modo de operação do sistema, deve ser possível codificar também em PDF os
documentos do tipo relatório e estatística.
A partir do problema apresentado, prospecte uma solução reutiliável que leve em
consideração a possibilidade de diferentes formas de condificação para todos os tipos de
documento, sem deixar de considerar suas semelhanças. Com base no padrão Strategy,
elabore um diagrama de classes capaz de abstrair todos os tipos de objetos envolvidos na
solução. Em seguida, implemente a solução modelada.
Lembre-se que a solução deve tratar tanto a especificidade do comportamento de
codificação para os diferentes tipos de documento como a dinâmica associada à alteração no
modo de operação para os documentos do tipo relatório e estatística.
Extensão
Uma modificação foi solicitada para o SCPC. Será preciso agora adicionar um outro
tipo de documento, o edital, e outro tipo de codificação, XML, visto que o sistema será
integrado ao sistema da Pró-Reitoria de Pós-Graduação. Modifique o diagrama de classes e a
implementação desenvolvidos para contemplar essas novas características no sistema.
Verifique se foi necessário modificar alguma das classes ou operações do modelo
original. Caso isso tenha acontecido, é possível que você não tenha desenvolvido o padrão
Strategy corretamente.
2.5 Abstract Factory
Apresentação
O padrão de projeto Abstract Factory tem por objetivo fornecer uma interface para
criação de famílias de objetos relacionados ou dependentes sem especificar suas classes
concretas [7]. Esse padrão permite que o sistema determine em tempo de execução qual
subclasse irá instanciar um objeto. Pois em determinadas aplicações a subclasse não é
conhecida durante o desenvolvimento ou simplesmente não se deseja especificar a subclasse.
Descrição com Exemplo
Tomando como exemplo a construção de um toolkit, que é um conjunto de
componentes básicos de interface gráfica com o usuário, por exemplo, botões, janelas, menus,
31
entre outros, para uma aplicação que será executada pelo sistema operacional Windows.
Inicialmente, a solução modelada para este toolkit utiliza a classe Button para representar o
componente gráfico botão, que possui o método paint() que irá desenhá-lo na tela, mostrada
na Figura 14(a). A partir dela são criados todos os botões da aplicação, que são utilizados,
nesse exemplo, pela classe Application.java mostrada na Figura 14 (b).
Figura 14 – (a) Classes Button e Menu (b) Classe Application.java
Supondo agora que essa aplicação deverá ser executada também no sistema
operacional Linux, foi desenvolvido um novo diagrama de classes, mostrado na Figura 15(a).
Utilizando os conceitos de Orientação a Objetos a classe Button agora se tornou abstrata, e as
classes WinButton e LinuxButton estendem dela, onde cada uma dessas classes irá
implementar o método paint() que irá desenhar o componente de acordo com seu sistema
operacional.
Figura 15 – (a) Classes WinButton e LinuxButton (b) Classe Application.java
32
Entretanto, utilizando essa solução, nota-se que as características de baixo
acoplamento e de reusabilidade da aplicação não são atendidas, pois em uma nova expansão
da aplicação para outro sistema operacional, teria-se que modificar novamente todas as partes
da aplicação onde os botões fossem criados, tornando a manutenção custosa e podendo gerar
inconsistências na aplicação.
Portanto, para que o toolkit da aplicação seja portátil para diferentes sistemas
operacionais, Windows ou Linux, é necessário que esses componentes não sejam codificados
exclusivamente para uma determinada interface gráfica, pois isso dificultaria a mudança de
uma interface para outra, com isso dificultando também a manutenção desse código.
Esse problema pode ser resolvido, utilizando o padrão de projeto Abstract Factory,
criando-se uma classe abstrata GUIFactory, mostrada na Figura 16, que será a classe base
para a criação dos componentes gráficos. Nessa classe serão definidos os métodos abstratos
para criação dos componentes, no nosso exemplo o método createButton(). Será também
nessa classe definido, através do método getFactory(), qual factory (fábrica em inglês),
WinFactory ou LinuxFactory, será utilizada para a criação de fato dos componentes. As
classes WinFactory e LinuxFactory são subclasses concretas da GUIFactory, são nessas
classes concretas que os componentes gráficos serão construídos de fato, através dos métodos
abstratos herdados da classe mãe, que serão implementados por elas, ou seja, a partir de agora
a criação dos componentes gráficos será de responsabilidade dessas duas classes concretas.
As classes que representam os componentes gráficos também serão criadas utilizando
a mesma ideia. No nosso exemplo, a classe abstrata Button possui o método abstrato paint().
A partir dessa classe serão criadas as suas subclasses, WinButton e LinuxButton, e nelas
implementados o método herdado paint().
Neste momento a criação de componentes gráfico torna-se transparente para a
aplicação, pois ela enxergará somente as classes GUIFactory e Button, como mostra o
diagrama de classes na Figura 16. Com isso o problema de portabilidade é resolvido sem
precisar modificar o código na aplicação que somente irá se preocupar com a criação dos
componentes, sem se preocupar em qual sistema operacional ela será executada. Internamente
será definido qual factory utilizar, WinFactory ou LinuxFactory, e, consequentemente, que
componentes criar, WinButton ou LinuxButton.
33
Figura 16 – Diagrama de Classes (UML) Abstract Factory
Codificação do Exemplo
Na Figura 17 temos a implementação da classe GUIFactory. Note que no método
estático getFactory() na linha 7 é definido em qual sistema operacional a aplicação está
rodando em tempo de execução e dependendo dele será instanciado um objeto do tipo
WinFactory ou LinuxFactory. É criado também, como dito anteriormente, o método abstrato
createButton(), que será implementado pelas suas subclasses para criar o componente gráfico
botão.
34
Figura 17 – Classe GUIFactory.java I
As classes WinFactory e LinuxFactory estendem de GUIFactory, Figura 18. Note que
o método abstrato createButton() é implementado e dependendo da classe ele irá retornar um
objeto do tipo WinButton ou LinuxButton.
Figura 18 – Classes WinFactory.java e LinuxFactory.java
A classe abstrata Button, Figura 19, é criada com o método abstrato paint(), que
deverá ser implementada pelas suas subclasses concretas.
Figura 19 – Classe Button.java
35
WinButton e LinuxButton, Figura 20, são as classes concretas que herdam de Button.
Nelas será definido como o botão será criado de fato, dependendo do sistema operacional,
através da implementação do método paint().
Figura 20 – Classes WinButton.java e LinuxButton.java
Note que na aplicação, aqui representada pela classe Application, mostrada na Figura
21, utiliza-se somente as classes GUIFactory e Button, que são as classes bases para a criação
dos componentes gráficos. Na linha 7 obtém-se o objeto factory através do método
GUIFactory.getFactory(), dessa forma vê-se que, para a aplicação, é transparente se esse
método irá retornar um objeto do tipo WinFactory ou LinuxFactory. Na linha 8, o objeto
button recebe um objeto através da chamada método factory.createButton() e posteriormente,
na linha 9, temos a chamada do método button.paint(). Note, mais uma vez, que para a
aplicação o tipo de botão que será criado é indiferente, bem como a forma como esse botão
vai ser desenhado na tela. Desse modo a aplicação não precisa se preocupar em qual sistema
operacional ela será executada, facilitando o seu desenvolvimento.
36
Figura 21 – Classes Application.java
É importante notar que ao surgir uma necessidade de expandir essa aplicação para
outro sistema operacional, por exemplo o Lion da Apple, não se faz necessário grandes
mudanças. Primeiramente iremos criar a classes LionFactory e LionButton, herdando de
GUIFactory e Button, respectivamente. Por fim, modificaríamos apenas o método
getFactory() da classe GUIFactory, como mostrado na Figura 22, mudança essa que não
impacta em nenhuma das classes previamente implementadas, principalmente na aplicação.
Figura 22 – Classe GUIFactory.java II
37
Proposição
Uma montadora de veículos está desenvolvendo um sistema para auxiliar sua linha de
montagem. Esse sistema será responsável pelo controle completo da montagem dos veículos
dessa montadora, desde o controle de estoque das peças que serão utilizadas até a montagem
das peças no veículo.
Sabe-se que cada modelo de veículo tem sua própria linha de montagem. Pois, apesar
de alguns modelos compartilharem algumas peças comuns, a montagem e grande parte das
peças são diferentes.
Inicialmente, a montadora quer que o sistema funcione para dois modelos de carro,
modelo X e modelo Y, a partir do qual serão feitos testes e será decidido pelos sócios se o
sistema será implantado para outros modelos ou não. Desenvolva, a partir do problema
descrito, uma solução para esse sistema, tendo como foco o baixo acoplamento e a
reusabilidade. Utilizando o padrão Abstract Factory elabora o diagrama de classes e
implemente a solução modelada.
Extensão
Após inúmeros testes a montadora aprovou o sistema de linha de montagem
desenvolvido, para os modelos de carro X e Y. Agora ela quer que a linha de montagem de
seu mais novo carro, modelo Z, também seja controlada por esse sistema. Modifique o
diagrama de classes e a implementação desenvolvidas para contemplar essa modificação.
Analise onde as modificações foram realizadas, se foi preciso modificar alguma das
classes implementadas ou as operações do sistema anterior, pois, em caso afirmativo, é
possível que o padrão Abstract Factory não tenha sido desenvolvido corretamente.
2.6 Composite
Apresentação
O padrão de projeto Composite consiste na criação de uma estrutura em árvore para
compor objetos utilizando componentes em estruturas hierárquicas. Cada um desses
componentes implementa uma mesma interface ou estende uma mesma superclasse. Esse
padrão permite que o cliente utilize esses componentes e a composição desses componentes
de forma uniforme.
38
Descrição com Exemplo
De acordo com [7] o desenvolvimento de interfaces gráficas fazem com que os
desenvolvedores construam diagramas complexos a partir de componentes gráficos simples.
O desenvolvedor pode agrupar um grupo de componentes para formar uma componente
maior, que por sua vez será utilizado para formar um outro componente ainda maior e assim
sucessivamente. Uma simples implementação seria a definição de classes de componentes
gráficos primitivos como Texto e Linha e de classes que funcionariam como containers para
essas classes primitivas, por exemplo Imagem.
Entretanto, o código utilizando essa abordagem teria que tratar as classes primitivas e
containers de forma diferente, embora na maioria das vezes elas tenham comportamentos
muito semelhantes. Fazer essa distinção faz com que o código se torne mais complexo na
medida que a aplicação cresce. Nesse contexto o padrão Composite propõe uma solução
recursiva para que os desenvolvedores não precisem fazer essa distinção.
Esse padrão tem como principal característica uma classe abstrata que representa tanto
o tipo primitivo como também o container, que é formado por um ou mais objetos do tipo
primitivo. Essa classe irá conter as características do objeto individual e as operações que a
composição desses objetos possuem. Desse modo o desenvolvedor não irá se preocupar com
as diferenças entre as composições e os objetos individuais, passando a tratar cada um deles
uniformemente.
Na Figura 23 temos o diagrama de classe utilizando o padrão de projeto em questão,
tomando como exemplo a construção de interfaces gráficas. Note que a classe abstrata
ComponenteGrafico representa os objetos individuais, como Linha e Texto, e a composição
desses objetos, nesse exemplo representado pela classe Imagem.
Observe que as classes Linha e Texto, apesar de serem subclasses da
ComponenteGrafico, não implementam os métodos relacionados a composição de objetos, já
que são classes primitivas e não são compostas por outros objetos. Essas classes só
implementam o método desenhar, que é característico da classe primitiva.
A classe Imagem, que representa a composição de objetos gráficos, por sua vez
implementa o método desenhar de forma que esse execute o método desenhar de cada um dos
objetos gráficos que o compõem. Implementam-se também os métodos que relacionam a
composição desses objetos como Adicionar e Remover.
39
Figura 23 – Diagrama de Classes (UML) Composite
Codificação do Exemplo
Está implementado em Java o exemplo discutido acima de forma simplificada. A
classe ComponenteGrafico é abstrata e possui 4 métodos que deverão ser implementados
pelas suas subclasses. Essa classe representa tanto os objetos primitivos quanto a composição
deles.
Figura 24 – Classe ComponenteGrafico.java
As classes Linha e Texto são as classes primitivas que herdam de ComponenteGrafico.
Essas classes implementam o método desenhar de acordo com o tipo do componente gráfico.
Os outros métodos herdados da superclasse são implementados de forma a não executarem
nenhuma ação significativa por não fazerem sentido para elas.
40
Figura 25 – Classes Linha.java e Texto.java
A classe Imagem, que também herda de ComponenteGrafico, implementa os métodos
adicionar, remover e getChild, já que ela é uma classe que é formada a partir da composição
de vários objetos do tipo ComponenteGrafico, inclusive dela mesma. Note que nessa classe
temos um atributo que não existe nas outras classes, uma lista de objetos do tipo
ComponenteGrafico. Essa lista simboliza a composição desse objeto a partir de outros
objetos, isso fica melhor entendido quando observamos a implementação do método
desenhar. Ao executar esse método ele irá chamar o método desenhar de cada um dos objetos
que o compõem, desse modo o conjunto dos objetos desenhados formam o objeto da classe
Imagem.
41
Figura 26 – Classe Imagem.java
Proposição
Um engenheiro de software está criando uma nova linguagem de programação, para
isso ele terá que definir uma gramática com os comandos que serão utilizados nessa nova
linguagem. Faça o diagrama de classes para representar os comandos dessa linguagem da
melhor forma possível utilizando o padrão discutido.
Dica: divida os comandos da gramática em comandos simples e comandos compostos.
Extensão
Após feito o diagrama de classes no exemplo proposto, deseja-se adicionar dois novos
comandos, um simple e um composto. Modifique o diagrama de classes para contemplar os
novos comandos a serem implementados.
42
2.7 Padrões de Projeto em Construção
No Apêndice deste trabalho estão descritos mais três padrões de projeto que ainda se
encontram em construção no formato do arcabouço proposto. Esses padrões são: Facade, no
apêndice A; Singleton, no apêndice B; e o Iterator, no apêndice C.
43
3. A Gênese do Arcabouço Proposto
Embora o arcabouço proposto neste trabalho não tenha ainda sido utilizado na prática
em um curso de Programação Orientada a Objetos, sua concepção é fruto de um estudo
teórico e da análise dos resultados da aplicação de uma atividade prática sobre Padrões de
Projetos em duas disciplinas de Engenharia de Software no Departamento de Engenharia de
Teleinformática da Universidade Federal do Ceará. Uma das disciplinas foi ministrada no
curso de graduação e a outra no curso de pós-graduação (neste último caso, em forma de
Estudos Especiais).
Ao longo do semestre, o professor da disciplina inferiu que a maioria dos alunos
possuíam sérias dificuldades conceituais em OO, especialmente em relação ao uso de
interfaces e na aplicação do polimorfismo. Essa dificuldade comprometeu de maneira
substancial o processo de aprendizagem de alguns conteúdos da disciplina, como a UML,
tendo sido revelada a partir de atividades de concepção de diagramas de classe e de sequência
em um dos trabalhos propostos na disciplina. Nesse sentido, faz-se em seguida um breve
relato do desenvolvimento do curso de Engenharia de Software no semestre letivo 2012-1,
registrando-se algumas observações e percepções identificadas pelo professor ou retiradas das
avaliações que foram realizadas ao final de cada atividade pelos alunos.
As duas disciplinas, de graduação e pós-graduação, foram contextualizadas sobre um
mesmo problema-base, o qual foi trabalhado ao longo de todo o semestre letivo. Várias
atividades foram propostas com base na metodologia PBL (Problem Based Learning),
fazendo-se algumas adaptações em termo de estruturação e de orientação para compatibilizar
a metodologia à realidade dos alunos (e do próprio professor), visto que possuíam pouco ou
nenhum contato com esse tipo de abordagem. As primeiras atividades foram relativamente
orientadas para a Análise e Projeto Orientados a Objeto (APOO). Já na segunda atividade,
trabalhou-se com os principais casos de uso identificados para o problema base. Solicitou-se
aos alunos que fizessem, para cada um dos principais casos de uso identificados, a abstração
dos objetos do domínio do negócio com ele comprometidos. Em seguida, pediu-se a
elaboração, também para cada caso de uso, de um diagrama de sequência que representasse o
desenvolvimento da solução do mesmo. Para isso, após rápida definição dos conceitos de
objetos de entidade, de controle e de fronteira, sugeriu-se a abstração de outros tipos de objeto
(no domínio da solução) que auxiliassem a implementação do caso de uso, o que, em um
sistema OO, se dá através da interação, ou comunicação, entre objetos.
44
A atividade foi realizada em grupo, como sugere a PBL. Os alunos manifestaram
grande dificuldade em seu desenvolvimento, o que foi, inicialmente, percebido pelo professor
como uma dificuldade relacionada com a metodologia. Entretanto, a partir das apresentações
e discussões realizadas em sala de aula, notou-se uma limitação particular em lidar com
soluções orientadas a objeto. Para os alunos de graduação, essa situação foi percebida com
alguma surpresa, visto que, na grade curricular do curso de Engenharia de Teleinformática,
existe uma disciplina semestral especificamente voltada para a Orientação a Objetos
(Técnicas de Programação para Engenharias II). No caso da pós-graduação, existe uma
heterogeneidade natural, devido ao fato de os alunos serem egressos de diversos cursos,
alguns não tendo tido qualquer tipo de formação em Orientação a Objetos.
A partir das percepções relatadas anteriormente, e tendo em perspectiva o avanço da
disciplina com incursões em Projeto (design) OO, com definição da arquitetura do sistema,
componentização, especificação das interfaces entre os componentes, entre outros aspectos
que dependem de um backgroud razoavelmente consistente em OO, foi preciso estabelecer
novos parâmetros para o aprendizado de Engenharia de Software. Em especial, percebeu-se
que seria praticamente inútil trabalhar Padrões de Projeto, que fazem parte do programa da
disciplina, sem o devido amadurecimento de conceitos de OO.
Figura 27 – Enunciado da Atividade sobre Design Patterns
Desenvolva um estudo sobre padrões de projetos e, para cada padrão definido para o seu grupo, registre
seus estudos em uma documentação contendo:
1. A apresentação do estudo sobre o padrão
2. Uma possível aplicação do padrão estudado no sistema “Máquinas de Alimentos em Redes” sobre
o qual vem sendo contextualizadas as nossas atividades. Na apresentação da aplicação, desenvolva:
2.1. o diagrama de classes para a situação-exemplo;
2.2. a implementação de teste com uma linguagem Orientada a Objetos (não precisa ser uma
aplicação completa, basta ser uma simulação de uso para as classes que compõem a
implementação do padrão!).
3. Observações:
3.1. Caso não consiga identificar uma aplicação no sistema que vem sendo trabalhado, exemplifique
o uso do padrão em um outro contexto.
3.2. Sugere-se a geração do código a partir do diagrama de classes concebido, usando, para isso,
uma ferramenta CASE (EA, Astah, Umbrello, etc.), além da complementação do código com o
desenvolvimento dos métodos especificados.
Lista de padrões a serem estudados por grupo (podendo ser estendida, caso o grupo deseje):
Grupo A - ADAPTER, OBSERVER, PROXY, COMMAND, BRIDGE, DECORATOR
Grupo B – ADAPTER, OBSERVER, PROXY, COMMAND, FACADE, TEMPLATE
Grupo C – ADAPTER, OBSERVER, PROXY, COMMAND, ITERATOR, COMPOSITE
Grupo D – ADAPTER, OBSERVER, PROXY, COMMAND, FACTORY, STRATEGY
Grupo E – ADAPTER, OBSERVER, PROXY, COMMAND, ITERATOR, STATE
Grupo F – OBSERVER, COMMAND, COMPOSITE (individual)
45
Assim, foi proposta uma atividade sobre Padrões de Projeto na disciplina de
Engenharia de Software que está na gênese do arcabouço formalizado neste trabalho de
conclusão de curso. O enunciado desta atividade encontra-se do quadro da Figura 27.
Durante a concepção desta atividade, considerou-se a possibilidade de reconstrução
dos conceitos fundamentais de Orientação a Objetos, pedindo-se, não só a modelagem de
situações de uso dos padrões de projeto, mas também a implementação da solução usando
uma linguagem OO.
A atividade foi desenvolvida em dupla. A quantidade de padrões solicitada por grupo
exigia que todos os componentes realizassem atividades de modelagem e programação,
inviabilizando o acúmulo da atividade de programação para um único aluno por dupla.
Alguns padrões foram desenvolvidos por todos os grupos visando a discussão e a comparação
das soluções em sala de aula.
Os resultados dessa atividade foram bastante significativos. Além da documentação
solicitada, associada aos estudos sobre o padrão, incluindo a codificação de uma situação
exemplo, pediu-se a apresentação do diagrama de classes em sala de aula, a explicação da
solução e a demonstração do seu uso. Durante as apresentações, tanto no curso de graduação
como no de pós-graduação, verificou-se que algumas soluções desenvolvidas resolviam o
problema ao qual se propunham, mas não se aplicavam corretamente ao padrão. Em geral, a
verificação da inconsistência era feita quando se solicitava a extensão da solução para
verificar a reusabilidade do padrão. Apesar do estresse inicial dos alunos, visto que as
modificações eram feitas “ao vivo”, com projeção do desktop do computador usado no
quadro, a atividade e a discussão se mostrou altamente produtiva e didática, reforçando os
conceitos de OO e mostrando o alto grau de reusabilidade e baixo acoplamento propiciado
pelos padrões de projeto.
A partir dessa observação, foram estruturados os blocos do arcabouço proposto neste
trabalho, que estão representados na Figura 4. Fruto das experiências relatadas no parágrafo
anterior, a ênfase é dada para o último bloco do arcabouço, relativo ao desenvolvimento
prático, onde na primeira parte é desenvolvido uma solução para um problema proposto,
enquanto na segunda parte valida-se a primeira, demonstrando a extensibilidade provida pelo
padrão.
Outro aspecto que apresentou uma particular dificuldade para o desenvolvimento da
atividade de padrões de projeto, que foi registrado entre os alunos de engenharia de software
nos dois cursos, foi a necessidade de encontrar a aplicabilidade do padrão de projeto em
46
estudo para o sistema sobre o qual se desenvolviam as atividades da disciplina. Esse tipo de
dificuldade pode comprometer o tempo do aluno e retirar o foco de seu aprendizado da
programação Orientada a Objetos. Assim, tendo em vista a sua aplicação em disciplinas de
programação, e não em disciplinas de engenharia de software, estabeleceu-se que o arcabouço
deve enunciar diretamente uma situação problema apropriada ao uso do padrão.
Ao final da atividade, foi solicitado aos alunos do curso de graduação que
preenchessem um formulário de avaliação. As questões eram avaliadas pelos alunos usando
uma escala de representação variando de A (nota máxima) a E (nota mínima), ou X (não se
aplica). Dentre outros aspectos menos relevantes para este trabalho, as quatro perguntas
abaixo foram colocadas:
1) Como você avalia os seus CONHECIMENTOS SOBRE ORIENTAÇÃO A
OBJETOS ANTES DO DESENVOLMENTO DESTA atividade?
2) Como você avalia os seus CONHECIMENTOS SOBRE ORIENTAÇÃO A
OBJETOS DEPOIS DO DESENVOLMENTO DESTA atividade?
3) Após a realização do trabalho, como você avalia a sua ABSORÇÃO DO
CONTEÚDO “DESIGN PATTERNS”?
4) Caso você venha a trabalhar ou trabalhe com desenvolvimento de software,
como você avalia a contribuição dessa atividade NA SUA VIDA
PROFISSIONAL?
A fim de computar as médias das respostas, os conceitos associados atribuídos pelos
alunos foram substituídos por valores numéricos da seguinte maneira: A-10, B-8, C-6, D-4, E-
2 e X-0.
A Tabela 1 resume as médias das avaliações realizadas pelos onze alunos da turma de
graduação:
Tabela 1– Avaliação média dos alunos da turma de graduação para a atividade sobre padrões de projeto
na disciplina de Engenharia de Software (2012-1)
Questão Média
1 6,6
2 8,6
3 7,8
4 8,8
47
Pelas médias apresentadas nas questões 1 e 2, respectivamente 6,6 e 8,6, percebe-se
que os alunos experimentaram um significativo aumento na sensação de aprendizado sobre o
conteúdo OO após a realização da atividade que durou por volta de três semanas. Essa
sensação foi observada também pelo professor em função do desempenho e da confiança
demonstrada pelos alunos durante a apresentação das soluções relativas à atividade.
Entretanto, no que concerne o aprendizado de padrões de projeto, foi registrada uma absorção
menor dos conceitos, revelado pela média 7,8 para a pergunta 3. Isso foi reforçado por
algumas observações escritas pelos alunos nos formulários das avaliações (em espaços
destinados a comentários livres) e em algumas manifestações verbais. Os comentários
convergiam para o fato de que seria necessário um tempo maior para trabalhar
satisfatoriamente com todos os padrões propostos, o que seria praticamente inviável em uma
disciplina de 4 créditos. Essa característica parece endossar a necessidade de uma dedicação
maior aos padrões de projeto no contexto de disciplinas de programação, previamente ao
ensino de Engenharia de Software.
Por último, a média da pergunta 4 mostra que os alunos da graduação que cursaram a
disciplina têm em perspectiva que o aprendizado de padrões de projeto possui importância
significativa em seu futuro profissional. É importante relatar que apenas um ou dois dos
alunos dessa turma apresentam uma perspectiva profissional mais voltada para a área de
hardware, o que pode ter influenciado para menos a média dessa questão.
A análise exposta neste capítulo fundamenta, assim, o arcabouço proposto, embora se
tenha consciência da necessidade de experimentações e readequações sucessivas a partir do
momento em que ele seja efetivamente empregado como ferramenta de apoio didático ao
processo de ensino e aprendizagem em disciplinas de programação.
48
4. Considerações finais
Dada a importância da utilização de padrões de projeto na qualidade do software a ser
produzido, foi desenvolvido neste trabalho um arcabouço para servir de apoio ao aprendizado
em disciplinas de programação orientada a objetos. Esse arcabouço é constituído por cinco
elementos que descrevem o comportamento de cada padrão de projeto: apresentação,
descrição com exemplo, codificação do exemplo, proposição e extensão.
Esse trabalho selecionou um conjunto de padrões que são julgados importantes e para
cada um deles descreve o que tem de mais importante na literatura [5], [7], [8], [11], [17],
sempre com foco na didática de ensino. Para cada padrão de projeto mostra-se em quais
aplicações ele é utilizado e destaca-se um problema em específico, tomando-o como exemplo,
que seja mais conveniente para facilitar o entendimento da solução que o padrão fornece. A
partir desse exemplo é criado o diagrama de classes da solução. O exemplo também é
implementado em Java para demonstrar o comportamento do padrão. Por fim, é apresentado
um exercício proposto e uma extensão do mesmo com o objetivo de fixar o entendimento do
padrão de projeto em questão.
O arcabouço possui duas seções principais no aprendizado de padrões de projeto,
“Proposição” e “Extensão”. Nessas seções são colocados problemas com o objetivo de prover
um melhor entendimento, na prática, do padrão de projeto, mostrando sua utilidade e sua
versatilidade na evolução e modificação da aplicação. Contudo, a elaboração de problemas
que sejam didáticos e que abordem o padrão na melhor forma possível não é uma tarefa fácil,
visto que é necessário que haja experiência em sala de aula notando as dificuldades e
deficiências dos alunos. Dessa forma, é necessário que haja uma utilização na prática do
arcabouço, tendo como propósito sua evolução de acordo com as dificuldades que forem
surgindo.
Apesar de o arcabouço proposto ainda não ter sido utilizado na prática em uma
disciplina de programação orientada a objetos, através de um estudo teórico e da análise de
uma atividade sobre padrões de projeto em duas disciplinas no Departamento de Engenharia
de Teleinformática da UFC, sendo uma na graduação e outra na pós-graduação, pode-se
inferir que as dificuldades conceituais em Orientação a Objetos, em especial no uso de
interfaces e de polimorfismo para solução de problemas, comprometeu substancialmente o
processo de aprendizagem. Percebeu-se que seria praticamente inútil trabalhar Padrões de
49
Projeto sem o devido amadurecimento de conceitos de OO. Desse modo, justifica-se incluir,
de maneira objetiva, o estudo sobre padrões de projeto já em disciplinas de programação
orientada a objetos, visto que eles podem ser vistos como técnicas avançadas e consolidadas
de programação.
A utilização deste trabalho em disciplinas de programação orientada a objetos torna o
aprendizado de padrões de projeto mais eficaz, já que a teoria é vista de forma mais próxima
da prática, por ter exercícios para a melhor compreensão do padrão, e mais objetiva, pois,
diferente de alguns livros, onde há, por vezes, exaustão de informações desnecessárias, neste
trabalho o padrão é visto tendo como foco a solução para o problema apresentado.
50
5. Referências Bibliográficas
[1] IEEE. Institute of Eletrical and Eletronics Engineers. Disponível em
<http://www.ieee.org>. Último acesso em 15 de maio de 2012.
[2] SOMMERVILLE, I. Engenharia de software. 8ª Edição. São Paulo: Pearson/Prentice
Hall, 2007. 522 p.
[3] ABNT (Associação Brasileira de Normas Técnicas). NBR ISO 9000/2005: Sistema de
Gestão da Qualidade - Fundamentos e Vocabulário. Rio de janeiro, 2005.
[4] PRESSMAN, R. S. Engenharia de software. 6ª Edição. São Paulo: McGraw-Hill, 2006.
720 p.
[5] Bruegge, B; Dutoit, A. H. Object-Oriented Software Engineering: Using UML, Patterns
and Java. 2nd Ed. Prentice Hall, 2003. 800 p.
[6] Alexander, C; Ishikawa, S; Silverstein, M; Jacobson, M; Fiksdahl-King, I; Angel, S. A
Pattern Language. Oxford University Press, NewYork, 1977.
[7] GAMMA, E; HELM, R; JOHNSON, R; VLISSIDES, J. Design Patterns - Elements of
Reusable Object-Oriented Software. 1a Edição. Addison-Wesley, 1995. 395p.
[8] DEITEL, H. M; DEITEL, P. J. Java: Como programar. 6ª Edição. São Paulo:Pearson,
2006. 1110 p.
[9] Sierra, K; Bates, B. SCJP Sun Certified Programmer for Java 6 Study Guide (Exam
310-065). McGraw-Hill, 2008.
[10] Liskov, B.H; Wing J.M. Behavioral Subtyping Using Invariants and Constraints.
Formal Methods for Distributed Processing, an Object Oriented Approach, Howard
Bowman and John Derrick, editors, Cambridge University Press, 2001, pp. 254-280.
[11] Freeman, E; Freeman, E; Sierra, K; Bates, B. Head First: Design Patterns. 1st Ed.
O'Reilly Media, 2004.
[12] ECLIPSE FOUNDATION. Eclipse Helios. Disponível em <http://www.eclipse.org>.
Último acesso em 30 de maio de 2012.
[13] KOSCIANKI, A; SOARES, M. S. Qualidade de software. 2ª Edição. São
Paulo:Novatec, 2007. 395 p.
[14] SERSON, R. R. A Bíblia: Certificação JAVA 6. Rio de Janeiro: Brasport, 2009.
51
[15] Dicionário Web. Disponível em <http://www.dicionarioweb.com.br>. Último acesso em
10 de junho de 2012.
[16] THAYER, R. H; CHRISTENSEN, M. J. Software engineering. 3rd Ed. Hoboken, NJ:
IEEE Computer Society, 2005.
[17] SHALLOWAY, A; TROTT, J. R. Explicando padrões de projeto: uma nova perspectiva
em projeto orientado a objeto. Porto Alegre: Bookman, 2004.
52
6. Apêndice
6.1. Apêndice A - Facade
Apresentação
O padrão de projeto Facade, fachada em francês, é um padrão do tipo estrutural e tem
por objetivo fornecer uma interface unificada que simplifique o entendimento e a utilização de
um subsistema mais complexo.
Descrição com Exemplo
Em algumas aplicações para um processo ser realizado é necessário que um conjunto
de ações em diferentes classes sejam executadas, a esse conjunto de classes chamamos de
subsistema. Dependendo da aplicação, esse subsistema pode se tornar bem complexo, isso
fará com que o uso e o entendimento dele se torne complexo. Com o propósito de simplificá-
lo é criado uma classe de fachada que irá conhecer as funções desse subsistema e irá fornecer
para a aplicação uma interface simplificada dos processos realizados por ele.
Ao ligar um carro, por exemplo, existem vários processos que ocorrem que não
conhecemos bem ao certo, mas que precisam ser executados para o carro ligar corretamente.
Esse comportamento é a base do padrão de projeto Façade, onde um objeto (chamado de
objeto fachada) fornece uma interface simples para um subsistema mais complexo. Nesse
caso o objeto carro é o nosso objeto fachada para o subsistema veículo. Exemplificando de
forma simplificada temos as classes Motor, Farol, Porta e Radio que fazem parte do
subsistema veículo. Ao ligar o carro o motor liga, o farol ascende, a porta tranca e o radio
liga. Ao desligar o carro o motor desliga, o farol apaga, a porta destranca e o radio desliga.
Nota-se que ao ligar e desligar o carro vários processos ocorrem, porém de forma transparente
para o cliente, ou seja, ele não precisa saber exatamente o que está por trás do processo ligar
ou desligar o carro, pois através desse padrão esses processos serão controlados pelo objeto
fachada.
53
Figura 28 – Diagrama de Classes (UML) Facade
Através da estrutura mostrada na Figura 28 é possível observar que a aplicação
enxerga o objeto fachada, e através dele e de suas operações acessa os processos do
subsistema. Esse padrão apesar de fornecer uma interface que simplifica o uso e o
entendimento de um subsistema, não impede que a aplicação utilize as classes desse
subsistema diretamente.
54
Codificação do Exemplo
Figura 29 – Classes Motor.java, Porta.java, Farol.java e Radio.java
As classes apresentadas na Figura 29 são classes do subsistema em questão. Note que
sem uma classe fachada a aplicação precisaria, ao ligar e desligar o carro, chamar cada um
dos métodos de cada uma das classes do subsistema.
55
Figura 30 – Classe CarroFacade.java
A classe CarroFacade, Figura 30, nossa classe fachada, faria o papel de simplificador
dos processos relacionados ao subsistema, nesse caso ligar e desligar o carro. Desse modo,
como dito anteriormente, a aplicação não precisaria se preocupar com cada um dos processos
internos, e sim com os processos que realmente são relevantes para ela: ligar e desligar o
carro.
Proposição
O sistema de uma loja virtual recebe dados de um cliente através de um formulário
que contém informações de usuário e do cartão de crédito. Após receber os dados, as entradas
são validadas e gravadas em um lugar apropriado. Utilize o padrão Facade para diminuir o
acoplamento entre os componentes, reduzindo as dependências entre eles. Na Figura 31
mostra-se o diagrama de classes do exemplo em questão.
56
Figura 31 – Exercício Proposto Facade
Extensão
No sistema do exercício acima, é necessário que seja adicionado ao formulário o
endereço do cliente, modifique o diagrama de classes e a implementação, explicitando onde se
dariam as modificações.
6.2. Apêndice B - Singleton
Apresentação
Assegurar que uma classe tenha somente uma única instância. Após a instanciação
desse objeto, não será mais permitido a criação de objetos adicionais dessa mesma classe.
Descrição com Exemplo
Existem aplicações que requerem que somente uma instância de uma classe seja
criada. Por exemplo, alguns sistemas se conectam a um banco de dados utilizando apenas uma
instância que gerencia a conexão, evitando que sejam desperdiçados recursos ou que se
incorra em erros. Outra exemplo é a utilização de arquivo de log, onde uma única instância
seja responsável pelo acesso a esse arquivo, evitando inconsistências nas informações
registradas.
Na API padrão do Java, por exemplo, são raros os exemplos que utilizam o padrão
Singleton, mas podemos encontrá-los em locais onde a JSE (Java Standard Edition) precisa
utilizar os recursos do SO (Sistema Operacional) ou da JVM (Java Virtual machine). A classe
Runtime é um bom exemplo, requerendo a existência de um único objeto que represente o
ambiente em que a aplicação está executando.
57
Figura 32 – Diagrama de Classes (UML) Singleton
Codificação do Exemplo
O padrão de projeto Singleton, como dito acima, garante que um sistema instancie no
máximo um e somente um objeto de uma classe. No exemplo abaixo, demonstra-se como é
feita a implementação desse padrão em Java.
Na linha 3 temos a declaração da classe Singleton com o modificador final,
objetivando a não criação de subclasses o que poderia fornecer outras instâncias de objetos da
mesma hierarquia, ferindo o objetivo principal do padrão. Na linha 8 declaramos o construtor
da classe Singleton como private. Esse construtor caracteriza o padrão de projeto Singleton,
pois, a partir dele, mostramos que somente a classe Singleton pode instanciar um objeto
Singleton. Juntamente com o construtor privado é criada uma referência private, encapsulando
a referência ao objeto único, e static, que garante a existência de uma única referência ao
objeto Singleton, declarado como singleObject, na linha 5. Na linha 13 é declarado o método
getInstance() que retornará aos clientes a instância única da classe. Nesse método é
instanciado o objeto singleObject de Singleton, caso ele ainda não exista. Nele é também
utilizado o modificador synchronized para impedir a execução simultânea deste método em
eventuais requisições.
58
Figura 33 – Classe Singleton.java
A classe Main.java utiliza a classe Singleton.java para exemplificar o funcionamento
desse padrão de projeto. Nessa classe podemos notar que realmente temos um e somente um
objeto instanciado do tipo Singleton, e que qualquer modificação feito nele será refletido em
todas as classes que o utilizam.
59
Figura 34 – Classe Main.java
Proposição
Precisa-se desenvolver um simulador de um processador, que possui apenas um núcleo
de processamento, que será utilizado para testar algoritmos de escalonamento. Nesse projeto
será utilizado um único objeto para representar esse único núcleo de processamento, onde as
aplicações irão utilizá-lo para executar suas tarefas. Implemente a classe CPU de forma a
garantir a integridade da execução de dados, utilizando o padrão Singleton.
Extensão
A partir do exercício anterior, implemente duas classes que representem duas
aplicações distintas que irão utilizar o microprocessador em questão.
6.2. Apêndice C - Iterator
Apresentação
Prover uma forma de acessar objetos pertencentes a uma estrutura de dados sem saber
exatamente como ela é representada, ou seja, sem conhecer como essa estrutura acessa seus
dados ou mesmo como ela os organiza.
60
Descrição com Exemplo
Estruturas de dados, como por exemplo arrays, listas, pilhas e filas, são largamente
utilizados no desenvolvimento de software para organizar os dados de um programa. Agora
imagine que o desenvolvedor tenha que percorrer os objetos pertencentes a dois ou mais tipos
de estruturas de dados. Para fazer isso ele terá que saber com qual estrutura de dados ele está
mexendo, entretanto se isso não for possível ele não poderá fazê-lo.
O padrão de projeto Iterator é uma solução para o problema descrito acima. Esse
padrão propõe a criação de uma classe que irá percorrer os objetos da estrutura de dados de
forma transparente, ou seja, sem precisar conhecer exatamente como os dados estão
organizados e qual o comportamento da estrutura de dados.
Na diagrama de classes mostrado na Figura 35 temos a classe que representa a
estrutura de dados Estrutura, que é uma classe abstrata, nela existe o método que será
implementado pelas suas subclasses concretas criarIterador(), nesse método será criado um
objeto que será usado para percorrer a estrutura. No exemplo abaixo, temos as classes Fila e
Lista como as subclasses concretas da classe Estrutura. Note que na implementação do
método criarIterador() cada uma das classes Fila e Lista irá retornar um objeto diferente.
Para representar a classe que irá percorrer os objetos da estrutura de dados, foi criada
uma classe abstrata Iterador que possui métodos que irão ser implementados pelas suas
subclasses concretas IteradorFila e IteradorLista. Esses métodos serão implementados de
acordo com a estrutura de dados irão percorrer, porém para o usuário isso é transparente, ele
só irá enxergar os métodos first(), next() e hasNext().
61
Figura 35 – Diagrama de Classes (UML) Iterator
Codificação do Exemplo
Na Figura 36 temos a implementação em Java do exemplo discutido. Temos as classes
abstratas Estrutura, que representa as diferentes estruturas de dados, e Iterador, que
representa a classe responsável por percorrer os objetos da estrutura de dados.
Figura 36 – Classes Estrutura.java e Iterador.java
As classes concretas Fila e Lista são subclasses de Estrutura, e implementam o
método criarIterador(). Esse método irá retornar o objeto que irá percorrer os objetos que
estão que estão na estrutura. Na classe Fila o método irá retornar um objeto do tipo
IteradorFila e na classe Lista esse método irá retornar um objeto do tipo IteradorLista.
62
Figura 37 – Classes Fila.java e Lista.java
As classes concretas IteradorLista e IteradorFila, são subclasses de Iterador. Elas
implementam os métodos definidos pela sua superclasse de acordo com a estrutura de dados
que elas irão percorrer.
63
Figura 38 – Classes IteradorLista.java e IteradorFila.java
Na classe Aplicacao, Figura 39, temos um exemplo utilizando as classes mostrada na
Figura 38. Nas linhas 7 e 8 instanciam-se dois objetos, uma lista e uma fila, e nas linhas 11 e
12 essas estruturas são populadas. Na linha 14 temos o objeto minhaEstrutura do tipo
Estrutura e a ele atribuímos o retorno do método getEstrutura(), nesse método é retornado de
forma aleatória a instância do objeto lista ou do objeto fila. Na linha seguinte temos o objeto
it do tipo Iterador que irá receber o iterador do objeto minhaEstrutura. Finalmente, nas linhas
17 e 18 é percorrido os objetos pertencentes à estrutura minhaEstrutura através do iterador it.
É importante observar que, apesar de o tipo de estrutura do objeto minhaEstrutura não ser
conhecido, a forma de acesso aos objetos nela inseridos é feita da mesma forma, usando o
iterador, independente de como se dá o acesso a essa estrutura ou de como ela está
organizada.
64
Figura 39 – Classe Aplicacao.java
Proposição
Faça um programa em Java utilizando o padrão acima discutido utilizando pilha e
array como estruturas de dados a serem percorridas pelo iterador de forma transparente.
Extensão
Agora, adicione ao programa desenvolvido uma nova estrutura de dados, hash map.