Você está na página 1de 32

Grupo EvoFavo, Projeto Monkey Buzziness

Equipe de projeto e arquitetura de Software


Composição de software orientado a objetos
Revisão 1.0

Goiânia, fevereiro de 2007.


Sumário
Parte I Determinando o espaço de trabalho

Palestra I
1. Conceitos de Orientação a objetos
2. Herança

Palestra II
1. Sistemas de tipos
2. Metodologias de desenvolvimento de software

Parte II Entendendo o problema

Palestra III
1. Determinando os requisitos
2. Analisando o problema

Parte III Fazendo o design da solução

Palestra IV
1.Design da arquitetura
2.Escolhendo tecnologias

Palestra V
1.Desenhando o Subsistema
2.Design patterns overview

Palestra VI
1.Especificando as interfaces de classes overview
2.Teste contínuo

Parte IV Estudo de Caso I

Palestra VII
Palestra VIII

Parte V Desenvolvimento de softwares orientados a interfaces

Palestra IX
1.Introdução as interfaces
2.Contratos de interfaces
3.Ingredientes das interfaces
4.O que deve estar presente em uma interface
5.Herança e interfaces
6.Interfaces remotas

Palestra X
1.Interfaces e as metodologias de desenvolvimento
2.Interfaces no mundo real, estudo de casos
3.Interfaces e patterns

Parte VI Design Patterns – Tópicos Avançados

Palestra XI
1. Sobre patterns
2. The observer pattern
3. The decorator pattern
4. The factory pattern
5. The singleton pattern
6. The command pattern

Palestra XII
7. The adapter and facade patterns
8. The template method pattern
9. The iterator and composite patterns

Palestra XIII
10. The state pattern
11. The proxy pattern

Palestra XIV
12. Compound pattern
13. Vivendo melhor com patterns
14. Outros patterns

Parte VII Usando XML para Desenvolvedores de Software

Palestra XV
Palestra XVI

Tempo esperado término: Julho/07


Introdução
O objetivo desse curso é ensinar análise e design de software orientado a
objetos. Portanto nosso intuito é modelar projetos de aplicações independente de
linguagem, portanto reprodutível em qualquer linguagem orientada a objetos como
Java, C++, C#.
O treinamento será de preferência usando recursos interativos, ou seja vídeo
aulas, exercícios práticos, e estudo de casos. Com isso desejamos que o material seja
assimilado aos poucos integrando-se ao pensamento do desenvolvedor.
Materiais teóricos serão acrescentados a esse documento a medida que o curso
evolui. Portanto tratem esse manual como um handbook para consultar toda hora que
tiver que analisar ou projetar e até mesmo programar um sistema.
As palestras presenciais serão na medida do possível reduzidas para garantir
além da reprodutibilidade do conteúdo que todos os tópicos sejam vistos.
Espero montar um material de extrema qualidade e que seja uma referencia
constante para todos envolvidos no projeto ou que desejem aplicar essas informações
em outros projetos. Portanto sempre terei em mente a simplicidade na exposição dos
conteúdos.
O meu alvo é programadores com alguma experiência em orientação a objetos e
que conheçam as estruturas básicas de uma linguagem OO como Java ou C#.
Diz um provérbio que você não sabe um assunto até que possa explicá-lo a uma
criança de 8 anos de idade. Minha intenção é mostrar que esse provérbio esta correto e
conto com o seu feedback para isso.
A melhor forma de ler o material é seguindo a ordem dos capítulos mas nada
impede do leitor mais interessado de pular alguns conceitos. Iniciamos nosso estudo
com as questões básicas da orientação a objetos e a construção de sistemas robustos e
reaproveitáveis. Seguimos abordando a análise do sistema. Em seguida estudamos o
design em alto nível ou seja o design da arquitetura do software e a escolha das
tecnologias a serem usadas, depois passamos ao design dos subsistema ao qual o leitor
deve estar mais familiarizado. Por fim avançamos sobre tópicos avançados como design
patterns e o uso de XML na comunicação entre os componentes do software. A notação
UML será incrementada durante o curso.
Muitos conceitos de design abordados aqui o leitor pode considerar
familiarizado porém convido você a refletir esses conceitos e garantir se realmente está
produzindo softwares orientados a objetos.

Nota aos integrantes do grupo EvoFavo: Esse material deve abordar as questões
técnicas do projeto de software sendo que as questões de negócio devem ser abordadas
pelo gerente do projeto. Minha intenção não é precipitar o design e fazê-lo apenas
depois que a maioria estiver bastante a vontade com as principais idéias aqui
apresentadas. Não durmam ou converse nas palestras, nem finja que está entendendo,
caso algum tópico lhe pareça obscuro pergunte imediatamente, não percamos nosso
tempo. Lembrem-se que a primeira iteração não temos tempo marcado. Eu proponho
que após terminarmos a primeira iteração e termos uma noção mais sólida de projeto de
software que façamos várias atividades para verificar se todos dominam o básico de
cada área, garantindo assim que o pessoal está qualificado, antes de prosseguir.
Podemos a partir do resultado identificar áreas falhas e treinar o pessoal.
Alberto Paulo Rabelo Barcelos

