Buscar

Aula 2 - Orientação a objetos e modelagem comportamental

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 25 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 25 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 25 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Interfaces de BD
Aula 2: Orientação a objetos e modelagem comportamental
Apresentação
A programação estruturada fornece um meio bastante direto de expressar processos, adequando-se muito bem à
implementação de algoritmos, mas com a evolução dos sistemas temos a necessidade de metodologias mais
organizadas.
Trabalhar com orientação a objetos exige um bom conhecimento acerca de seus pilares: Abstração, Encapsulamento,
Herança e Polimor�smo. Não devemos manter uma visão baseada em processos, mas adotar uma nova perspectiva, com
a modelagem de personagens em termos de ações e características físicas.
Foi observado que nem tudo pode ser visto como objeto, e a modelagem comportamental desponta no mercado com
base em ferramentais, como elementos genéricos e anotações.
Objetivos
Explicar os princípios da Orientação a Objetos;
Explicar as palavras da sintaxe Java para Orientação a Objetos;
Utilizar elementos de modelagem comportamental.
Histórico e características
gerais
Um fato marcante para a tecnologia de computadores, de
forma geral, foi o surgimento da arquitetura de von
Neumann, que permitiu a coexistência de dados e
programas em uma mesma região de memória. Essa
mudança trouxe uma adaptabilidade ímpar para as
máquinas, que agora poderiam executar funções básicas
nas mais diversas ordens, a partir de comandos inseridos
dinamicamente.
Fonte: Shuttershock por catwalker
Os modelos de programação passaram por diversas mudanças ao longo da história, sempre na busca de formas mais
organizadas para expressar como a sequência de ações do computador deve ser orquestrada. No início havia apenas o
Assembly, com a mera representação das instruções básicas do processador e uso de registradores, mas logo apareceram
linguagens mais próximas da comunicação humana.
As linguagens com uso de desvios foram as primeiras, muito ancoradas na metodologia do Assembly, com saltos
condicionados ao valor de �ags, mas logo os próprios desvios passaram a ser criticados, e a programação estruturada, com
um conjunto de estruturas para controle de �uxo, torna-se o estado da arte em termos de implementação de sistemas.
Comentário
Isso não durou tanto tempo assim, pois em termos de tecnologia, tudo que é regra hoje se torna inadequado no momento
seguinte. Com a criação de sistemas cada vez maiores e de grande apelo visual, técnicas tradicionais de modelagem e
programação estruturada começaram a entrar em colapso, e complexos trechos de código inter-relacionados, com
documentação escassa e diversas replicações de processos já existentes, acabaram tornando a manutenção dos sistemas
extremamente difícil, aumentando o custo e diminuindo as possibilidades evolutivas.
A orientação a objetos surgiu nesse contexto, trazendo uma
forma bem mais organizada de trabalho, em que a
modelagem e a implementação se mostram muito mais
próximas que nas técnicas ditas tradicionais. Pelo uso
dessa metodologia, os programadores obtiveram uma série
de vantagens no médio prazo, como facilidade de
manutenção, aumento do reúso de código e documentação
automatizada.
Fonte: Shuttershock por Bakhtiar Zein
Para podermos adotar a �loso�a orientada a objetos, devemos deixar de pensar em termos de processos e funções, pois este é
o foco estruturado. Agora nosso objeto de estudo estará ancorado em personagens, pela de�nição de características físicas e
ações que eles podem assumir.
Por exemplo, na programação estruturada poderíamos ter um procedimento
para lançar projéteis, com o ângulo e impulso iniciais, cálculo da trajetória
com base na ação da gravidade e atrito com o ar, e retorno do ponto de
impacto com base nesse cálculo. Em termos de orientação a objetos, nós
devemos considerar a de�nição dos personagens Projétil e Canhão, e a
partir disso modelar em Canhão a capacidade de atirar um Projétil.
Note como essa mudança de foco nos aproxima dos aspectos observados no mundo real, determinando a primeira vantagem
que foi obtida, chamada de documentação automática, pois coloca a modelagem e a implementação em um mesmo nível.
Enquanto nos projetos estruturados temos diagramas voltados para processos, como Fluxogramas e DFDs, os quais
apresentam grande liberdade para a fase de codi�cação, na orientação a objetos temos a UML para a diagramação, e a
proximidade do nível de implementação é tão grande que ferramentas de modelagem como Enterprise Architect e Visual
Paradigm conseguem efetuar engenharia reversa sobre o código já existente, de�nindo ou complementando os diagramas do
projeto.
A metodologia orientada a objetos é baseada em quatro pilares:
01 Abstração;
02 Encapsulamento;
03 Herança
04 Polimor�smo
 Pilares
 Clique no botão acima.
