Fj pratica04

  • 244 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
244
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
2
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Fundamentos de Java Série Programação JavaAssunto: • Herança Prática 4 • Sobrecarga • Polimorfismopor Frederico C. G. Pereira © Copyright 2003 Fundamentação Teórica A herança, juntamente com o conceito de abstração e o de polimorfismo, formam o tripé deconceitos do paradigma de programação orientado a objetos. As abstrações são concretizadas naslinguagens deste paradigma através do conceito de classe; em outras palavras, uma classe define umaabstração do domínio do problema. Na vida real, costumamos agrupar os objetos segundo suas similaridades. Assim, temos umataxonomia para agrupar a fauna e a flora do planeta. Ao identificarmos um determinado animal comomamífero estamos dando um conjunto de informações sobre ele, conjunto esse que é compartilhadopor outros animais totalmente diferentes do primeiro. Ao identificarmos nosso mamífero como primata,estamos especializando cada vez mais sua definição. Observe que, neste caso, está implícita umarelação muito importante: um primata é um mamífero. Ou seja, poderíamos definir nosso animalapenas como primata que o estaríamos definindo também (automaticamente) como mamífero. Na programação, que proveito poderíamos retirar da relação é-um? Resposta: oreaproveitamento de dados e de código entre classes distintas, porém inter-relacionadas. Uma classe(subclasse) que herde propriedades e métodos de uma outra classe (superclasse) está apta a: • Receber mensagens cuja implementação (método) esteja na superclasse; • Utilizar as propriedades definidas na superclasse sem restrições1 em seus próprios métodos; • Redefinir um método com o mesmo nome e lista de parâmetros de um da superclasse (sobrescrita de métodos); A herança também permite que variáveis do tipo da superclasse possam referenciar (conter)objetos do tipo da subclasse. À primeira vista, isto pode parecer confuso, mas é justamente um dosconceitos mais poderosos e úteis da orientação a objetos: o polimorfismo. Vejamos um exemplosimples: uma variável do tipo Mamífero poderia apontar para um objeto Girafa, um objeto Leão ouum objeto Homem. Quais as vantagens práticas disto? Vejamos um exemplo onde a herança e opolimorfismo fazem a diferença. Imagine que você está implementando um editor gráfico simples, capaz de desenhar váriasfiguras geométricas, tais como retângulos, círculos, quadrados, triângulos, etc. Um usuário do sistemapoderia criar vários objetos destes num canvas; sendo assim, você definiu uma classe para cada tipode figura possível. Ao implementar a classe Losango, a classe Retângulo e a classe Q uadrado, vocênotou que o método que calcula a área destas figuras é exatamente igual; além disso, para cada umadestas figuras você guarda como propriedade o tamanho do lado maior e do lado menor (podemos ver1 Java e outras linguagens permitem apenas o acesso às propriedades públicas.
  • 2. o quadrado como um caso particular em que os l dos "maior" e "menor" possuem o mesmo tamanho). aA repetição de código desnecessariamente num programa trás conseqüências indesejadas: • Durante uma manutenção, o programador deve estar atento para modificar todos os trechos que necessitam da manutenção em todas as classes; • Um trecho pode ser alterado corretamente e outro não, gerando inconsistências durante o teste do sistema; • O código ocupará mais memória durante a execução. Percebendo isso, você criou superclasses para abrigar as características comuns: uma classeQuadrilátero de onde herdam Retângulo, Quadrado e Losango; uma classe Polígono de onde Retângulo Losangoherdam Quadrilátero, Triangulo e Circulo. A classe Quadrilátero define duas propriedades para Quadrilátero Circuloguardar o tamanho do lado maior e o do lado menor do quadrilátero e também um método paracalcular a área do quadrilátero. Polígono x, y move() Círculo Quadrilátero Triângulo raio ladoMaior, ladoMenor l1, l2, l3 area(), draw() area() area(), draw() Quadrado Retangulo Losango draw() draw() draw() ; Figura 1: Hierarquia de classes de figuras Pela Figura 1, podemos concluir que um objeto da classe Retangulo terá 4 propriedades (x, y,ladoMenor e ladoMaior) e três métodos (move, area e draw). Isto é, podemos enviar uma mensagemmove() para um objeto Retangulo, mesmo não existindo um move definido nesta classe. O método Retangulomove() modifica o valor dos campos x e y, fazendo com que o objeto mude de local quando forreimpresso no vídeo. O método area() calcula a área de qualquer quadrilátero e o método draw (emcada quadrilátero) desenha o quadrilátero na saída padrão do sistema. Note que não é possívelescrever uma únic a versão de draw na classe Quadrilátero porque cada quadrilátero tem sua própriaforma de se apresentar.2 Fundamentos de Java - Prática 4 © 2003
  • 3. Voltando ao editor gráfico, cada figura que o usuário cria através da interface gráfica é,internamente, armazenada num arranjo ( array) de objetos P oligono (ou seja, só podemos colocarobjetos desta classe em cada posição do arranjo). Suponha que o usuário criou um triângulo, umcírculo, um quadrado e um losango. Em seguida, ele clicou no botão Redesenhar da interface gráficado editor. Este botão envia para cada objeto no arranjo o método draw, para que o mesmo seredesenhe. Vejamos a vantagem do polimorfismo para executar tal operação. Primeiramentemostremos como o procedimento associado ao botão Redesenha seria implementado caso nãohouvesse o polimorfismo: redesenhar() { for (int i = 0; i < MAXPOLIG; ++i) { if (poligono[i] instanceof Quadrado) //o poligono em i é um Quadrado? poligono[i].drawQuadrado(); else if (poligono[i] instanceof Retangulo) poligono[i].drawRetangulo(); else if (poligono[i] instanceof Losango) poligono[i].drawLosango(); else if (poligono[i] instanceof Circulo) poligono[i].drawCirculo(); else if (poligono[i] instanceof Triangulo) poligono[i].drawTriangulo(); } Código 1: Método redesenhar sem polimorfismo O operador instanceOf testa se o i ésimo objeto do arranjo é uma instância da classe à sua -direita. Cada classe teria seu próprio método drawXxxx(), nomes iguais não poderiam ser utilizadospara não confundir o compilador. Este código, além de ser confuso de ler, tem ainda mais umproblema: a expansão do modelo de classes do sistema. Ao adicionarmos uma nova subclasse emPoligono, digamos Elipse, teremos que adicionar mais linhas a este código. O método que redesenhaPoligono Elipseum canvas de um editor gráfico está fortemente acoplado à hierarquia de classes. Vejamos como ficaria o mesmo código com o uso do polimorfismo: redesenhar() { for (int i = 0; i < MAXPOLIG; ++i) { poligono[i].draw(); } Código 2: Método redesenhar utilizando polimorfismo Se a linguagem oferece polimorfismo, o código fica muito mais enxuto e fácil de entender. Opolimorfismo só é possível graças à ligação dinâmica existente entre a chamada do método e suaimplementação. Em outras palavras, a chamada poligono[i].draw() só será ligada a um dos métodosdraw() durante a execução do programa. Durante a compilação não há como determinar qual draw()será chamado (o de Quadrado? o de Retangulo?) porque não se sabe ainda qual polígono efetivamente Quadrado Retanguloexiste em cada posição do arranjo. Relembre a ordem em que os polígonos foram criados: umtriângulo, um círculo, um quadrado e um losango. Então, na primeira iteração do laço for seráchamado o método draw() de Triangulo, na segunda o método draw() de Circulo, na terceira o Triangulo Circulométodo draw() de Quadrado e na quarta e última o método draw() de Losango. Se adicionássemos um Losangonovíssimo objeto da classe Elipse no arranjo ele também seria redesenhado sem nenhuma Fundamentos de Java - Prática 4 © 2003 3
  • 4. modificação do código acima. É a ligação dinâmica que permite a existência do conceito depolimorfismo nas linguagens orientadas a objeto. Um outro aspecto do polimorfismo é a capacidade de se poder definir mais de um método com omesmo nome na mesma classe, tendo como diferenciador apenas a quantidade e o tipo dosparâmetros. Esta característica também é conhecida por sobrecarga ou polimorfismo paramétrico.Um exemplo: o método draw() poderia ter uma versão sobrecarregada que recebesse uma cor comoparâmetro, draw(String cor), e que seria utilizada para definir a cor de fundo da figura. Assimteríamos a opção de escolher a cor padrão (enviando draw() para o polígono) ou uma outra qualquer(enviando draw("blue")). A lista de parâmetros define qual draw será chamado. ExercíciosO objetivo desta prática é mostrar as conseqüências da herança para os objetos de classes relacionadaspor esse mecanismo. O aluno comprovará a existência da herança e do polimorfismo na linguagem Javade uma forma que não exija muito conhecimento da sintaxe da linguagem.1) Familiarize-se com o modelo de classes desta prática. a) Sob Y:projetos, crie uma pasta chamada heranca b) Descompacte o arquivo Y:Documentos do Cursoheranca.zip na pasta heranca. c) No BlueJ, selecione Projeto|Abrir Projeto..., vá para a pasta Y:projetos, selecione heranca e clique em Abrir. d) Tente identificar as relações entre as classes mostradas no diagrama. Quem herda de quem? Que classe utiliza outra para implementar sua funcionalidade? Figura 2: Diagrama de classes do projeto herança. Este modelo de classes é muito semelhante ao da última prática. A diferença fica por conta daaplicação da herança na classe Venda. Uma venda consiste numa lista de produtos a serem adquiridospor um cliente (veja Figura 2). Uma venda concreta é sempre a prazo ou à vista. A classe Venda, neste Vendacaso, serve apenas para definição da parte comum aos dois tipos de vendas. Ela contém , por exemplo,4 Fundamentos de Java - Prática 4 © 2003
  • 5. um método para indicar a quantidade de produtos na venda; outro para adicionar produtos à venda,etc. Estas operações são comuns tanto para vendas à vista como para vendas à prazo. A classe VendaVista contém um campo que indica o valor do desconto (apenas vendas à vistapossuem esta propriedade). A classe VendaPrazo , por sua vez, define uma propriedade para guardar ataxa de juros e outra para guardar a quantidade de parcelas da venda. Além destas propriedadesespecíficas, cada uma destas classes terá uma forma particular de calcular o seu valor total. A venda àvista vai aplicar um desconto (que pode ser 0) e a venda a prazo vai aplicar uma taxa de juros. Noteque não dá mais para utilizar o método calculeTotal() de Venda, pois ele não aplica desconto nemjuros ao total bruto.2) Inspecione um objeto VendaVista para constatar a existência da herança: a) Crie um objeto da classe Cliente. Cliente b) Crie um objeto Produto: informe a descrição ("Computador") e o preço do produto (2000); c) Crie agora um objeto da classe VendaVista: informe um cliente e um valor de desconto como VendaVista parâmetros. Digite o identific ador do objeto cliente2 no primeiro campo (cliente_1 na Figura 3) . Figura 3: Criando um objeto VendaVista d) Clique com o botão direito sobre o objeto vendaVis_1. O menu pop-up apresentado divide-se em três partes: a parte superior mostra os métodos herdados da superclasse Venda e da superclasse de Venda, Object; a parte do meio mostra os métodos definidos na própria classe Venda Object VendaVista e a parte de baixo são as já conhecidas operações para inspecionar e remover o objeto (Figura 4). Figura 4: Interface de métodos da classe VendaVista2 O identificador de um objeto está localizado na parte superior do ícone do objeto antes dos dois-pontos. Você pode definir esseidentificador ou aceitar o gerado automaticamente pelo BlueJ. Fundamentos de Java - Prática 4 © 2003 5
  • 6. 3) Adicione produtos ao objeto VendaVista utilizando o método adicioneProduto() herdado de Venda. a) Clique com o botão direito sobre o objeto vendaVis_1 e procure um método herdado de Venda que insira um produto na venda (ver Figura 5). b) Como parâmetro, digite o identificador do objeto Produto criado no exercício (2)b) Figura 5: Utilizando um método herdado de Venda. c) Envie uma mensagem getNumProdutos() para o objeto vendaVis_1 (procure um método com esse nome na hierarquia de métodos) para consultar a quantidade de itens da venda. O resultado deve ser 1.4) Mensagens próprias da classe VendaVista. a) Envie a mensagem calculeTotal() para o objeto VendaVista. VendaVista b) Consulte o valor atual do desconto enviando-lhe a mensagem getDesconto().5) Constate a importância do polimorfismo a) Crie um produto "DVD" com valor 600; b) Crie um objeto da classe VendaPrazo: i) Informe o identificador do objeto cliente criado no exercício (2.a) como primeiro parâmetro ii) Informe a taxa de juros: 5. iii) Informe o número de parcelas da venda a prazo: 2. c) Adicione à esta venda o produto criado no passo (a ). d) Consulte o total da venda a prazo enviando-lhe a mensagem calculeTotal(). e) Consulte o valor atual da taxa de juros enviando-lhe a mensagem getTaxaJuros(). Observe que as mensagens calculeTotal() realizam computações diferentes para cada tipo de objeto venda. Veremos agora como o polimorfismo é utilizado. f) Crie um objeto da classe Caixa: esta classe é capaz de manter um arranjo de objetos Venda, Venda independentemente de serem VendaVista ou VendaPrazo. VendaPrazo g) Envie duas mensagens adicioneVenda() ao objeto caixa: uma para adicionar o objeto VendaVista e outra para adicionar o VendaPrazo. i) Informe o identificador dos objetos VendaVista e VendaPrazo como parâmetro de cada mensagem enviada. h) Certifique-se que o seu objeto caixa possui as duas vendas: envie-lhe a mensagem getNumVendas(). O resultado deve ser 2. Se desejar, inspecione o objeto caixa para "ver" as duas vendas guardadas em suas variáveis de instância.6 Fundamentos de Java - Prática 4 © 2003
  • 7. i) Envie uma mensagem calculeTotal() para o caixa e veja quanto foi o faturamento obtido com todas as vendas3 registradas. Esta operação utiliza o polimorfismo descrito na fundamentação teórica desta prática. Vale a pena explicar o que acontece quando do envio desta mensagem para o caixa. Não vamos mostrar o código, mas apenas dar uma idéia intuitiva do que acontece nesta operação: caixa_1 calculeTotal() { adiciona juros } VendaPrazo calculeTotal () { aplica desconto } vendaVis_1 vendaPra_1 VendaVista Figura 6: Representação gráfica para entender o polimorfismo. O objeto caixa possui um arranjo com objetos de diferentes tipos em cada posição. Na primeira posição existe um objeto da classe VendaVista e na segunda um objeto da classe VendaPrazo. Isto é possível graças ao polimorfismo. Na verdade, o tipo base do arranjo é Venda, e como as duas classes herdam dela, isto é, são tipos especializados de vendas, podemos adicionar objetos destas classes no arranjo sem problemas. Cada posição do arranjo pode guardar diferentes (poly) formas (morphos) de objetos Venda. Na sua linguagem de programação atual4 você é capaz de construir um arranjo que pode guardar diferentes tipos de dados? Mas isto não é tudo! O método calculeTotal() da classe Caixa tem um algoritmo semelhante ao método redesenhar() da fundamentação teórica (ver Código 2). Em um laço, ele envia para cada objeto do seu arranjo a mensagem calculeTotal(). Se o objeto n i-ésima iteração do a laço for uma VendaVista, o método calculeTotal() desta classe será ativado, se for um VendaPrazo, o método calculeTotal() dela será ativado. Ou seja, a ligação entre a chamada venda[i].calculeTotal() dentro do método calculeTotal() de Caixa e o método calculeTotal() em cada classe acontece dinamicamente, dependendo da classe do objeto para onde se está enviando a mensagem. O programador deve ter ciência de que isso é feito assim, mas não precisa fazer nada para que aconteça, o interpretador Java cuida de tudo. A seguir, o código do método calculeTotal() de Caixa. O calculeTotal() em destaque é ligado a um dos dois totais na figura acima: /** Informa o valor total do caixa */ public double calculeTotal() { double soma=0; for(int i=0; i < numVendas ;i++) soma += vendas[i].calculeTotal(); // <<<<< polimorfismo aqui! return soma; } n3 Isto não significa que este dinheiro esteja no caixa, pois há vendas a prazo registradas.4 Em breve será sua ex-linguagem de programação, pois tenho certeza que Java a substituirá! Fundamentos de Java - Prática 4 © 2003 7