Parte I Determinando o espaço de trabalho

Palestra I

Conceitos de Orientação a objetos

Introdução

Nos dias atuais praticamente todos softwares são orientados a objetos. Mas nem
sempre se consegue com facilidade escrever na prática códigos orientados a objetos.
Tal atividade requer treinamento e reflexão.
Há muito mais atividades no desenvolvimento de software que escrever linhas de
código. Temos de fazer análise de negócios, requisitos do software, design da
arquitetura e o design de baixo nível, entre outras.
Portanto nada mais natural que basear o desenvolvimento em uma nova abstração
chamada objeto. Os objetos reduzem a quantidade de informação que deve ser
assimilada assim como garantem uma melhor comunicação entre os
desenvolvedores.

Tipos de Programação

 Linguagem de máquina
 Assembly e o uso do assembler
 Linguagens de Alto Nível(Cobol, Fortran)
 Programação estruturada
 Programação orientada a objetos
 Programação lógica
 Programação funcional

Todos esses tipos de programação continuam existindo nos dias de hoje. Não
devemos encarar um estilo melhor que outro ou uma língua dentro de um tipo
melhor que outra, devemos sim analisar e escolher o tipo de programação e língua
de acordo com o problema que estamos resolvendo.

Exercício 1.1: Pesquise sobre os paradigmas logico e funcional de programação.


Descubra suas aplicações e diga como é sua aceitação no mundo industrial.

Exercício 1.2: Pesquise sobre o Assembly. Diga qual suas características e sua
aplicação. Verifique os deferentes tipos de Assembly e diga quais são mais usados.

Metodologia

Uma metodologia é uma descrição das etapas que devemos seguir para alcançar
um produto de alta qualidade.
Existem metodologias para os vários tipos de programação. As metodologias
predominantes nos padrões inustriais atuais são RUP e uma que vem ganhando
espaço, o XP.
A maioria dessa metodologias possuem muitos detalhes e práticas recomendadas
que podem confundir um iniciante, por isso adotaremos nesse material aqui presente
uma metodologia simples, que permita a criatividade em cada etapa do
desenvolvimento, metodologia ripple.

Exercício 1.3: O que é RUP? Quais são suas características e quando deve ser
usado.
Exercício 1.4: O que é XP? Quais as vantagens dessa metodologia e porque ela vem
ganhando espaço?

Introdução aos Objetos

Omitiremos o básico apostando no background do leitor.


Primeiro vamos dar uma olhadinha em modelos. Modelo é a representação de
um domínio do problema ou uma definida solução que nos permite discutir e
adicionar idéias ao todo. Assim como arquitetos montam um modelo antes de
discutir uma construção para ter uma idéia de como o resultado será, devemos
construir modelo para vislumbrar nossa aplicação. Grande parte do desenvolvimento
de software envolve construir e refinar modelos.
Uma classe é um protótipo para se criar um objeto, uma fábrica. Para criarmos
um objeto devemos escrever a seguinte linha de código:
new Pessoa(“Giovana”)
Depois de criado o objeto temos que coloca-lo em algum lugar onde possamos
acha-lo mais tarde, ou seja devemos atribuir uma referencia a ele:
aPessoa = new Pessoa(“Giovana”)
Em um diagrama UML nome de classes não são sublinhadios enquanto de objetos
sim. Nós podemos fazer a maior parte do trabalho de projeto de software usando
diagrama de classes, diagramas de objetos podem ser usados para propósitos de
ilustração e verificação. Nomes de classes normalmente possuem letra inicial
maiúscula.
Softwares orientados a objetos são mais naturais devido a tendência humana de
dividir tudo ao seu redor em classes.
Estrutura básica das classes

Algumas linguagens puramente orientadas a objetos possuem asserções,


condições lógicas que sempre devem ser verdadeiras. Lembre-se da asserção usada
no teste de unidade. As asserções podem ajudar a compar a estrutura básica de uma
classe.

Diferença entre identidade e equivalência


Duas referencias podem apontar para um mesmo objeto, nesse caso dizemos que
elas possuem a mesma identidade. Mas se dois objetos com diferentes identidades
possuírem os mesmos atributos dizemos que eles são equivalentes.
Teste de identidade ( aPessoaum == aPessoadois)
Teste de equivalência aPessoaum.equals(aPessoadois)
Os testes retornam booleanos e podem ser usados em várias estruturas de
programação.

Fig. Representação UML de um objeto. Se tivéssemos uma classe o nome não


poderia estar sublinhado e normalmente começaria com maiúscula.

Ao escrever o design de um objeto primeiro devemos determinar as


operações que um objeto deve executar e quais atributos o objeto deve ter para
garantir que essas operações sejam exeutadas. Operações são ditas métodos dos
objetos.

Encapsulando objetos

Esconder seus atributos usando suas operações é encapsular um objeto.