Uma abstração é um modelo simpli�cado de algo, considerando apenas as características necessárias para
determinado contexto. Um ser humano, por exemplo, apresenta um enorme conjunto de características, sendo capaz
de efetuar as mais diversas ações, mas para �ns cadastrais, apenas atributos como identidade, CPF e nome completo
podem ser su�cientes.
A abstração, na orientação a objetos, é de�nida em termos de personagem, com as características físicas e as ações
que pode executar. Esse tipo de modelagem cria estruturas fechadas, independentes do ambiente, o que é chamado
de encapsulamento.
Com os personagens de�nidos, eles podem participar de qualquer processo sem modi�cações, como se �zesse parte
do roteiro de um �lme, explicando as vantagens obtidas em termos de reúso de código – nesse caso, um reúso de
escopo horizontal. Além da possibilidade de reutilização, a concentração da modelagem na �gura do personagem
permite que modi�cações sejam efetuadas de forma centralizada, repercutindo para todo o sistema, o que traz uma
grande facilidade de manutenção.
As mudanças não pararam por aí. O que foi descrito até este momento poderia ser obtido na metodologia estruturada,
com uma boa organização dos processos em termos de módulos, mas surge a herança, que não poderia ser emulada
no ambiente antigo.
Com a herança é possível de�nir um novo tipo de personagem a partir de outro já existente, aproveitando as
características do antigo dentro de um processo incremental, o que viabilizou o reúso e a manutenção no escopo
vertical. Claro que esse reaproveitamento de funcionalidades antigas pode levar à necessidade de adaptações a novos
contextos, em que o quarto pilar da orientação a objetos, o polimor�smo, permitirá a adaptação com a modi�cação de
verbos herdados.
Mesmo uma metodologia bem organizada pode ser utilizada da forma errada, e os programas orientados a objetos
acabam trazendo muitos vícios de metodologias antigas. Diante disso, modelagens padronizadas para a solução de
problemas recorrentes passam a ser adotadas, por meio de ferramentas conhecidas como padrões de
desenvolvimento.
Aliados aos padrões, elementos já previstos na orientação a objetos, como classes template, despontam no mercado
na forma de elementos genéricos. O mercado começa a abraçar a modelagem comportamental, que inclui ainda as
anotações e injeção de código.
Abstração
A Programação Orientada a Objetos (POO) teve uma utilização mais ampla com a popularização das interfaces grá�cas, pois a
metodologia estruturada não era a melhor opção para construir ambientes de janelas. Nas primeiras versões dessas interfaces
era necessário utilizar chamadas para as APIs do sistema operacional na construção de cada janela, de forma procedimental,
mas com a POO podemos de�nir um personagem denominado Janela, o qual terá atributos como posição, largura e altura, e
será capaz de efetuar ações como abrir, fechar, minimizar e maximizar, replicando o personagem quantas vezes forem
necessárias para implementar as interfaces dos sistemas.
O que estamos fazendo aqui é o processo conhecido como abstração, que se refere à de�nição de um modelo simpli�cado de
algo. Quando abstraímos um elemento, estamos preocupados apenas com os detalhes que sejam relevantes para o problema
de interesse para o sistema, e essas abstrações serão representadas comoclasses na POO, as quais trazem a de�nição dos
atributos e métodos suportados pelos personagens.
Os atributos de�nem características físicas, como cor, idade, endereço, ou sexo, enquanto os métodos são ações, ou verbos,
que são praticadas, como comer, andar ou dormir.
Comentário
Neste ponto podemos abandonar a palavra personagem e passar a tratar dos dois níveis de programação que utilizaremos:
Classes e Objetos.
Classe
Uma classe funciona como um
molde (tipo ou domínio), de
forma a de�nir como serão os
objetos criados a partir dela. Ela
de�ne apenas os tipos dos
atributos, sem assumir valores.
Objetos
Os objetos, por sua vez, são
como variáveis, ou elementos
físicos, criados a partir do molde
que é a classe. Cada objeto irá
assumir valores especí�cos para
os atributos de�nidos na classe.
É justamente a partir disso que podemos justi�car uma expressão muito comum na literatura acerca de orientação a objetos:
"Um objeto é a instância de uma classe".
Começaremos com um exemplo simples, de�nindo uma classe Pessoa, com os atributos necessários para um processo
cadastral e um método para exibição de dados. A palavra class é utilizada pelo Java para de�nir uma estrutura de classe.
 
public class Pessoa {
String nome;
String telefone;
void exibir(){
System.out.println(nome+"::"+telefone);
}
}
Após a de�nição da classe, podemos criar um programa de teste com algumas instâncias (objetos), assumindo valores
próprios, e exibindo seus dados na chamada ao método de exibição.
 
public class TestePessoa {
public static void main(String[] args) {
Pessoa p1 = new Pessoa();
Pessoa p2 = new Pessoa();
p1.nome = "João";
p1.telefone = "1111-1111";
p2.nome = "Maria";
p2.telefone = "2222-2222";
p1.exibir();
p2.exibir();
}
}
Observe a presença do operador new na de�nição das instâncias. Esse é um detalhe fundamental no estudo da orientação a
objetos, pois o operador é responsável pela alocação de memória, o que nos permite dizer que todo objeto pode ser
interpretado como um ponteiro.
Na sequência, teremos o preenchimento dos atributos nome e telefone para cada um deles, além de chamadas ao método
exibir, obtendo a saída visualizada a seguir.
Ao de�nir uma classe, podemos acrescentar métodos voltados para o preenchimento dos valores iniciais dos atributos, os
quais são chamados de construtores. Eles não alocam os objetos na memória, o que é feito pelo operador new, mas permitem
inicializar os atributos do objeto em seu primeiro momento de existência.
Em algumas linguagens temos também o método destrutor, que é usado na desalocação do objeto, mas o Java não necessita
dele, pois conta com uma tecnologia denominada garbage collector, ou coletor de lixo. Com o uso do coletor de lixo, qualquer
objeto que não possa ser acessado e não tenha mais utilidade para o programa será removido da memória.
 