Se alterássemos diretamente esses atributos teríamos um código dependente desses
atributos. Se futuramente desejássemos mudar um atributo teríamos que alterar
todos códigos que fazem referencia a ele, com o encapsulamento podemos
entretanto alterar apenas uma operação. Assim garantimos a reusabilidade do código
bem como evitamos bugs.

Associação e agregação

Nenhum objeto é isolado. Os objetos se comunicam através de outros objetos


e além disso precisam trocar informações tornando assim o sistema mais poderoso.
As duas principais formas de conectar objetos é através da associação e
agreação.
Associação é uma forma simples e “fraca” de se conectar objetos. Através
da associação combinamos objetos de forma que eles não se tornem dependentes um
dos outros. Por exemplo considere um carro e dois passageiros. Eles estão
associados, vão ao mesmo destino e ocupam o mesmo volume no espaço. Mas a
qualquer momento um dos passageiros podem abandonar o carro e assim a
associação.
Agregação é uma conexão mais forte entre os objetos. Significa juntarmos
objetos pequenos para fabricarmos um grande objeto. Geralmente um agregação
combina com uma relação de objetos do mundo real parte-todo. Significa
dependência pois a parte perde sua função se considerada fora do contexto do todo.
Colocamos um losango branco no lado do todo para representar a agregação.
Uma boa pergunta para determinarmos se devemos ter uma associação ou uma
agregação portanto é nos perguntar se um objeto existe ou melhor se tem utilidade
fora da “possível” agregação.

Amigos são uma associação


Componentes de uma televisão são uma agregação
Livros em uma biblioteca são uma associação

Exercício 1.6: Determine os seguinte casos como assosiação ou agregação:


Casas em uma rua
Páginas em um livro
Componentes num sistema de multimídia de uma casa

Lembrem-se da diferença entre grafos e árvores. Uma agregação se parece mais com
uma árvore e uma associação com um grafo. Uma árvore é um caso especial de
grafo onde cada nó pode ter apenas um nó parente e vários nós filhos. Observe que a
agregação deve respeitar além dessa lei a dependência entre as classes.

Navegabilidade

As conexões entre classes vistas até aqui são chamadas de links.


Há também a navegabilidade. O rótulo da navegabilidade normalmente está
ligado a um atributo que faz uma classes estar conectada a outra. Por exemplo o
Objeto Consumidor tem outro Objeto Adress como atributo, podemos representar
essa característica usando a navegabilidade com o rótulo adress. Observação a
navegabilidade é representada pela seta. Portanto a navegabilidade só ocorre da
parte para o todo, portanto a parte desconhece a existência do todo. Ou seja não há
nada em sua implementação que a diz que faz parte do todo. Muitos links de
navegabilidade terminam como ponteiros em linguagens orientadas a objetos.
Ponteiros são os endereços de memória de um objeto.

Obs.: Com a intenção de ser independente de linguagem todos os exemplos aqui


apresentados são ilustrativos, ou seja, você poderá não encontrar classes em Java
que correspondam exatamente as usadas aqui. Portanto se você for um programador
Java ou .NET tenha em mão a sua biblioteca de classes.

Mensagens

Sabemos que objetos isolados não possuem muita utilidade. Os objetos


comunicam entre si através de mensagens. Exemplo de colaboração entre objetos
usando UML.
Links não tem direção
Nomes objetos não são sublinhado
A resposta não segue o padrão UML.
Abaixo estão alguns exemplos de mensagens.

As mensagens nem sempre recebem uma resposta ou um resultado plausível.


Fazer com que as mensagens sempre recebam resultados plausíveis pode ser
fundamental em casos como o computador de bordo de um avião. Existe uma área
da engenharia de software chamada “software reliability” ou confiabilidade de
software que lida exatamente com esses problemas. Por hora assumiremos que todas
as mensagens retornam e seus resultados são válidos.
Quando um objeto recebe uma mensagem ele executa códigos, portanto uma
mensagem invoca uma operação. As mensagens podem ter parametros também
chamados de argumentos, um parametro é um requisito que o objeto que executa a
operação precisa para realizá-la com sucesso.

Pense : Ao passar um argumento a um método pense se é possível aquele


método realizar aquela operação sem aquele argumento, se é possível usarmos
outra lista de argumentos e qual abordagem é mais relevante para a
reusabilidade do código, ou seja, qual abordagem é “mais” OO.

Lembre-se que em java determinamos qual objeto receberá a mensagem usando


um ponto, como em: aObject.message(aParameter)
Podemos as vezes pensar se uma mensagem que enviamos a um objeto deve
requisitar alguma informação ou mandá-lo executar um comando. Uma regra que
podemos lembrar é: “Uma mensagem deve ser uma pergunta ou um comando,
mas não ambos”.
Se for uma pergunta a mensagem não deve alterar nenhum atributo do objeto
nem de outros objetos ligados a ele. Como exemplo o tempo não pode mudar
apenas porque o pedimos.
Caso a mensagem mande um objeto executar uma operação então não devemos
obter nenhuma resposta. Como exemplo podemos mandar um objeto sacar 100 reais
de uma conta, não devemos esperar que esse objeto retorne uma resposta como o
restante do dinheiro que sobrou na conta. Se quisermos obter tal resposta então
devemos mandar outra mensagem perguntando qual o saldo atual da conta. Um
comando altera um objeto que recebe como argumento ou o próprio objeto em que
está implementado.

Pense : Seus métodos estão seguindo as regras básicas de OO?

Há casos em que vale a pena termos uma mensagem dos dois tipos
É valido um objeto peguntar a si mesmo. Assim como fazemos perguntas a nós
mesmos como em “O que eu fiz ontem?”

Exemplo de Colaboração
Temos uma interação complicada entre dois tipos de objetos, programar já é
suficiente dificíl sem esse tipo de complexidade do mundo real. Temos um consumidor
que dependente da interface da balconista e um vendedor que depende da interface do
consumidor. Mudar um objeto significa mudar o outro também, um pesadelo para a
manutenção do código.
Constratemos a comunicação entre a balconista e o consumidor com a
comunicação entre um contador e a balconista. A balconista pode mandar uma
mensagem para o contador e esperar uma resposta mas o contador não pode perguntar
nada a balconista. Assim a interação é de mão unica e os mesmos objetivos continuam
sendo atingidos. Chamamos essa interação de cliente-fornecedor. Porém nesse tipo de
interação temos mais chance de reusar o código e portanto chegamos a um resultado
orientado a objetos.

Como funciona um programa orientado a objetos


Um programa orientado a objetos instancia objetos, conecta eles e os faz
colaborar mandando mensagens entre si. Mas quem faz as coisas acontecerem? Todas as
linguagens orientadas a objetos possuem um ponto de entrada que no caso do Java é a
função main.

Garbage Collection = Coleção de Lixo


Todo objeto ocupa um espaço na memória. A medida que o programa
continua sendo executada esses objetos passam a ocupar mais memória, imagine um
programa rodando em um servidor por anos. Portanto devemos limpar da memória
objetos que não estão mais sendo usados(garbage collection). Gerenciar o ciclo de vida
de um objeto e decidir quando ele deve ser apagado da memória é uma tarefa difícil e
não é incomum um programador esquecer uma etapa, tal falha é chamada de memory
leak.
Em Java a própria plataforma faz isso para o programador. Todo programa em
java possui um assistente chamado coletor de lixo(garbage collector).
Em linguagens que não possuem essa característica é comum termos que
escrever um sistema que é responsável pela “organização da casa”, tal sistema é
chamado de run time system e devemos escreve-los nós próprios, tal sistema inclui um
garbage collector.
Superficialmente um sistema coletor funciona da seguinte forma: ele deleta
qualquer objeto que não pode ser acessado, direta ou indiretamente, de qualquer nome
do programa. Se um objeto não pode ser acessado ele não pode mandar nenhuma
mensagem consequentemente também não pode responder a nenhuma pergunta,
portanto ele deve ser lixo.
Herança/Generalização/Especialização

Omitirei o básico sobre esse tipo de relação de classes.

Encapsulando objetos
Devemos declarar um campo como privado toda vez que ele for útil apenas para a
classe em questão, da mesma forma para os métodos. Não use um método público
exceto se ele for responsável pelas mensagens do objeto ou se você tiver um bom
motivo para tal. Garantimos assim objetos encapsulados que operamos usando apenas
sua interface(métodos) e além disso garantimos a reusabilidade e a orientação a objetos.
Já que nenhum outro objeto pode alterar as varáveis do nosso objeto sem usar sua
interface e usando a interface(métodos) nosso código pode ser mais facilmente
estendido e facilitamos sua manutenção.

Elementos de Classe
Quando queremos que uma informação não seja vinculada especificadamente a um
objeto mas sim a classe podemos declarar uma variável ou método como estáticos. Em
java isso equivale a inserir a keyword static na declaração desse método ou variável.
Esses elementos são chamados de elementos da classe. Não é tão fácil quanto parece o
uso de elementos de classe pois algumas linguagens não trata uma classe como um
objeto puro, isso quer dizer que as classes não tem as mesmas características como
herança, mesmo linguagens que possuem essa característica ficam a merce de
complicações como metaclasses( C++). Portanto sempre antes de decidir que sobre a
utilidade de um elemento de classe pense nas seguintes alterntivas:
1. Procure ou introduza um novo tipo de objeto, fique atento para não criar
objetos sem sentidos só para evitar o uso elementos de classe. Por exemplo ao
invés de inserir um novo elemento de classe taxaDeInvestimento podemos criar
ou mesmo inserir esse campo numa classe chamada Banco e ainda ganhamos
com a extensão do sistema já que o sistema pode agora conter vários tipos
diferentes de bancos.
2. Use um singleton pattern. Singleton é um pattern que garante que podemos ter
apenas uma instância de uma classe, o singleton object.
Exemplo: A pergunta “Esse é um ano bissexto?” combina perfeitamente com
esse padrão já que precisamos apenas uma instância do calendário gregoriano.
Uma instância onde todos os objetos podem perguntar datas.