public class Pessoa {
String nome;
String telefone;
Pessoa(String nome, String telefone){
this.nome = nome;
this.telefone = telefone;
}
Pessoa(){
this("","");
}
void exibir(){
System.out.println(nome+"::"+telefone);
System.out.println(nome+"::"+telefone);
}
}
Modi�camos a classe Pessoa, acrescentando dois métodos construtores. Na linguagem Java, e outras derivadas do C++, um
método construtor deve ser criado com o uso do nome da classe, e pode apresentar um conjunto de parâmetros que serão
utilizados na inicialização do objeto.
O uso de métodos com o mesmo nome, mas com listas de parâmetros
diferentes, trata de uma técnica existente já na programação estruturada, a
qual recebeu o nome de sobrecarga, como pudemos observar na classe
Pessoa. Construtores sobrecarregados são comuns no Java, permitindo que
os objetos sejam inicializados da melhor forma para cada contexto previsto.
Além do uso de sobrecarga nos métodos construtores, observe a presença do operador this, também conhecido como
ponteiro de autorreferência, que permite diferenciar os parâmetros do método e os atributos de mesmo nome no objeto.
Quando de�nimos os parâmetros do construtor, podemos utilizar qualquer nome, mas se tivéssemos adotado uma
nomenclatura que não fosse equivalente aos atributos, teríamos uma perda de semântica.
A forma mais fácil de ler a primeira linha com this seria: “O nome deste objeto receberá o nome passado como parâmetro”.
Podemos alterar nossa classe de teste para utilizar o construtor de Pessoa com base em parâmetros, simpli�cando a escrita,
mas sem alterar a saída. Note que a escolha da versão sobrecarregada que será executada ocorre a partir da análise dos tipos
de dados envolvidos na chamada.
 
public class TestePessoa {
public static void main(String[] args) {
Pessoa p1 = new Pessoa("João","1111-1111");
Pessoa p2 = new Pessoa("Maria","2222-2222");
p1.exibir();
p2.exibir();
}
}
Não podemos deixar de observar outra utilização do operador this, com a presença de parênteses e a passagem de dois textos
vazios. Nesse ponto do código, o construtor padrão chama o construtor parametrizado, passando valores vazios (não nulos)
nos parâmetros nome e telefone.
Herança
O segundo pilar da orientação a objetos é a herança, voltada
para o reúso vertical, com a de�nição de novas classes a
partir de outras já existentes, reaproveitando os atributos e
métodos originais. Apesar de a técnica oferecer um grande
nível de reutilização, ela também aumenta o acoplamento,
pois as mudanças de uma classe impactam todos os seus
descendentes, algo que pode facilitar a manutenção, mas
também pode ter efeitos negativos.
Um dos primeiros contatos que temos com o conceito de
herança ocorre nos estudos primários de biologia, quando
somos apresentados a uma hierarquia de classes nas quais
enquadramos os diversos tipos de animais. Para de�nir um
mamífero, não partimos de um modelo vazio, mas
aproveitamos todas as características da classe de animais
de sangue quente, a qual foi de�nida a partir da classe de
vertebrados.
Fonte: Shuttershock por Vecton
Na sintaxe do Java, a descendência da classe é de�nida por meio da palavra reservada extends, e assim como podemos
acessar os atributos e métodos internos de um objeto com this, utilizamos a palavra super para ter acesso aos elementos
herdados da classe original.
 
public class Profissional extends Pessoa{
String profissao;
Profissional(String nome, String telefone, String profissao) {
super(nome, telefone);
this.profissao = profissao;
}
}
A nova classe, com o nome Pro�ssional, descendente de Pessoa com o uso de extends, acumulando os métodos e atributos
originais, e de�nindo um novo atributo com o nome pro�ssao.
Note que de�nimos um novo construtor, com três parâmetros, em que os dois primeiros são repassados ao construtor herdado
de Pessoa, pelo uso de super, e o terceiro é associado ao atributo interno por meio do operador this, da mesma forma que foi
feito na classe Pessoa.
Atenção
Na linguagem Java, a de�nição de um construtor, no nível da classe, oculta todos os construtores herdados.
Encapsulamento
Classes são estruturas fechadas e devem ser capazes de determinar quais atributos e métodos poderão ser disponibilizados
para o mundo exterior, da mesma forma que um elemento físico, como os carros, nos quais temos acesso ao volante e pedais,
mas não à câmara de combustão do motor. Nas classes, o nível de acesso de�ne a visibilidade do atributo ou método.
Podemos contar com três níveis padronizados de acesso:
Público (public)
Permite que qualquer um acesse
o atributo ou método;
Privado (private)
Não permite acessos externos,
sendo utilizado apenas na
programação interna da classe;
Protegido (protected)
Permite a utilização na classe e
nos descendentes, mas não
permite acessos externos.
Além dos níveis clássicos de acesso, cada linguagem pode apresentar níveis próprios, como o published, do Delphi, que deixa o
atributo visível no editor visual de interfaces grá�cas. Para o Java, se não de�nimos nada em termos de nível de acesso, o
padrão é o de pacote, ou seja, todas as classes do mesmo pacote podem acessar, mas se ocorrer a importaçãodesse pacote
por uma classe pertencente a outro, a classe externa não terá acesso aos atributos e métodos com nível de pacote.
Saiba mais
As bibliotecas de classes do Java são organizadas em pacotes, basicamente estruturas de diretórios nos quais salvamos os
arquivos das classes. Com o pacote de�nido, as classes constituintes podem ser utilizadas em outros arquivos de código-fonte
com o uso de import.
É bastante comum a necessidade de controlar o acesso a um determinado atributo sem, no entanto, impedir esse acesso.
Nesse ponto teremos os getters e os setters. Esse controle é chamado de encapsulamento, que trata de mais um dos pilares
da orientação a objetos e, em termos formais, visa a ocultar detalhes da implementação interna da classe, fornecendo apenas
uma interface com métodos de negócio e métodos de acesso.
O método getter obtém o valor de um atributo interno, enquanto no setter escrevemos um valor no atributo, momento em que
pode ser feita uma validação de restrições associadas ao valor. O conjunto formado por esses métodos de�ne uma
propriedade da classe.
 