Tipos primitivos e objetos


Uma linguagem orientada a objetos pura deve considerar tudo como um
objeto. Algumas linguagens também possuem tipos primitivos, existem várias
justificativas para o uso desse tipos brevidade, performance e resquícios de
linguagem estruturada. A maior distinção entre objetos e tipos primitivos é que
podemos mandar mensagens para esses objetos primitivos ou qualquer outra
característica de um objeto. Primitivos são uteis para valores simples como um
inteiro ou caracteres individuais, qualquer outra coisa deve ser um objeto.
Em java arrays são algo entre um objeto e um primitivo por questões de
performance mas o programador pode ser puro evitando esse tipo de primitivo e
usando a classe List.
A notação UML permite usar tipos genéricos para podermos mapear o design
para qualquer linguagem orientada a objetos. Por exemplo: Integer, Real, Boolean.
Entretanto a convenção UML também permite usar os tipos especificos de uma
linguagem desde que o pojetista sabe o que está fazendo.
Como regra sempre que possível tente usar um objeto no lugar de um tipo
primitivo. Em java corresponde a usarmos “Integer a” ao invés de usarmos “int
a”.

Terminologia
Usaremos apenas os termos sublinhados

Nas etapas inciais do desenvolvimento tendemos a usar os termos operações


e atributos e nas etapas finais tendemos a usar a terminologia métodos e campos
porque esta metodologia está mais próxima da programação. Não se deixe confundir
pela terminologia, caso entre em dúvida procure um dicionário online rapidamente.

Reusando Código

Objetivos: desenvolvimento rápido e simples, manutenção simples, código


mais robusto(toda vez que ele é reusado achamos novos erros, eles são
corrigidos, a performance é melhorada). Focar nas questões do “negócio”
e não em pequenas mágicas da programação.

Porque sistemas orientandos a objetos usam conceitos de um domínio é


muito mais provável que oportunidades de reuso apareceram. Por exemplo ao invés
de pensarmos o que um sistema de pagamento deve esperar que uma classe
empregado contenha devemos pensar o que a classe empregado significa para uma
companhia.
Assim podemos usar a classe empregado em outros sistemas. Além disso a
modularidade dos objetos diminui a tendência de espalhar atributos e operaçções da
classe empregado pelo sistema, tornando mais simples incrementarmos nossa
implementação com novas características e também correção de bugs. Hoje temos
que apenas implementar as partes do código que necessitaremos apenas para nossa
aplicação os demais códigos podem ser reaproveitados.

Tipos de reusabilidade:

1. Reutilização de funções dentro de um sistema: escrevemos funções


genéricas que podem ser chamadas de qualquer parte do sistema, como por
exemplo uma função para ordenar uma lista
2. Reutilização de métodos de um objeto: métodos encapsulados dentro de um
objeto podem ser chamados por outros métodos dentro do mesmo objeto.
Utilize essa técnica SEMPRE que possível. Normalmente métodos não
públicos servem para dividir da melhor forma tarefas complexas que devem
ser realizadas por um objeto.
3. Reusando classes dentro de um sistema: é o ponto principal de orientação a
objetos, quando especializamos uma classe, quando agregamos duas classes
estamos reaproveitando código
4. Reusando funções entre sistemas
5. Reusando classes entre sistemas
6. Bibliotecas de funções: exemplo stdio.h em C.
7. Biblioteca de classes: exemplo J2EE Class Library, biblioteca .NET
framework. Essas bibliotecas podem possuir direitos autorias.
Portanto escrever um código reusável pode render dinheiro as empresas.
Podemos chegar a casos de usar componentes completos dentro de uma
aplicação.
8. Design Patterns
9. Frameworks: é uma estrutura pré existente onde você acopla seu código. Um
framework é constituido de classes junto com documentações que descrevem
as regras de construção que devem ser seguidas pelo desenvolvedor.
Exemplo: Enterprise Java Beans, Hibernate, JUnit.
Portanto como projetamos sistemas reusáveis?
Primeiro aprenderemos a projetar classes reusáveis assim todas as classes
conectadas a elas também serão, depois mergulharemos em tópicos mais
avançados como design patterns e frameworks.

Dicas para maximizar a reusabilidade:


 Siga estilo de codificação padrão.
 Documente extensivamente
■ Nome auto explicativo
■ Comentário curto resumindo a utilidade da classe
■ Comentário longo de vários paragrafos explicando: Como, Onde,
Porque, Quando deve ser usada e como foi implementada.
■ Descrever como mensagens publicas devem ser usadas. Lembre-se que
quem for reutilizar sua classe não poderá invocar as mensagens privadas
de seu objeto.
■ Descrever o contrato entre o objeto e seus clientes, por exemplo
obrigações de ambos os lados
■ Documentações separadas da classe como projeto de classes, tutorias e
definição de requisitos
 Prepare-se para escrever mais código, ou seja evitar atalhos lógicos que só
você entenderá e encapsular sua classe.

 Use padrões e frameworks. Usá-los além de diminuir seu trabalho facilita a