public class Pessoa {
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
private String nome;
private String telefone;
public Pessoa(String nome, String telefone){
this.nome = nome;
this.telefone = telefone;
}
public Pessoa(){
this("","");
}
public void exibir(){
System.out.println(getNome()+"::"+getTelefone());
}
}
Agora temos, na classe Pessoa, a de�nição dos níveis de acesso e a presença dos métodos de leitura e de escrita, e para não
ocorrer perda de semântica devido a alteração de nomes, o uso de this é necessário nos setters. Essa alteração causaria
impacto na primeira versão da classe de testes, que apresentaria erro devido ao uso de private nos atributos, forçando a
modi�cação do código para a adoção dos métodos de escrita.
 
public class TestePessoa {
public static void main(String[] args) {
Pessoa p1 = new Pessoa();
p1.setNome("João");
p1.setTelefone("1111-1111");
p1.exibir();
}
}
Devemos lembrar que estamos trabalhando em um ambiente integrado em busca de produtividade, e como tal, não
modi�caremos manualmente todo o código. Clique com o botão direito sobre qualquer um dos atributos, no editor de código-
fonte, e escolha a opção Refactor..Encapsulate Fields, ou Refatorar..Encapsular Campos, na versão em português,
selecionando as opções necessárias para a geração dos getters e setters, e tanto a classe como todos os trechos de código-
fonte nos quais os atributos são usados serão alterados de forma automática, após o clique em Refactor (Refatorar).
Polimor�smo
Quando herdamos os métodos de uma classe, alguns deles podem não se enquadrar nas necessidades do descendente,
levando à necessidade de um ferramental que traga uma característica adaptativa na implementação de programas orientados
a objetos. Nesse ponto devemos analisar o último dos pilares da orientação a objetos, o polimor�smo, que pode ser traduzido
como múltiplas formas.
Na prática, o polimor�smo se traduz na capacidade de modi�car um método herdado para que se adapte a um novo contexto,
permitindo aproveitar ou não as funcionalidades originais. Essa pode ser considerada a característica mais poderosa da
orientação a objetos, proporcionando grande �exibilidade a qualquer sistema, e devemos compreendê-la corretamente para
tirar o melhor proveito da metodologia.
Considere, por exemplo, a classe Pro�ssional, em que o método trabalhar representa o conjunto de ações especializadas que
devem ser executadas pelas suas instâncias. É bem simples compreender que um médico trabalha de forma diferente de um
advogado, ou um engenheiro, mas todos eles são pro�ssionais, e o verbo não muda, ou seja, todos trabalham.
Cada classe deverá sobrescrever o método trabalhar para que se adeque a suas necessidades. Note que aqui falamos de
sobrescrita, em que ocorre a reprogramação do método herdado, sem a mudança da assinatura, e não pode ser confundido
com sobrecarga, em que temos o mesmo método com várias assinaturas, sem a necessidade da utilização de herança.
Voltando à classe Pro�ssional de nosso exemplo de herança, se testou os objetos instanciados, já percebeu que o método
exibir continua mostrando apenas o nome e o telefone de�nidos em Pessoa. Para que ela funcione corretamente, devemos
modi�car o método herdado.
 
public class Profissional extends Pessoa{
private String profissao;
public Profissional(String nome, String telefone,String profissao) {
super(nome, telefone);
this.profissao = profissao;
}
@Override
public void exibir() {
super.exibir();
System.out.println("\tTrabalha como "+profissao);
}
}
Note que o novo método exibir chama o método originalmente herdado de Pessoa pelo uso de super. Essa chamada não é
obrigatória, mas é muito comum, pois além de aproveitar o que já existia e apenas completar a funcionalidade requerida,
garante a continuidade da manutenção, já que as alterações efetuadas no método exibir da classe Pessoa irão repercutir
diretamente no exibir de Pro�ssional, assim como de todos os descendentes que solicitam a execução do ancestral.
Observe também a presença da anotação @Override, que é colocada nos métodos polimór�cos e signi�ca sobrescrita, não
devendo ser confundida, como citado anteriormente, com a sobrecarga, cujo nome em inglês seria overload.
Se perguntamos qual o animal de estimação de alguém, a pessoa pode responder que é um cachorro, um gato, ou outro tipo de
animal. Isso se deve ao fato de que um cachorro é um animal, assim como os demais, e na orientação a objetos podemos
aplicar a mesma regra.
Atenção
Na programação orientada a objetos, qualquer descendente pode ser utilizado no lugar da classe original.
Essa regra permite outra interpretação do polimor�smo, em que dizemos que um objeto pode assumir múltiplas formas, e os
métodos se comportam de acordo com a forma (descendência) utilizada.
 
public class TestePessoaProfissional {
public static void main(String[] args) {
Pessoa[] pessoas = {
new Pessoa("Joao","1111-1111"),
new Pessoa("Maria","2222-2222"),
new Profissional("Luiz","3333-3333","Advogado")};
for(int i=0; i<3; i++)
pessoas[i].exibir();
}
}
No código podemos observar um vetor de objetos do tipo Pessoa, com duas instâncias de Pessoa e uma de Pro�ssional, o
que é possível devido à regra de que o descendente pode ser utilizado no lugar do original. Em seguida, temos um loop em que
é chamado o método exibir de cada objeto do vetor, gerando a saída que pode ser observada a seguir.
Observe atentamente a impressão dos dados do terceiro objeto, pois apesar de ser um vetor do tipo Pessoa, foi executada a
versão de Pro�ssional, e isso se deve ao polimor�smo, já que o objeto sabe que é um pro�ssional e executa a versão correta,
de�nida em sua classe.
Comentário
No Java podemos utilizar o operador instanceof para testar se um objeto é de uma determinada classe ou descendente dela.
Com esse exemplo, conseguimos compreender o signi�cado de polimor�smo na prática, ou seja, múltiplas formas de
implementar o mesmo verbo.
Elementos abstratos
A palavra abstração tem origem no verbo abstrair, ou seja, analisar um ou
mais aspectos contidos em um todo (de�nição do dicionário). Isso é o que
fazemos ao de�nir nossas classes, com o conjunto de atributos e métodos
necessários ao domínio do sistema.
Normalmente confundimos muito as palavras abstração e abstrato, mas elas apresentam signi�cados muito distintos.
Enquanto uma abstração é um modelo simpli�cado de algo, um elemento abstrato é algo intangível, que não é palpável ou
concreto.
Um exemplo clássico de algo abstrato seria mamífero, de�nindo o conjunto de animais que mamam, pois não existe um
mamífero genérico, sendo uma necessidade para os descendentes, como cachorro ou gato, implementar o verbo mamar para
que sejam considerados mamíferos.Ainda podem existir níveis intermediários abstratos, que de�nem regras adicionais, mas
ainda não representam um elemento concreto, como felino ou equino.
Esse conceito é implementado na orientação a objetos pela de�nição de classes abstratas, trazendo estruturas incompletas,
que não podem gerar objetos, mas que servem de base para impor alguma característica funcional aos descendentes, já que
eles serão obrigados a implementar as funcionalidades abstratas que foram previstas no ancestral. Podemos ver que, mesmo
não podendo gerar objetos, o elemento abstrato de�ne uma regra para seus descendentes.
 
public abstract class Mamifero {
protected String familia;
public abstract void mamar();
public String getFamilia(){
return familia;
}
}
Um método abstrato é de�nido com o uso da palavra abstract, da
mesma forma que a classe abstrata. Para que uma classe seja
abstrata, basta que um método seja abstrato, e para que a classe seja
concreta ela não pode conter nenhum método abstrato.
Para que os descendentes de Mamifero se tornem classes concretas, eles são obrigados a implementar o método mamar.
Novamente temos métodos polimór�cos, mas o uso de super não seria viável, já que é a sobrescrita de um elemento abstrato.
 
public class Cachorro extends Mamifero{
public Cachorro() {
familia = "Canidae";
}
@Override
public void mamar() {
System.out.println("Cachorro mamando...");
}
}
public class Gato extends Mamifero{
public Gato() {
familia = "Felidae";
}
@Override
public void mamar() {
System.out.println("Gato mamando...");
}
}
public class ExemploAbstratos {
public static void main(String[] args) {
Mamifero m = new Gato();
m.mamar();
}
}
Na classe de teste é instanciado um Gato no lugar de Mamifero, sendo chamado em seguida o método mamar, e devido ao
polimor�smo será executada a versão de�nida no descendente, causando a impressão da frase “Gato mamando...”.
Outro tipo de elemento abstrato é a interface, tratando de um conjunto de assinaturas de métodos que devem ser
implementados pelas classes. Aqui estamos interessados no artefato de programação utilizado pela orientação a objetos, não
devendo ser confundida com interface visual, que é voltada para a interação com o usuário.
Em termos práticos, uma interface é um conjunto de assinaturas para métodos abstratos, e devido à sua natureza, os métodos
são considerados por padrão públicos e abstratos, sem o uso de palavras reservadas. Uma observação é a de que as interfaces
não aceitam atributos, sendo de�nidas com o uso da palavra interface no lugar de class.
 
public interface CriptoData {
String decripto();
void cripto(String dados);
}
Normalmente as interfaces são implementadas por classes que participam de processos maiores, algo muito comum no uso
de frameworks. Podemos utilizar a interface do exemplo para a de�nição de um processo cadastral com o uso de dados
criptografados, em que cada classe envolvida pode adotar um algoritmo de criptogra�a próprio.
Ao implementar os métodos de uma interface, as classes passam a ser reconhecidas por qualquer ferramental em que ela é
utilizada, adaptando todo o processo às funcionalidades especí�cas de�nidas nessas classes.
 