compreensão de quem vai ler seu código.
 Projete objetos cliente-fornecedor: se você tiver comunicação de mão dupla
ou pior cíclica em seu código acaba tendo como resultado um código
chamado “código espaguete”. Pense nos objetos como serventes que
executam o que forem pedidos sem se preocupar com quem pediu.
 Faça cada objeto com um propósito único, também conhecido como alta
coesão.
 Separe a interface gráfica da lógica da aplicação. Detalhes sobre esse tópico
serão abordados futuramente.
 Projete Objetos para responder questões e realizar operações.
Herança
Vantagens:
 Permite uma modelagem muito mais poderosa e rica. Beneficia tanto quem está
desenvolvendo código como o torna reusável.
 Permite definir informação e comportamento em uma classe e compartilhar
 É natural

Exemplo:
Observe a notação em UML para declarar um o tipos da variável e tipo dos
parametros e os valores de retorno dos métodos, usa-se os dois pontos “ : “.
Outro exemplo, suponha que queiramos modelar uma coleção. Coleções são
objetos que contem outros objetos.
Tipos:
Listas: Coleção de objetos que os mantém na ordem que foram inseridos
Bag: Não mantém os objetos na ordem.
LinkedList
ArrayList
Objetivo: Colocar todas essas classes como uma herança de Collection.
Quando estivermos montando uma hierarquia quanto mais perto da base
colocarmos uma mensagem melhor. Normalmente nós tendemos a primeiro buscar as
mensagens que um objeto deve ter depois seus atributos isso é natural já que as
mensagens são sua interface com o mundo externo

1.Decidir onde cada classe se encaixa na hierarquia


Todas classes são coleções então natural colocarmos Collection na base
Notamos que todas as classes mantém suas coleções ordenadas exceto Bag
portanto Bag deve estar abaixo de Collection e separada das demais classes. Fica claro
também que ArrayList e LinkedList são tipos de List.

2. Depois procuramos mensagens que podemos compartilhar entre as classes


Mensagens candidatas:
contains(:Object):Boolean
numberOfElements():int
elementAt(:Int):Object
Quanto mais perto da base colocarmos o método melhor.

A mensagem elementAt(:Int):Object só faz sentido para listas ordenadas


Mas para cada lista ordenada teremos uma implementação diferente desse
método.

Pense que iremos implementar o método contains. Chegamos a conclusão de que


o método contains não pode ser escrito na classe Collection já que sua implementação
será diferente em listas ordenadas e não ordenadas.
Implementação para List
1 boolean contains(Object o) {
2 for (int i = 0; i < numberOfElements(); ++i) {
3 if (elementAt(i) == o) {
4 return true;
5 }
6 }
7 return false;
8}

Chegamos ao real benefício da herança, agora podemos chamar a mensagen


contains em qualquer direção da herança sem nos preocupar com sua implementação.
Com relação a implementação de numberOfElements():int, pensemos:
Abordagem 1) Guardar o número de posições como um campo de instância
Abordagem 2) Usar uma operação para calcular o número de Objetos

Esse tipo de problema sempre surge e temos que decidir entre espaço e tempo.
Nunca podemos escolher precipitadamente uma abordagem, pois nenhuma abordagem
sempre será útil em todos os casos. No nosso exemplo preferimos usar a abordagem
número 2.
As mensagens que não possuem o método implementado estão em itálico, são os
chamados métodos abstratos. Já aquelas que efetivamente possuem a implementação
são chamados métodos concretos. Em UML voce pode representar o método abstrato
usando o itálico ou a palavra {abstract} em frente ao método.

Classes abstratas
Classes abstratas são aquelas que possuem pelo menos um método abstrato, seja
ele introduzido pela própria classe ou herdado.
As mesmas notações UML valem para as mensagens e nome das classes.
Vantagens:
 Permite modelagem muito mais rica e flexível, por exemplo nossa classe
List possuem as três mensagens independente se podemos ou não
implementar o método.
 Permite maior compartilhamento de código
Exemplo: Considere uma fruta e sua casca. Caso queiramos descascar a fruta
sabemos que podemos fazê-lo com qualquer fruta mas não podemos descrever como
fazer isso pois para cada fruta teremos um processo diferente. Essa operação deve ser
abstrata e o conceito de fruta também o é.
Se tivéssemos que dizer a uma fruta para ela se descascar ? Como saberia como
executar a operação se não temos sua implementação para a fruta?
A maioria das linguagens orientadas a objetos evita isso impedindo o
programador de instanciar uma classe abstrata.
Isso é natural, se te desse dinheiro para ir ao mercado comprar uma fruta, você
me perguntaria qual fruta você deseja que eu compre? Porque você precisa de um
pedido concreto.
Quando você for montar uma hierarquia de classes deve ter em mente que a
maioria das super classes são abstratas. Isso decorre do fato que a herança é uma
generalização de baixo para cima:

1.Nós olhamos conceitos concretos que existem no domínio do nosso problema