public class GestorCripto {
public void imprimir(CriptoData objetoCripto){
System.out.println(objetoCripto.decripto());
}
}
Note que um tipo de interface pode ser utilizado como parâmetro de um método, e poderá aceitar uma instância de qualquer
classe que implemente a interface, sem a necessidade de pertencer a uma família especí�ca. Para utilizar uma interface é
necessária a palavra implements, no lugar de extends, e devemos implementar todos os métodos previstos.
 
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Coordenadas implements CriptoData{
private static final String chave = "0123456789ABCDEF";
private byte[] latlong = null;
@Override
public String decripto() {
if(latlong==null)
return null;
try {
Cipher c = Cipher.getInstance("AES");
SecretKey k = new SecretKeySpec(chave.getBytes(),"AES");
c.init(Cipher.DECRYPT_MODE, k);
return new String(c.doFinal(latlong));
}catch(Exception e){
return null;
}
}
@Override
public void cripto(String dados) {
try {
Cipher c = Cipher.getInstance("AES");
SecretKey k = new SecretKeySpec(chave.getBytes(),"AES");
c.init(Cipher.ENCRYPT_MODE, k);
System.out.println(new String(latlong = c.doFinal(dados.getBytes())));
}catch(Exception e){
latlong = null;
}
}
}
Nessa implementação da interface é utilizada a biblioteca de criptogra�a padrão do Java e uma chave �xa, de 16 bytes. No
método cripto também é impresso o valor criptografado, mas isso não seria necessário, sendo feito dessa forma apenas para
acompanhamento visual durante a execução.
Os métodos são bastante parecidos, com a inicialização da chave por meio de SecretKeySpec, e do encriptador por meio de
getInstance, devendo ser utilizado o mesmo algoritmo em ambos, no caso AES. O método doFinal faz a criptogra�a ou
decriptogra�a, de acordo com o modo de inicialização.
 
public class TesteCripto {
public static void main(String[] args) {
GestorCripto gestor = new GestorCripto();
Coordenadas coord = new Coordenadas();
coord.cripto("22.5N12.5W");
gestor.imprimir(coord);
}
}
No programa de teste nós temos uma instância de GestorCripto e outra de Coordenadas. Com a inicialização do valor das
coordenadas por meio de cripto, ocorre a criptogra�a do valor 22.5N12.5W e armazenagem no atributo interno latlong, além da
impressão do valor criptografado, e na chamada ao método imprimir, do gestor, o valor de latlong é recuperado por meio de
decripto e impresso no console.
Podemos deixar a classe de gerência mais genérica, com a utilização de valores planos e criptografados, desde que o
parâmetro seja do tipo Object. Nesse caso será necessário testar a presença da interface com instanceof, mesmo operador
utilizado para classes, e efetuar a conversão de tipo, de forma a acessar os métodos da interface, segundo o que é conhecido
como type cast.
 
public class GestorCripto {
public void imprimir(Object objetoCripto){
String valor = (objetoCripto instanceof CriptoData)? ((CriptoData)objetoCripto).decripto():
objetoCripto.toString();
System.out.println(valor);
}
}
O processo de type cast no Java é muito simples, necessitando apenas do tipo de destino entre parênteses. Com essa
modi�cação, se objetoCripto for do tipo CriptoData, ele é convertido para o tipo correto e o valor é obtido com o uso de
decripto, enquanto para os demais é utilizado toString.
Embora muitos confundam as interfaces com classes abstratas, as diferenças em termos metodológicos são muito grandes.
Enquanto as classes abstratas de�nem uma regra para sua família de classes, as interfaces podem ser implementadas por
qualquer classe que necessite participar de algum processo especí�co.
Atenção
O Java aceita apenas herança simples, ao contrário de linguagens como C++, em que é possível herdar de várias classes
simultaneamente, mas ele permite implementar quantas interfaces forem necessárias, o que viabiliza a participação em
múltiplos processos.
O uso de criptogra�a nos obrigou a adotar uma estrutura de tratamento de exceções, assunto que será abordado na próxima
aula.
Modelagem comportamental
Nem sempre conseguimos expressar os elementos do mundo físico apenas em termos de classes. Por exemplo, somos
capazes de de�nir uma classe Passaro, e instanciar todos os objetos que forem necessários, mas como podemos de�nir uma
revoada?
Para situações desse tipo, nosso foco não deve ser nos objetos individuais, mas no comportamento do grupo, levando a um
novo nível de abstração, em que modelamos comportamentos genéricos, aplicáveis a qualquer classe, pertencente a um grupo
especí�co ou não. No exemplo inicial, poderíamos de�nir o comportamento de revoada e aplicá-lo a qualquer tipo de pássaro,
mas nada impediria que fosse aproveitado para outros tipos de animais que voam, como os morcegos.Poderíamos fazer o mesmo tipo de consideração acerca das �las. Você pode ter uma sequência de pessoas, elefantes, carros,
ou qualquer outra coisa, e todas essas sequências organizadas seriam �las, independentemente dos objetos que as
constituem.
 Fonte: Shuttershock por Allen McDavid Stoddard
Essa generalização também pode ser feita para uma pilha de pratos ou de livros, pois não importa de qual tipo de objeto se
trata, e sim que eles estejam empilhados.
Na verdade, Filas e Pilhas são estruturas de dados clássicas, que utilizam ponteiros nas implementações estruturadas, ou
abordagens semelhantes na programação orientada a objetos, mas que podem ser generalizadas com a utilização de
modelagem comportamental. Para esse tipo de abordagem temos dois modelos de implementação no Java: Classes
Genéricas e Anotações.
 Classes genéricas e Anotações
 Clique no botão acima.
Classes genéricas
As classes genéricas, também chamadas de classes template, já tinham sido idealizadas no início da orientação a
objetos, e eram comuns em linguagens como o C++, mas vieram para o Java em versões um pouco mais recentes,
sendo conhecidas como Generics.
Uma classe genérica modela um comportamento com lacunas bem de�nidas e qualquer classe pode ser utilizada
para o preenchimento dessas lacunas, de�nindo a funcionalidade do processo de forma concreta.
Nesse exemplo foi de�nida uma classe genérica de Pilha, com os métodos para empilhar e desempilhar (push/pop),
em que pode ser observada a presença da lacuna de�nida pela letra K. Na verdade poderia ser qualquer letra, devendo
apenas ser colocada junto ao nome da classe.
A letra K representa uma classe qualquer, e será substituída em todas as ocorrências posteriores, quando de�nirmos o
tipo de objeto que será utilizado. Por exemplo, se utilizarmos String, o método empilhar poderia ser lido da forma
apresentada a seguir.
É interessante observar também a de�nição de uma inner class chamada NoPilha, ou seja, uma classe utilitária
interna, que só pode ser utilizada na programação da classe Pilha. Na prática ela auxilia na construção de uma
estrutura de pilha típica, em que um valor é colocado no topo, e deve apontar para o elemento logo abaixo na pilha
(proximo).
 
public class Pilha<K> {
class NoPilha
K dado;
NoPilha proximo;
}
private NoPilha<K> topo = null;
public void empilhar(K dado){
NoPilha<K> novo = new NoPilha<>();
novo.dado = dado;
novo.proximo = topo;
topo = novo;
}
public K desempilhar(){
if(topo==null)
return null;
NoPilha<K> antigo = topo;
topo = topo.proximo;
return antigo.dado;
}
}
 
public void empilhar(String dado){
NoPilha novo = new NoPilha<>();
novo.dado = dado;
novo.proximo = topo;
topo = novo;
}
Por se tratar de uma estrutura LIFO, o último elemento empilhado é o primeiro a ser recuperado, como em uma pilha
de pratos, em que não seria possível pegar o prato de baixo sem que os demais caíssem.
Nesse exemplo temos uma Pilha de Integer, classe wrapper para int, que permitirá o empilhamento de valores inteiros.
Após o empilhamento de alguns números, eles são desempilhados na variável x e impressos, até que o valor obtido
seja igual a null, sendo apresentados na ordem inversa da que foi utilizada na entrada.
Classes genéricas são amplamente utilizadas pelo framework de coleções do Java, sendo comum a utilização de
ArrayList na de�nição de conjuntos de entidades. Ao contrário dos vetores, nas coleções não estamos restritos a uma
quantidade �xa de elementos, e ainda podemos utilizar o comando for no estilo foreach.
As coleções funcionam internamente como estruturas de listas encadeadas, o que justi�ca a �exibilidade com relação
à quantidade de elementos. Note que no comportamento padrão, os elementos da lista são varridos na ordem de
entrada, conforme você poderá veri�car com a execução do exemplo.
Anotações
Em algumas linguagens, como o Java, os objetos apresentam informações que permitem o reconhecimento da
estrutura interna sem a prévia de�nição da classe. Essa característica é chamada de re�exividade computacional,
trazendo dados relevantes acerca do funcionamento da classe.
 Fonte: tutorialspoint.com.
 
public class TestePilha {
public static void main(String[] args) {
Pilha pilha1 = new Pilha<>();
pilha1.empilhar(5);
pilha1.empilhar(7);
pilha1.empilhar(9);
Integer x;
while((x=pilha1.desempilhar())!=null)
System.out.println(x);
}
}
 