e pensamos sobre seu comportamento e conhecimento.
2. Buscamos semelhanças entre classes concretas para chegarmos a superclasses
3.Agrupamos superclasses em mais superclasses até atingirmos nossa classe
mais generalista.

Quando chegamos a superclasses devemos pensar que elas são abstratos exceto
se elas representarem conceitos concretos na etapa 1.
É tão natural que uma superclasse seja abstrata que Java e UML nos permite
marcar uma classe como abstrata mesmo se não tivermos nenhum método abstrato
ainda.
Portanto se você observar uma classe concreta herdando outra classe concreta
pense nas seguintes transformações.
1.

Imagine que X fosse fruta e Y uma laranja. Esperaríamos que uma laranja
trabalhasse exatamente como uma fruta, mas com informações extras e comportamento
extra.
Motivos para se redefinir métodos
1.Implementar um método abstrato
2.Estender as funcionalidades do método herdado

3.Implementar novamente um algorítimo de forma mais eficiente para


determinada subclasse.

Implementando um Stack

Imagine uma pilha de pratos. Uma stack é uma estrutura de dados parecida,
podemos: adcionar um elemnto a pilha(push( ) ); retornar um objeto no topo da pilha
(peek ( ) ); retornar verdadeiro se a pilha estiver vazia ( isEmpty( ) ); remover um objeto
do topo da pilha ( pop( ) );
Como escrever software orientados a objetos envolve reusabilidade do código,
pensemos em como reaproveitar uma classe que já existe, nesse caso a classe
LinkedList, para aproveitarmos o código devemos ter uma herança ou uma composição.
Uma composição é uma agregação de objetos extremamente forte.

Agregação e composição
A associação tem duas formas particulares: a agregação e a composição. As
duas são muito parecidas, elas relacionam um objeto composto com suas partes. Por
exemplo, a associação entre uma universidade e seus departamentos é uma agregação, a
associação entre um carro e suas partes é uma composição. A diferença entre os dois é
que a composição é mais ``física'', com a composição, uma parte não pode pertencer a
dois objetos compostos ao mesmo tempo (um motor não pode pertencer a dois carros ao
mesmo tempo), e uma parte não pode existir sem o objeto composto (mas o composto
pode ``sobre-viver'' às suas partes).
Agregação e composicão aceitam multiplicidade, só do lado das partes para a
composição (um carro é composto de quatro rodas) e dos dois lados com a agregação
(uma palavra pode pertencer a várias frases e uma frase possui várias palavras).
Geralmente, a composição implica uma forma de propagação de algumas
propriedades. Por exemplo, quando o objeto composto morrer, as partes morrem
também (definição da composição), ou quando um carro se mover, as partes se movem
também. Essas propagação não é a herança, ela não é automática, você tem que
especificá-la e implementá-la. Todas as propriedades não são propagadas, por exemplo,
um carro pode ser vermelho e o motor não.
Herança versus Composição
Vantagens únicas da herança
Natural/Elegante/Permite escrever código genérico
Desvantagens da herança
Difícil fazer bem/Difícil mudar quando descobrimos deficiências em
nosso design/É mais difícil para programas clientes entenderem/ Mais difícil mudá-lo
também
Composição atinge os mesmo objetivos que a herança com as vantagens:
Simples de produzir/Fácil de mudar/ Fácil de entender para os clientes/ Não
deixa a desejar no código pelo lado do cliente.

Herança é uma relação do tipo “é um”


Agregação é uma relação do tipo “tem um”

A frase “Carro possui pneus” apresenta agrega¸c˜ao entre carro e


pneu. Por´em, “Ferrari ´e um tipo de Carro” apresenta heran¸ca.
Princípio da substituição de Liskov
Padrões de Interfaces

 Introdução as Interfaces
 Adapter
Adapta uma interface de uma classe para atender as expectativas de um cliente
 Facade
Fornece uma simples interface em um coleção de classes.
 Composite
Define uma interface que se aplica a objetos individuais e grupos de objetos.
 Bridge
Desacopla uma abstração de sua implementação assim elas podem variar
independentemente.
Introdução as interfaces

O que é?
Conjunto de campos e métodos que uma classe permite que a outra acesse. Em
Java esse conceito é elevado no sentido de se tornar uma construção separada entre
implementação e interface. Assim várias classes podem ser atreladas a mesma interface
ou uma classe pode implementar mais de uma interface.
Muitas vezes sua intenção no design vai além do simples conceito de interface.
Nesse contexto que surgem os padrões de interface. Eles estendem além o conceito de
interface para atingir objetivos do designer.

Atividade: Cite diferenças entre classe abstrata e interfaces em JAVA.

Exemplo:
interface Act {
void act();
}

class Actor1 implements Act {


public void act() {
System.out.println("To be, or not to be");
}
}

class Actor2 implements Act {


public void act() {
System.out.println("Wherefore art thou Romeo?");
}
}

public class TryOut {


public static void main(String args[]) {
Actor1 hamlet = new Actor1();
Actor2 juliet = new Actor2();
tryout(hamlet);
tryout(juliet);
}

private static void tryout(Act actor) {


actor.act();
}
}