import java.util.ArrayList;
public class TesteColecao {
public static void main(String[] args) {
ArrayList lista = new ArrayList<>();
lista.add(5);
lista.add(7);
lista.add(9);
for(Integer i: lista)
System.out.println(i);
}
}
javascript:void(0);
Com o uso de classes re�exivas, os frameworks conseguem buscar qualquer informação necessária para a execução
dos processos, sem que o objeto seja conhecido previamente, o que permite toda a generalização obtida em diversas
ferramentas do mercado.
O princípio de re�exividade do Java foi de�nido de forma muito simples, pois os objetos derivam direta ou
indiretamente da classe Object, e toda a de�nição estrutural é oferecida a partir de outra classe, chamada Class.
Não confunda a palavra class (minúsculo), para a de�nição de classes, com a classe Class (maiúsculo), para a
gerência de classes.
Com o uso de Class podemos instanciar objetos a partir do nome completo da classe em modo texto, obter e
manipular atributos em objetos Field, e acessar os métodos com objetos Method e Parameter.
Note que nesse exemplo o objeto da classe Pessoa é criado a partir do nome completo, incluindo o pacote, no formato
texto, e conseguimos extrair os seus atributos com objetos Field. Qualquer nome de classe que fosse utilizado iria
funcionar e mostraria os atributos de�nidos ao nível da classe.
Também podemos observar a captura dos setters e do método exibir, com a chamada a getMethod, passando o nome
e tipo dos parâmetros, em que o retorno ocorre em objetos Method. Esses objetos são acionados com o uso de
invoke, sendo passados o objeto e os valores dos parâmetros, o que proporciona uma chamada indireta aos métodos.
Com esse exemplo, nós podemos observar a manipulação de uma instância de Pessoa, sem a utilização de uma
codi�cação direta. Nos frameworks Java são utilizados processos semelhantes, con�gurados a partir de arquivos no
modo texto, como XML ou JSON.
A re�exividade computacional é a base funcional das anotações, o segundo tipo de modelagem comportamental. Elas
trabalham com metadados que podem ser reconhecidos por ferramentas externas.
Essa é uma modelagem comportamental que segue o caminho inverso da anterior, pois nas classes genéricas
aplicamos o objeto ao comportamento, enquanto nas anotações aplicamos o comportamento aos objetos. Por
exemplo, temos uma pilha de objetos, ou o objeto se comporta como uma entidade de banco.
 
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TesteReflexividade {
public static void main(String[] args) throws Exception {
Class classe = Class.forName("poo.Pessoa");
Object objeto = classe.getConstructor().newInstance();
for(Field f: classe.getDeclaredFields())
System.out.println("Campo: "+f.getName());
Method sNome = classe.getMethod("setNome", String.class);
Method sTelefone = classe.getMethod("setTelefone", String.class);
Method exibir = classe.getMethod("exibir");
sNome.invoke(objeto, "Maria");
sTelefone.invoke(objeto, "3333-3333");
exibir.invoke(objeto);
}
}
Para de�nir uma anotação, precisamos utilizar o comando @interface, além de de�nir a utilização desta com @Target,
que nesse exemplo indica que a anotação será voltada para classes, mas que poderiam ser diversos outros tipos,
como métodos e atributos, bem como o escopo de utilização com @Retention, colocado aqui como em tempo de
execução.
Para essa anotação criamos três campos, sendo que dois são de preenchimento obrigatório e um opcional, no caso
empresa, já que é de�nido um valor padrão com default.
Após criar nossa interface de anotação, podemos aplicá-la a uma classe.Com nossas anotações de�nidas e aplicadas às classes de interesse, vamos criar um processo que as utilize, da
mesma forma que os frameworks.
Inicialmente de�nimos um vetor de objetos, recebendo um Carro e uma String, e nesse caso apenas o Carro traz a
anotação de Autoria.
Ao percorrer o vetor, para cada Object dele iremos obter o Class, e em seguida perguntar se a anotação de Autoria
está presente com o uso de isAnnotationPresent. Estando presente, a anotação é capturada, sendo convertida para o
tipo correto, e os dados podem ser impressos no console.
Aqui nós apenas exibimos os dados da anotação, mas o JPA utiliza esses dados para efetuar o mapeamento entre a
entidade de classe e a tabela do banco de dados, como veremos no decorrer da disciplina.
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
public @interface Autoria {
String autor();
int ano();
String empresa() default "UNESA";
}
 
@Autoria(autor = "Denis", ano = 2020)
public class Carro {
}
 
Object[] objetos = {new Carro(), "XPTO" };
for(Object obj: objetos){
Class c1 = obj.getClass();
if(c1.isAnnotationPresent(Autoria.class)){
Autoria a1 = (Autoria)c1.getAnnotation(Autoria.class);
System.out.println("Classe " + c1.getName() + " escrita por " + a1.autor() + " em " + a1.ano());
}
}
Atividades
1. Qual das características da Orientação a Objetos permite a alteração funcional de um método herdado, proporcionando
grande �exibilidade e adaptabilidade ao ambiente?
a) Sobrecarga
b) Construtores
c) Encapsulamento
d) Polimorfismo
e) Destrutores
2. Você criou um sistema de �nanças com um núcleo de cálculo adaptável, e as classes que desejam utilizar as
funcionalidades desse núcleo podem pertencer a qualquer família, mas devem implementar um conjunto de métodos. Qual
elemento de programação você deverá utilizar para viabilizar essa funcionalidade?
a) Interface
b) Classe abstrata
c) Classe concreta
d) Herança
e) Sobrecarga
3. Segundo a sintaxe do Java, qual a palavra utilizada para indicar que uma classe é descendente de outra?
a) Extends
b) Implements
c) Super
d) This
e) Private
4. Qual a palavra reservada da sintaxe Java que permite a manutenção da semântica em setters e construtores?
a) Class
b) Public
c) New
d) This
e) Protected
5. Você desenvolveu um sistema e descobriu que as atividades de inserção, exclusão e consulta aos dados era repetitiva em
termos de programação. Com isso resolveu generalizar a solução em vez de criar dezenas de classes similares. Baseado no
esqueleto de uma das classes, crie uma solução com classe genérica.
 
public class PessoaDAO {
public void inserir(Pessoa entidade){ }
public void excluir(Integer chave){ }
public ArrayList<Pessoa> buscarTodos( ){ }
}
Notas
Título modal 1
Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográ�ca e de impressos. Lorem Ipsum é simplesmente
uma simulação de texto da indústria tipográ�ca e de impressos. Lorem Ipsum é simplesmente uma simulação de texto da
indústria tipográ�ca e de impressos.
Título modal 1
Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográ�ca e de impressos. Lorem Ipsum é simplesmente
uma simulação de texto da indústria tipográ�ca e de impressos. Lorem Ipsum é simplesmente uma simulação de texto da
indústria tipográ�ca e de impressos.
Referências
CORNELL, G.; HORSTMANN, C. Core Java. 8. ed. São Paulo: Pearson, 2010.
 
DEITEL, P.; DEITEL, H. Java: como programar. 8. ed. São Paulo: Pearson, 2010.
 
FONSECA, E. Desenvolvimento de software. Rio de Janeiro: Estácio, 2015.
 
SANTOS, F. Programação I. Rio de Janeiro: Estácio, 2017.
Próxima aula
Controle de exceções;
Manipulação de coleções;
De�nição de entidades e relacionamentos.
Explore mais
Leia os textos:
Padrões de design
Como criar um pool de objetos genéricos em Java
javascript:void(0);
javascript:void(0);

Outros materiais