Diferença entre o uso de classes abstratas e interfaces


Uma classe abstrata sem nenhum método não abstrato tem um funcionamento
parecido com o de uma interface. Mas há diferenças, observe:
1. Uma classe pode implementas quantas interfaces quiser mas pode ser subclasse
de apenas uma classe abstrata
2. Todos métodos da interface são abstratos, seja eles declarados explicitamente ou
não.
3. Uma interface não pode conte campos de instância apenas constantes.
4. Uma classe abstrata pode ser publica, protected, private, ou nenhuma. Já uma
interface pode ser apenas publica ou nenhuma(visível a package).
5. Todos métodos em uma interface devem ser públicos enquanto em uma classe
abstrata eles podem ser protected, private ou nenhum.
6. Uma classe abstrata herda de Object e portanto possiu vários métodos como
clone() ou equals()
Obs.: Interfaces sem métodos podem são chamadas de interfaces de marcação. Uma
aplicação útil é o caso de um método na hierarquia de classe não ser útil a todo tipo de
subclasse. Por exemplo o método Object.clone(). Assim voce pode criar uma interface
de marcação para obrigar as subclasses a optar pelo método caso queira usá-lo assim o
método clone() exige que toda classe que o execute possua a implementação de uma
interface de marcação, como no nosso caso Clonable.

Interfaces e obrigações
Todas as classes que implementarem a interface devem respeitar o nome do
método e os comentários sobre ele. Esses dois itens estabelecem o que chamamos de
contrato.
Algumas vezes os métodos da interface não exigem em seu contrato que sejam
implementados por todos objetos assim podemos deixar seu corpo vazio na classe que o
implemente. Ou seja, esse objeto não necessariamente reagira diante determinada
chamada de outro objeto. Essa técnica é usada quando desejamos registrar para obter
notificações, nesse caso o objeto deve tomar alguma atitude quando o método é
chamado mas geralmente não está oferecendo um serviço ao objeto que chamou o
método.
Exemplo: A classe que implemente MouseMotionListener interface de java.awt.event
deverá implementar os métodos mouseDragged(MouseEvent e) e
mouseMoved(MouseEvent e). A presença desses métodos não significa que o objeto
deve tomar uma atitude quando o método for chamado. De fato seria natural reagir a um
drag and drop e não a um movimento do mouse.
Se voce planeja criar uma interface que possua muitos métodos notificadores é
interessante que crie também um stub. Um stub é uma classe que implementa uma
interface com métodos que não fazem nada. A partir do stub podemos criar uma
hierarquia de classes, sobrescrevendo apenas os métodos da interface que sejam
importantes para a nossa aplicação.
Exemplo: WindowsApadter
Adapter
Como programador escrevemos nossas classes e nossas interfaces portanto nos
escrevemos os contratos aos quais nossas classes devem obdecer. Mas nem sempre voce
poderá fazer tal.
Quando voce precisa implementar uma interface voce verá que existirá uma
classe que fornece os serviços que o cliente precisa mas com nomes dos métodos
diferentes. Voce pode usar a classe existente para atingir as necessidades dos clientes
usando o Adapter pattern.
A intenção desse padrão é fornecer uma interface que um cliente espera usando
os serviços de uma classe que implementa outra interace.

1. Um cliente numa package precisa chamar um método service () através da


interface.
2. Existe uma classe que possui o método usefulMethod que possui a mesma
função que o método service()
3. Sua missão é escreve uma classe que herda a classe ExistingClass e que
implementa a interface ThoughtfulInterface
4. Depois você deve sobrescrever o método service() para que ele chame o método
usefulMethod()

Arquitetura de software

Definição

Não há definição padrão, universalmente aceita, para arquitetura de software.


[Software Architecture in Practice (2nd edition), (Bass, Clements, Kazman; Addison-
Wesley 2003]
“Conjunto de decisões sobre a organização de um sistema computacional. Escolha
de quais elementos irão compor o sistema, qual a responsabilidade de cada um e
qual a interação entre eles.”

Propriedades visíveis. Exemplo propriedade da camada de persistência


A arquitetura não leva em consideração propriedades que não podem ser visíveis.
Ex. Tipo de BD

Importância da arquitetura de software.


“Dividir para conquistar” Permite que várias pessoas trabalhem num projeto
complexo e em componentes diferentes sabendo apenas um pouco sobre outros
componentes. Portanto transcende as barreiras geográficas e temporais com
relação ao desenvolvimento de software.

Exemplo de arquitetura

Regras que valem para todas as camadas


A interação entre as camadas ocorrem através de interfaces
A criação de objetos de uma camada diferente utiliza o padrão factory

diagrama de componentes
usando UML 2.0

diagramas de componentes são úteis para modelar arquitetura de um sistema. Esses


diagramas mostram ao organização de um sistema de código fonte bibliotecas tabelas
de bancos de dados. Um componente isca do sistema. O UML reconhece cinco
estereótipos de componente: executavel, biblioteca, tabela, document,arquivo.ano

Você também pode gostar