Projetos de POO em JAVA

Page 1

9cm x 24cm

16,7cm x 24cm

18mm

16,7cm x 24cm

9cm x 24cm

F. Mário Martins

Nesta nova edição foram incluídos os temas das mais recentes versões do JAVA e desenvolvidas as matérias referentes a vetores e matrizes. Inclui exemplos, exercícios úteis e um projeto completo.

Dirigido aos estudantes de programação; aprofunda o estudo dos vários tipos de algoritmos expondo a análise experimental e formal da sua complexidade. Pseudocódigo compatível com C e Java.

.. .. ..

Classes e Instâncias; Encapsulamento, Modularidade e Reutilização; Todas as Coleções de Java (JCF); Hierarquia de Classes, Classes Abstratas, Herança, Interfaces e Polimorfismo; Streams de I/O.

Em cada capítulo apresenta-se uma síntese teórica dos assuntos abordados, as construções de Java necessárias ao projeto exemplo e, em seguida, vários exercícios que são analisados e completamente implementados em Java. Este livro tem como principais destinatários estudantes de nível secundário e universitário e profissionais de informática em geral.

Pela sua estrutura, pode ser usado quer como livro de base para as aulas práticas de laboratório, quer como livro de texto no caso de disciplinas com uma abordagem de ensino essencialmente prática. Principais temas abordados no livro:

. . . . . . .

Tipos Primitivos e Estruturas de Controlo; Arrays; Classes; Coleções de Java: ArrayList<E>, HashSet<E> e TreeSet<E>; Hierarquia de Classes, Herança, Classes Abstratas e Polimorfismo; Coleções de Java: HashMap<K,V> e TreeMap<K,V>; Streams de I/O.

Este livro disponibiliza ainda a correspondência dos principais termos técnicos para o português do Brasil. Livro indispensável na criação e desenvolvimento de jogos digitais: programação de jogos para web, dispositivos móveis e Windows 8. Com Python, Pygame, HTML5 e outros. Para estudantes e profissionais.

Código fonte de todos os exercícios e projetos do livro disponível em www.fca.pt, até este se esgotar ou ser publicada nova edição atualizada ou com alterações.

ISBN 978‐972‐722‐792‐1

9 789727 227921

Projetos de POO em JAVA

Este livro tem por objetivo, com base em princípios fundamentais da Engenharia de Software, apresentar regras e boas práticas na análise, conceção e desenvolvimento (projeto) de aplicações orientadas pelos objetos em geral, neste caso usando as construções da linguagem Java. O livro centra-se à volta da noção de projeto de software, sendo apresentados projetos sobre todos os assuntos essenciais à Programação Orientada pelos Objetos (POO), designadamente (além dos básicos):

Professor Associado do Departamento de Informática da Universidade do Minho. É responsável por disciplinas de licenciatura e de mestrado nas áreas de Paradigmas e Metodologias da Programação, Metodologias e Tecnologias de Objetos e Arquiteturas de Software. Os Modelos de Componentes e as Arquiteturas de Software Multicamada e Multifuncionais são as suas atuais áreas de interesse. Autor dos livros JAVA6 e Programação Orientada pelos Objectos, JAVA5 e Programação por Objectos e Programação Orientada aos Objectos em JAVA2, também publicados pela FCA.


Distribuição

Lidel – edições técnicas, lda.

Sede R. D. Estefânia, 183, R/C Dto. – 1049-057 LISBOA +351 213 511 448 * Fax: +351 213 522 684 Tel: Revenda: revenda@lidel.pt Exportação: depinternacional@lidel.pt online: livraria@lidel.pt Venda Marketing: marketing@lidel.pt

Livraria Distribuição Distribuição Distribuição Av. Praia da Vitória, 14 – 1000-247 LISBOA Tel: +351 213 511 448 * Fax: +351 213 173 259 livraria@lidel.pt

Lidel –Edição Lidel edições –Lidel edições técnicas, – edições técnicas, lda técnicas, lda lda

SEDE:SEDE: R. D.SEDE: R. Estefânia, D. R. Estefânia, D. Estefânia, 183, R/C 183, Dto., R/C 183, Dto., R/C 1049-057 Dto., 1049-057 LISBOA 1049-057 LISBOA LISBOA Internet: Internet: 21 Internet: 354 2114 354 21 1814 354 – livrarialx@lidel.pt 1814 – livrarialx@lidel.pt 18 – livrarialx@lidel.pt / Revenda: / Revenda: / Revenda: 21 351 2114 351 21 4314 351 – revenda@lidel.pt 4314 – revenda@lidel.pt 43 – revenda@lidel.pt Formação/Marketing: Formação/Marketing: Formação/Marketing: 21 351 2114 351 21 4814 351 – formacao@lidel.pt 4814 – formacao@lidel.pt 48 – formacao@lidel.pt / marketing@lidel.pt / marketing@lidel.pt / marketing@lidel.pt Ens. Línguas/Exportação: Ens.deLínguas/Exportação: Ens. Línguas/Exportação: 21 351 2114 351 21 4214 351 – depinternacional@lidel.pt 4214 – depinternacional@lidel.pt 42 – depinternacional@lidel.pt FCA – Editora Informática Fax: 21 Fax: 357 21 Fax: 78 357 21 2778 357 – 21 2778 352 – 21 2726 352 – 21 8426 352 8426 84 Av. Praia da Vitória, 14 – 1000-247 LISBOA Tel: +351 213 511 448 LIVRARIAS: LIVRARIAS: LIVRARIAS: LISBOA: LISBOA: Av. LISBOA: Praia Av. Praia Av. da Vitória, Praia da Vitória, da 14Vitória, – 1000-247 14 – 1000-247 14 – LISBOA 1000-247 LISBOA – Tel.: LISBOA – 21 Tel.: 354 – 21 Tel.: 14 354 21 18,14 354 e-mail: 18, 14e-mail: 18, livrarialx@lidel.pt e-mail: livrarialx@lidel.pt livrarialx@lidel.pt Email: fca@fca.pt PORTO: PORTO: R.PORTO: Damião R. Damião R.de Damião Góis, de Góis, 452 de Góis, –452 4050-224 –452 4050-224 – PORTO 4050-224 PORTO – Tel.: PORTO – 22 Tel.: 557 – 22 Tel.: 35 557 22 10,35 557 e-mail: 10,35e-mail: 10, delporto@lidel.pt e-mail: delporto@lidel.pt delporto@lidel.pt Copyright © julho 2014 FCA – Editora de Informática, Lda. ISBN: 978-972-722-792-1 Capa: José Manuel Reis Copyright Copyright Copyright © Fevereiro Fevereiro © Miguel Fevereiro 2011 2011 2011 Ilustração da©capa: Montenegro FCA Pré-impressão: FCA – Editora FCA – Editora de – Editora Informática, de Informática, de Informática, Lda. Carlos Mendes Lda. Lda. Impressão e acabamento: Cafilesa – Soluções Gráficas, Lda. – Venda do Pinheiro ISBN:ISBN: 978-972-722-679-5 ISBN: 978-972-722-679-5 978-972-722-679-5 Depósito Legal N.º 377682/14 Capa: Capa: José Capa: José M. Ferrão José M. Ferrão M. – Look-Ahead Ferrão – Look-Ahead – Look-Ahead Livro segundo o Novo Acordo Ortográfico Impressão Impressão Impressão e acabamento: e acabamento: e acabamento: Tipografia Tipografia Tipografia Rolo Rolo & Filhos, Rolo & Filhos, II&– Filhos, S.A. II – S.A. II – S.A. Todos os nossos livros passam Depósito Depósito Depósito Legal Legal N.º Legal ……… N.º ……… N.º ………por um rigoroso controlo de qualidade, no entanto, aconselhamos a consulta periódica do nosso site (www.fca.pt) para fazer o download de eventuais correções.

nomes comerciais referenciados neste livro têm patente registada. Os

Registadas Registadas de FCA FCA – de Editora FCA – Editora de – Editora Informática, de Informática, de Informática, Lda. Marcas Marcas Marcas Registadas Registadas Marcas dede FCA – Editora de Informática, Lda. Lda. – Lda.

®

®

®

®

®

®

® ® ®

Reservados todos os direitos. Estauma publicação nem transmitida, todo ou em parte, Este pictograma Este Este pictograma pictograma merece merece uma merece uma explicação. explicação. explicação. O não seu Opode propósito seu Oser propósito seureproduzida, propósito é alertar é alertar o é leitor alertar o leitor para o leitor para a no ameaça para a ameaça a ameaça que que que por qualquer processo eletrónico, mecânico, fotocópia, digitalização, gravação, sistema de armazenamento representa representa representa parapara o futuro para o futuro da o futuro escrita, da escrita, da nomeadamente escrita, nomeadamente nomeadamente na área na área da na edição área da edição datécnica edição técnica etécnica universitária, e universitária, e universitária, o oe o disponibilização demassivo informação, sítio blogue ou outros, sem prévia autorização escrita da Editora, exceto desenvolvimento desenvolvimento desenvolvimento massivo da massivo fotocópia. da fotocópia. da Web, fotocópia. o permitido pelo CDADC, em termos deque cópia privada pela AGECOP –alei, Associação para aautorização Gestão da Cópia O Código O Código Odo Código Direito do Direito do de Direito Autor de Autor de estabelece Autor estabelece estabelece é que crime é que crime punido é crime punido por punido lei, por fotocópia poralei, fotocópia a fotocópia sem sem sem autorização autorização Privada, pagamento taxas. dos proprietários dos proprietários dosatravés proprietários do do copyright. do copyright. do copyright. No das entanto, Norespetivas entanto, No esta entanto, esta prática esta prática generalizou-se prática generalizou-se generalizou-se sobretudo sobretudo sobretudo no ensino no ensino no superior, ensino superior, superior, provocando provocando provocando uma uma queda uma queda substancial queda substancial substancial na compra na compra na de compra livros de livros de técnicos. livros técnicos. técnicos. Assim, Assim, num Assim, num paísnum país em que país em que a em que a a literatura literatura literatura técnica técnica étécnica tãoéescassa, tãoéescassa, tão os escassa, autores os autores osnão autores sentem não sentem nãomotivação sentem motivação motivação parapara criarpara criar obras criar obras inéditas obras inéditas einéditas fazêe fazêe fazê-las publicar, -las publicar, -las ficando publicar, ficando os ficando leitores os leitores os impossibilitados leitores impossibilitados impossibilitados de ter debibliografia ter debibliografia ter bibliografia em português. em português. em português. Lembramos portanto, portanto, portanto, que que é expressamente que é expressamente é expressamente proibida proibida proibida a reprodução, a reprodução, a reprodução, no todo no todo no ou todo em ou parte, em ou parte, em daparte, da da Lembramos Lembramos presente presente obra presente obra semobra autorização sem sem autorização autorização da editora. da editora. da editora.


ÍNDICE GERAL

1

TIPOS PRIMITIVOS E ESTRUTURAS DE CONTROLO

1

1.1

SÍNTESE TEÓRICA............................................................................................................................................................ 1

1.2

SINTAXE ESSENCIAL....................................................................................................................................................... 3 1.2.1

ESTRUTURA BASE DE UM PROGRAMA JAVA...................................................................................... 3

1.2.2 COMPILAÇÃO E EXECUÇÃO A PARTIR DA LINHA DE COMANDOS............................................. 3 1.2.3 EDIÇÃO, COMPILAÇÃO E EXECUÇÃO USANDO BLUEJ...................................................................... 4 1.2.4 TIPOS PRIMITIVOS E OPERADORES......................................................................................................... 6 1.2.5 DECLARAÇÃO E INICIALIZAÇÃO DE VARIÁVEIS................................................................................. 6 1.2.6 DECLARAÇÃO DE CONSTANTES................................................................................................................. 7 1.2.7 COMENTÁRIOS..................................................................................................................................................... 8 1.2.8 ESTRUTURAS DE CONTROLO....................................................................................................................... 8 1.2.9 IMPORTAÇÃO NORMAL E ESTÁTICA: REUTILIZAÇÃO DE CLASSES.......................................... 9 1.2.10 ESCRITA BÁSICA E ESCRITA FORMATADA............................................................................................ 9 1.2.11 LEITURA DE DADOS: CLASSE Scanner................................................................................................. 10 1.2.12 CLASSES PREDEFINIDAS IMPORTANTES............................................................................................... 10 EXERCÍCIOS..................................................................................................................................................................................... 12

2

ARRAYS 29 2.1

SÍNTESE TEÓRICA............................................................................................................................................................ 29

2.2 SINTAXE ESSENCIAL....................................................................................................................................................... 30 2.2.1 DECLARAÇÕES, INICIALIZAÇÕES E DIMENSIONAMENTO............................................................... 30 2.2.2 DIMENSÃO E ACESSO AOS ELEMENTOS............................................................................................... 31 2.2.3 LEITURA E INSERÇÃO DE VALORES.......................................................................................................... 32 2.2.4 ALGORITMO DE PROCURA............................................................................................................................. 33 2.2.5 CÓPIA ENTRE ARRAYS.................................................................................................................................... 34 2.2.6 CLASSE java.util.Arrays (DE TIPOS SIMPLES)........................................................................... 34

EXERCÍCIOS..................................................................................................................................................................................... 35

3 CLASSES 3.1

47

SÍNTESE TEÓRICA............................................................................................................................................................ 47

3.2 PARADIGMA IMPERATIVO VERSUS ORIENTAÇÃO PELOS OBJETOS..................................................... 50 3.3 CLASSES SIMPLES, OBJETOS E COMPORTAMENTO...................................................................................... 53 3.4 CLASSES SIMPLES E MÉTODOS COMPLEMENTARES.................................................................................... 56 3.5 REGRAS FUNDAMENTAIS PARA A CRIAÇÃO DE CLASSES......................................................................... 60 3.6 DESENVOLVIMENTO EM BLUEJ................................................................................................................................ 61 EXERCÍCIOS..................................................................................................................................................................................... 63

4 COLEÇÕES DE JAVA: ArrayList<E> , HashSet<E> E TreeSet<E> 101 4.1

SÍNTESE TEÓRICA ArrayList<E>......................................................................................................................... 101 4.1.1

API ESSENCIAL DE ArrayList<E>.......................................................................................................... 102

4.1.2 EXEMPLOS SIMPLES COM ArrayList<E>........................................................................................... 103 © FCA – EDITORA DE INFORMÁTICA

V


PROJETOS DE POO EM JAVA 4.2 SÍNTESE TEÓRICA HashSet<E> E TreeSet<E>.............................................................................................. 106

4.2.1 API ESSENCIAL DE HashSet<E> E TreeSet<E>.................................................................................. 107

4.3 BOXING E UNBOXING...................................................................................................................................................... 108 4.4 PROJETO EXEMPLO........................................................................................................................................................ 108 4.4.1 INTRODUÇÃO........................................................................................................................................................ 108 4.4.2 REQUISITOS.......................................................................................................................................................... 109 4.4.3 ANÁLISE DOS REQUISITOS............................................................................................................................ 110 4.4.4 PROTOTIPAGEM EM BLUEJ........................................................................................................................... 118 4.4.5 CLASSE DE TESTE.............................................................................................................................................. 118 4.4.6 EXECUÇÃO E TESTE EM BLUEJ.................................................................................................................... 119 EXERCÍCIOS..................................................................................................................................................................................... 123

5 HIERARQUIA DE CLASSES, HERANÇA, CLASSES ABSTRATAS E POLIMORFISMO 5.1

153

SÍNTESE TEÓRICA............................................................................................................................................................ 153

5.2 HIERARQUIA E HERANÇA............................................................................................................................................ 154 5.3 HERANÇA DE Object.................................................................................................................................................... 156 5.4 HERANÇA E ENCAPSULAMENTO............................................................................................................................. 156 5.5 CRIAÇÃO DE CLASSES VIA HERANÇA: EXEMPLOS......................................................................................... 157 5.5.1 SUBCLASSES DE Ponto2D............................................................................................................................ 157

EXERCÍCIOS..................................................................................................................................................................................... 160

5.6 TIPO ESTÁTICO E TIPO DINÂMICO............................................................................................................................ 166 5.7 CLASSES ABSTRATAS................................................................................................................................................... 176 5.8 HIERARQUIA, POLIMORFISMO E EXTENSIBILIDADE....................................................................................... 179

6 COLEÇÕES DE JAVA: HashMap<K,V> E TreeMap<K,V> 193 6.1

SÍNTESE TEÓRICA............................................................................................................................................................ 193

6.2 REGRAS BÁSICAS DE UTILIZAÇÃO DE COLEÇÕES.......................................................................................... 195 EXERCÍCIOS..................................................................................................................................................................................... 196 6.3 INTERFACES Comparable<T> E Comparator<T>....................................................................................... 235 6.4 GENERALIZAÇÃO DO CÓDIGO E ABSTRAÇÃO................................................................................................... 254

6.5 NOTAÇÃO DIAMANTE <>............................................................................................................................................. 256

7

STREAMS DE I/O 7.1

259

SÍNTESE TEÓRICA............................................................................................................................................................ 259

7.2 I/O DE LINHAS DE TEXTO............................................................................................................................................ 266 EXERCÍCIOS..................................................................................................................................................................................... 266 7.3 FICHEIROS DE TEXTO..................................................................................................................................................... 285 7.4 OBJECT STREAMS........................................................................................................................................................... 301

A ANEXO – API DE CLASSES ADICIONAIS

305

A.1 API DE CLASSES NÃO-COLEÇÕES USADAS NOS PROJETOS.................................................................... 305

GLOSSÁRIO DE TERMOS – PORTUGUÊS EUROPEU/PORTUGUÊS DO BRASIL ÍNDICE REMISSIVO VI

© FCA – EDITORA DE INFORMÁTICA

309 311


CLASSES

3

Façamos então o seguinte exercício: Que atributos representam a nossa abstração de uma televisão e que mensagens pretendemos enviar a tv_kk e para quê, ou seja, que deverão fazer os métodos associados a tais mensagens? Visualizemos o puzzle (Figura 3.3):

FIGURA 3.3 – O PUZZLE FUNDAMENTAL DA POO

A resolução gradual deste puzzle, que consiste em determinar as mensagens a que o objeto deverá ser capaz de responder e, em consequência, quais os seus atributos indispensáveis, representa uma das primeiras fases do processo de conceção OO. Imediatamente a seguir vamos definir uma sintaxe adequada para cada uma das mensagens, designadamente o seu nome e, eventualmente, os identificadores e tipos dos seus parâmetros. Em paralelo, deveremos dar toda a atenção aos atributos necessários ao objeto (e que representam a cada momento o seu estado interno) e respetivos tipos. Muitos destes atributos resultam diretamente das propriedades do objeto do mundo real que se pretendem representar no projeto, enquanto outros resultam da necessidade de o objeto exibir os comportamentos requeridos por cada uma das mensagens que foram sendo definidas. No caso da televisão poderíamos ter, por exemplo, as seguintes mensagens, entre outras (Figura 3.4):

FIGURA 3.4 – FASE INICIAL DO PROCESSO DE CONCEÇÃO OO: MENSAGENS E ATRIBUTOS

Muitos dos atributos inferem-se do comportamento esperado. Assim, se temos mensagens para ligar on() e desligar off() um televisor, deveremos então ter um atributo que registe o estado atual do dispositivo. Dentre muitas hipóteses, ter uma variável de instância definida como boolean estado; seria suficiente. Terminado o processo de conceção, teremos de definir em Java a classe Tv onde vamos declarar todos os atributos e todos os métodos que são comuns a todos os objetos de software que acabámos de conceber. A Figura 3.5 sintetiza a estrutura típica mais simples de definição de uma classe. Designam-se por membros de uma classe, entre outros, os métodos de instância, as variáveis de instância e os construtores. De momento são estes os membros mais importantes para nós, podendo desde já © FCA – EDITORA DE INFORMÁTICA

51


PROJETOS DE POO EM JAVA afirmar-se também que uma classe poderá ter as suas próprias variáveis e os seus próprios métodos, designados por métodos de classe.

FIGURA 3.5 – ESTRUTURA DE DEFINIÇÃO DE INSTÂNCIAS TÍPICA DE UMA CLASSE

Todos os membros de uma classe possuem o designado modificador de acesso (ou tipo de acesso), que é uma palavra reservada de Java dentre public, private ou protected (ou mesmo nenhuma destas caso se use a definição por omissão) que define quais os contextos em que tais membros são acessíveis (ou visíveis) e podem, portanto, ser usados. Por exemplo, a maioria das classes que desenvolveremos será de tipo de acesso public, logo globalmente acessível no ambiente Java. As variáveis de instância, que representam os atributos privados do objeto, serão, por tal motivo, definidas como private (por razões que veremos adiante serão sempre private) e os métodos de instância serão public, caso se pretenda que sejam invocados do exterior, ou private, se forem métodos apenas invocáveis a partir de outros métodos de instância (em geral, neste caso serão métodos auxiliares). Voltaremos com mais detalhe à estrutura de definição de uma classe após a apresentação de um exemplo concreto, que pretende consolidar as noções até agora apresentadas de objeto, estado interno, métodos e mensagens.

52

© FCA – EDITORA DE INFORMÁTICA


CLASSES

3

3.3 CLASSES SIMPLES, OBJETOS E COMPORTAMENTO Costuma dizer-se em linguagem POO que os objetos são “representações software” (criadas por nós) de entidades do mundo real. Vejamos então como chegamos a tais representações para um exemplo concreto do mundo real: um copo (Figura 3.6)

FIGURA 3.6 – OBJETOS SÃO REPRESENTAÇÕES DE ENTIDADES REAIS

O exemplo é propositadamente estranho, dado que, à primeira vista, não parece muito interessante ou plausível modelar um copo. Porém, vamos analisar a questão por partes e realizar a conceção pensando nos comportamentos e nos atributos a representar. Comecemos pelos comportamentos. Os requisitos normais do problema indicam-nos que pretendemos que este objeto possua os seguintes comportamentos usuais, depois de ser criado: 1. Possa receber uma dada quantidade de líquido (em centilitros – cl) sem transbordar. 2. Possa ser esvaziado de uma dada quantidade de líquido (em cl) até ficar vazio. 3. Permita saber se está já cheio ou não. 4. Permita saber que líquido contém num dado momento. 5. Permita saber qual a sua capacidade total. 6. Permita saber em percentagem inteira que quantidade de líquido contém. Empregando os princípios simples de conceção anteriormente referidos, somos conduzidos à definição sintática de um conjunto de mensagens que pretendemos ver respondidas por qualquer objeto Copo que delas venha a ser o efetivo recetor (Figura 3.7).

FIGURA 3.7 – OBJETOS EXIBEM COMPORTAMENTO EM RESPOSTA A MENSAGENS

Note-se desde já que certos requisitos de mensagens irão corresponder a comportamentos de modificação do estado interno do objeto, enquanto outros são apenas consultas ao seu atual estado interno. A sintaxe das mensagens (logo, dos respetivos métodos de instância) torna a distinção óbvia: métodos sem resultado (cf. void) são modificadores; métodos com resultado são de consulta. Os requisitos 4 e 5 referem explicitamente certos atributos e propriedades que apenas podem ser satisfeitos se existirem variáveis de instância no objeto que os representem de forma direta, designadamente: tipo de líquido e capacidade do copo. O requisito 6 refere a percentagem entre a © FCA – EDITORA DE INFORMÁTICA

53


PROJETOS DE POO EM JAVA capacidade e a quantidade contida, pelo que necessitamos de possuir uma variável de instância que, a cada momento, guarde a quantidade contida. Acabamos de encontrar as três variáveis de instância (atributos) representativas do estado interno do nosso modelo de um copo, designadamente: nome do líquido, capacidade e quanto contém. Serão declaradas como: private String liquido; private double capac; private double contem;

Os métodos que consultam ou alteram diretamente as variáveis de instância devem, em Java (por questões de estilo mas não só), possuir nomes standard, designadamente getNomeVariavel() e setNomeVariavel(tipo var);, tal como se apresenta na Figura 3.7. Antes mesmo de escrevermos o código final da classe Copo, vamos, através de algumas imagens, ilustrar os comportamentos esperados de alguns construtores e métodos de instância. Comecemos por criar dois objetos da classe Copo, usando dois construtores diferentes mas típicos: o construtor vazio (sem parâmetros) e um outro construtor com dois parâmetros. Usemos o construtor vazio, cf. a seguinte declaração: Copo copo1 = new Copo();

O construtor vazio não possui parâmetros, pelo que os valores iniciais das variáveis de instância são atribuídos no código do próprio construtor. A Figura 3.8 mostra quais as inicializações realizadas e o estado do objeto criado.

FIGURA 3.8 – CONSTRUTOR VAZIO E INICIALIZAÇÃO

O segundo construtor recebe dois parâmetros, que são os valores iniciais das variáveis liquido e capac. A variável contem, sendo do tipo double, é automaticamente inicializada a 0.0 (Figura 3.9). Copo copo2 = new Copo("sumo", 33.0);

FIGURA 3.9 – CONSTRUTOR COM PARÂMETROS

Considerando o estado de copo2 imediatamente após a sua criação (cf. Figura 3.9), vejamos os resultados do envio de algumas das mensagens programadas na classe Copo e as alterações do estado interno do objeto ou as suas respostas (Figuras 3.10 e 3.11). copo2.encher(20.0); 54

© FCA – EDITORA DE INFORMÁTICA


CLASSES

3

FIGURA 3.10 – RESULTADO DE copo2.encher(20.0)

copo2.esvaziar(10.0);

FIGURA 3.11 – RESULTADO DE copo2.esvaziar(10.0)

Finalmente, e tendo em atenção o estado de copo2 num dado momento, vejamos os resultados obtidos após o envio de duas mensagens de consulta (Figuras 3.12 e 3.13): String nomeLiq = copo2.getLiquido();

FIGURA 3.12 – RESULTADO DE getLiquido()

boolean maximo = copo2.cheio();

FIGURA 3.13 – RESULTADO DE cheio()

Apresenta-se em seguida o código da classe Copo tal como concebido a partir dos requisitos analisados anteriormente. As classes de Java possuem, em geral, mais métodos do que aqueles que são encontrados no processo normal de conceção a partir dos requisitos dos problemas. Por exemplo, é usual ter um método getX(); e um método setX(tipo var); para cada variável de instância X, e ainda outros métodos complementares, que serão introduzidos na secção seguinte. © FCA – EDITORA DE INFORMÁTICA

55


PROJETOS DE POO EM JAVA /** Classe que define uma representação para copos de líquidos, que podem ser enchidos, esvaziados, etc. */ import static java.lang.Math.round; public class Copo {

// Variáveis de Instância private String liquido; private double capac; private double contem;

// Construtores public Copo() { liquido = "??"; capac = 25.0; contem = 0.0; } public Copo(String liq, double vol) { liquido = liq; capac = vol; contem = 0.0; }

// Métodos de Instância public String getLiquido() { return liquido; }

public double getCapacidade() { return capac; }

public double getQuant() { return contem; }

public void encher(double vol) { if (contem + vol >= capac) contem = capac; else contem += vol; } public void setLiquido(String nomeLiq) { liquid = nomeLiq; } public void esvaziar(double vol) { double aux = contem – vol; contem = (aux <= 0.0) ? 0.0 : aux; }

public boolean cheio() { return contem == capac; }

public int quantoEmPerc() { return (int) round(contem/capac*100); } }

3.4 CLASSES SIMPLES E MÉTODOS COMPLEMENTARES A classe Ponto2D apresentada em seguida é um exemplo do que se pode designar por uma classe completamente definida. Para além dos métodos considerados fundamentais para a manipulação e consulta dos objetos Ponto2D, a classe apresenta os três construtores típicos, vazio, das partes e de cópia, e os métodos fundamentais equals(), toString() e clone(). Não referiremos aqui o método hashCode() que, em certos contextos, é mesmo um método obrigatório e muito ligado ao próprio método equals(). 56

© FCA – EDITORA DE INFORMÁTICA


COLEÇÕES DE JAVA: ArrayList<E>, HashSet<E> E TreeSet<E>

4

Vamos em seguida analisar a estrutura e o comportamento da classe que irá agregar estas fichas, classe que designaremos por TurmaList.

4.4.3.2 CLASSE TurmaList Na primeira versão deste projeto, e de forma propositada, vamos considerar que a classe TurmaList estrutura as fichas de aluno sob a forma de uma lista de fichas sem ordem especial a não ser a sua ordem de entrada, tendo-se assim decidido utilizar a classe ArrayList<E>, que implementa uma estrutura sequencial indexada de 0 até n – uma lista – em arrays dinâmicos em tamanho, e virtualmente infinitos de elementos de tipo E, possuindo um enorme conjunto de métodos para operar sobre tal estrutura. Assim, para além do seu código e do seu nome, a classe turma possuirá um arraylist de FichaAluno, cf. variáveis de instância a seguir apresentadas. // Variáveis de Instância private String codigo; // código interno da disciplina private String nomeDiscp; private ArrayList<FichaAluno> turma;

Vamos criar os construtores usuais: um que redefine o construtor por omissão, outro que corresponde ao construtor completo ou das partes e o construtor de cópia. public TurmaList() { codigo = ""; nome = ""; turma = new ArrayList<FichaAluno>(); } public TurmaList(String cod, String nm) { codigo = cod; nome = nm; turma = new ArrayList<FichaAluno>(); } public TurmaList(TurmaList t) { codigo = t.getCodigo(); nome = t.getNome(); turma = new ArrayList<FichaAluno>(); for(FichaAluno fa : t.daFichas()) turma.add(fa.clone()); } /** Construtor completo. Insere um arraylist de fichas válidas */ public TurmaList(String cod, String nm, ArrayList<FichaAluno> colFichas) { codigo = cod; nome = nm; turma = new ArrayList<FichaAluno>(); turma.addAll(colFichas); // atenção: código incorreto! }

O construtor completo assume que o arraylist de fichas de aluno que lhe é passado como parâmetro foi anteriormente validado, ou seja, o construtor não assume qualquer responsabilidade por introduzir fichas que possam conter incoerências, isto é, que não cumpram as propriedades (invariantes, requisitos, regras, etc.). © FCA – EDITORA DE INFORMÁTICA

113


PROJETOS DE POO EM JAVA Este construtor, aparentemente correto, não funciona como se pretenderia, dado que o método addAll() de algumas coleções de Java não faz, nem poderia fazer, uma cópia do objeto a adicionar à coleção recetora, apenas passando o endereço do objeto. Ou seja, este objeto passa a estar partilhado pelas duas coleções: a recetora e a coleção parâmetro. Como sabemos, tal não pode acontecer. O código está errado. O código seguinte, que se baseia na utilização do construtor de ArrayList<E> que aceita uma coleção como parâmetro e inicializa o arraylist que está a criar com os elementos de tal coleção, também está apenas sintaticamente correto. De facto, o resultado desta sintaxe é exatamente igual ao uso de addAll() e, portanto, os endereços das FichaAluno serão mais uma vez partilhados. Para que o código seja de facto correto, é necessário que cada FichaAluno oriunda do parâmetro de entrada seja, por nós, explicitamente copiada para a coleção recetora, usando o método clone() definido na classe FichaAluno. Teremos, finalmente, uma deep copy e endereços não partilhados, ou seja, objetos distintos (ainda que com valores iguais). /** Construtor completo. Insere um arraylist de fichas válidas em turma */ public TurmaList(String cod, String nm, ArrayList<FichaAluno> colFichas) { codigo = cod; nome = nm; turma = new ArrayList<FichaAluno>(); for(FichaAluno ficha : colFichas) turma.add(ficha.clone()); // cópia antes de inserir }

Vamos agora analisar os métodos de instância de TurmaList, começando pelos usuais métodos de consulta e de modificação. // Métodos de Instância /** Devolve o nome da disciplina/turma */ public String getNome() { return nomeDiscp; } /** Muda o nome da disciplina/turma */ public void mudaNomeDiscip(String novoNome) { nomeDiscp = novoNome; } /** Devolve o código da disciplina/turma */ public String getCodigo() { return codigo; } /** Determina o número de alunos da turma */ public int numAlunos() { return turma.size(); } /** Devolve uma cópia do arraylist de FichaAluno */ public ArrayList<FichaAluno> daFichas() { ArrayList<FichaAluno> fichas = new ArrayList<FichaAluno>(); for(FichaAluno fa : turma) fichas.add(fa.clone()); return fichas; }

Criar uma lista com os números dos alunos da turma, sendo os números dos alunos da turma do tipo String, pode ser resolvido usando um ArrayList<String> que se inicializa a vazio e para o qual se copiam, um a um, todos os números dos alunos obtidos de cada uma das fichas encontradas no arraylist turma. Para percorrer o arraylist, vamos mais uma vez usar o iterador sobre coleções for(each) (ler “para cada … obtida de ...”).

114

© FCA – EDITORA DE INFORMÁTICA


COLEÇÕES DE JAVA: ArrayList<E>, HashSet<E> E TreeSet<E>

4

/** Devolve uma lista com os números dos alunos */ public ArrayList<String> codigos() { ArrayList<String> cods = new ArrayList<String>(); for(FichaAluno ficha : turma) cods.add(ficha.getNumero()); return cods; }

Estando tal solução perfeitamente correta do ponto de vista algorítmico, tanto mais que não existem dois alunos com o mesmo número e, portanto, tal lista, ainda que sendo uma lista, é de facto um conjunto, pois não contém duplicados, a verdade é que, em Informática, existe uma certa tendência para nos requisitos dos projetos nos pedirem listas de coisas que, de facto, não são listas mas sim conjuntos. No exemplo anterior, e embora nos tenha sido pedida uma lista dos números dos alunos e tenha sido isso que foi documentado e programado, mais correto seria termos programado e documentado de forma explícita que o resultado do método é um conjunto de números de alunos, pois é uma coleção onde não devem existir duplicados. Procurando realizar tal correção, que é bastante simples, teríamos apenas de procurar saber quais as classes de Java que implementam conjuntos matemáticos. Teríamos duas possíveis implementações genéricas: TreeSet<E> e HashSet<E>. A classe TreeSet<E> não só implementa conjuntos, mas também permite definir uma ordenação dos seus elementos, e vamos usá-la neste caso. /** Cria um conjunto com os números dos alunos */ public TreeSet<String> codigos() { TreeSet<String> cods = new TreeSet<String>(); for(FichaAluno ficha : turma) cods.add(ficha.getNumero()); return cods; }

Verificar se um determinado aluno cujo número é dado está inscrito na turma consiste em realizar uma operação de pesquisa sequencial sobre as fichas dos alunos da turma, quer até encontrar tal número, em cujo caso o aluno está inscrito, quer até esgotar o conjunto das fichas, em cujo caso o aluno não está inscrito. Como não se trata de um algoritmo que necessite garantidamente de percorrer todo o espaço de procura (todas as fichas), pois pode terminar a qualquer momento, ou seja, não é exaustivo ou de “varrimento”, deve ser usado um Iterator<E>, porque o ciclo for(..) sobre coleções não permite incluir condições de paragem da iteração. /** Verifica se um aluno de número dado existe */ public boolean existeAluno(String numAluno) { Iterator<FichaAluno> it = turma.iterator(); FichaAluno fiche = null; String numero; boolean existe = false; while(it.hasNext() && !existe) { ficha = it.next(); numero = ficha.getNumero(); if(numero.equals(numAluno)) existe = true; } return existe; }

© FCA – EDITORA DE INFORMÁTICA

115


PROJETOS DE POO EM JAVA out.println("-- EMPREGADOS POR CÓDIGO --\n"); lstEmp = empresa.ordenaPor(new EmpregadoComparator()); for(Empregado e : lstEmp) out.println(e.toString()); out.println("-- EMPREGADOS POR SALÁRIO --\n"); lstEmp = empresa.ordenaPor(new EmpSalarioComparator()); for(Empregado e : lstEmp) out.println(e.toString());

Como se pode verificar, criadas as classes que implementam os comparadores e criado um método flexível tal como ordenaPor();, temos possibilidade de, em qualquer projeto, implementar facilmente a criação de listagens (que são conjuntos) de objetos por ordens diversas. Repare-se que nos exemplos anteriores usámos a classe Produto e a classe Empregado sem termos tido de alterar o seu código original. Usando Comparator<E>, os algoritmos de ordenação são exteriores às instâncias da classe a ordenar, e portanto existe grande independência. Finalmente, Comparator<E> e Comparable<E> são também independentes. Por exemplo, poderíamos declarar que a classe Empregado implementa Comparable<E> e implementar na classe Empregado o método compareTo(Empregado emp);. Tal seria suficiente para criarmos TreeSet<Empregado> sem necessidade de usar um Comparator<Empregado> como, por exemplo, em: TreeSet<Empregado> emps = new TreeSet<Empregado>();

Dado que Empregado implementa Comparable<Empregado>, os métodos add(); e addAll(); usarão o método interno compareTo(Empregado emp); para realizar a inserção ordenada, tal como definida no método. No entanto, a qualquer momento poderemos usar também outro qualquer algoritmo de ordenação definido num Comparator<Empregado>, tal como vimos anteriormente. Assim, o algoritmo definido no método compareTo(Empregado emp); é wired, ou seja, está no código da classe, enquanto todos os outros possíveis algoritmos são externos e estão codificados em classes que implementam Comparator<Empregado>. Temos, pois, usando Comparator<E>, o máximo de flexibilidade. Finalmente, nos TreeMap<K,V>, tudo o que foi dito anteriormente aplica-se apenas ao tipo K. Se K é Comparable<K>, ou seja, implementa compareTo(K k);, então o construtor do treemap é o normal. Senão, tal como para os TreeSet<E>, devemos usar o construtor que aceita um Comparator<K> para ordenar as chaves. Por exemplo, sabendo que a classe Ponto2D não implementa Comparable<Ponto2D> e admitindo que temos uma classe Ponto2DComparator que implementa a interface Comparator<Ponto2D>, a criação de um TreeMap<Ponto2D, Cidade> mapa deverá ser realizada segundo a expressão: TreeMap<Ponto2D, Cidade) mapa = new TreeMap<Ponto2D, Cidade>( new Ponto2DComparator() );

O Exercício 6.8, que fechará este capítulo, permitirá uma extensa prática no uso de coleções, comparadores e polimorfismo. 6.8 Pretende-se desenvolver uma aplicação de Gestão de Estufas para explorações agrícolas. Cada estufa possui um código alfanumérico, uma área em m2, as temperaturas mínima e máxima de funcionamento e a temperatura atual. Devem ser considerados inicialmente três tipos de estufas: de fruta, de flores e de legumes. Para cada estufa existe um valor comercial fixo definido em euros por m2. O valor comercial total atual é a soma do valor fixo com o seu valor comercial. 238

© FCA – EDITORA DE INFORMÁTICA


COLEÇÕES DE JAVA: HashMap<K,V> E TreeMap<K,V>

6

As estufas de flores e de fruta possuem um valor comercial que corresponde ao produto do preço unitário pelo número de unidades que se espera produzir. Porém, as estufas de legumes possuem antes um preço por quilograma (kg) e um número total de quilos que são esperados. Estes preços unitários ou por kg são definidos estufa a estufa, pelo que poderemos ter, por exemplo, duas estufas com as mesmas flores mas com preços unitários diferentes. As estufas de fruta e de legumes necessitam também de possuir um atributo referente ao grau de humidade atual na estufa. O sistema de informação regista todas as medições de temperatura e humidade realizadas nas várias estufas da exploração agrícola. Um registo de estufa contém o código da estufa e a temperatura e, no caso das estufas de fruta e de legumes, também a humidade. Estes registos de estufa devem ser guardados etiquetados por um tempo, definido sob a forma hora, minuto e segundo. Para cada possível tempo existe apenas um, e um só, registo, ou seja, não há possibilidade de existirem dois registos feitos ao mesmo tempo. Este subsistema de informação designa-se por Monitor Diário de Estufas. O sistema de informação de Gestão de Estufas deverá, portanto, guardar toda a informação referente às estufas atualmente em funcionamento, permitindo consultar estas informações e inserir e remover estufas, bem como guardar e gerir toda a informação relativa ao Monitor Diário de Estufas. NOTA Antes de começarmos a codificar a aplicação pretendida, vamos realizar uma primeira análise das possíveis classes envolvidas, em função dos requisitos apresentados até este ponto.

ANÁLISE Todo o projeto será desenvolvido à volta da noção de estufa, pelo que teremos certamente de desenvolver uma classe Estufa. Existem tipos diferentes de estufas? Sim – então, teremos uma hierarquia de classes a partir da classe Estufa. Que subtipos podem ser de momento identificados? Estufa de flores, estufa de legumes e estufa de fruta – então, teremos as subclasses EstufaFlores, EstufaLegumes e EstufaFruta. Teremos de criar esta hierarquia (Figura 6.10). Na classe Estufa colocaremos tudo o que é comum e nas subclasses os atributos particulares de cada uma.

FIGURA 6.10 – HIERARQUIA DE CLASSES: Estufa E RegEstufa

Note-se que nos requisitos do projeto ainda não foram especificadas todas as funcionalidades da aplicação, e estas podem influenciar o comportamento (métodos) das várias classes. Por tal razão, podemos já raciocinar sobre a metaestrutura, mas não ainda sobre a estrutura detalhada de cada classe. © FCA – EDITORA DE INFORMÁTICA

239


PROJETOS DE POO EM JAVA Continuando a análise dos requisitos, sabemos que vamos precisar de desenvolver uma classe que represente a entidade registo de estufa – será a classe RegEstufa, que terá no entanto duas subclasses: RegEstufaNormal e RegEstufaHumidade. A primeira regista o código da estufa e a temperatura, a segunda regista também a humidade. Os requisitos apontam também para a necessidade de criação de uma classe Tempo, com os atributos hora, minuto e segundo. Esta decisão parece, à primeira vista, contraditória com a ideia de reutilização de código, sempre presente em POO, dado que Java oferece uma classe Time, exatamente com a mesma estrutura da classe Tempo a criar. Então, porque não usar a classe Time? Porque a classe Time é subclasse da classe Date, e ambas foram definidas, desde Java5, como classes deprecated. Uma classe deprecated é uma classe que, ou deixou de existir, ou deixou de ser segura, que garantidamente não vai ser mais usada em futuras API, e que, por tudo isto, é completamente desaconselhável que seja usada por um programador atento e seguro. Teríamos então duas possibilidades: usar a classe GregorianCalendar ou definir a nossa própria classe. Não se justifica usar a complexidade de um GregorianCalendar, pelo que criaremos uma classe simples chamada Tempo com três atributos inteiros. Tomada esta decisão, torna-se claro que o subsistema Monitor Diário de Estufas poderá ser representado por uma classe designada por MonitorEstufas, que implementa um TreeMap<Tempo, RegEstufa> no qual se efetua um registo de estufa associado a um dado instante. Na sua versão mínima, esta classe deverá implementar os métodos usuais e, pelo menos, o método que permite inserir um novo registo. Finalmente, teremos de desenvolver uma classe GereEstufas que representará a grande estrutura de informação do projeto e respetiva funcionalidade. Do ponto de vista da sua estrutura interna, de momento temos a certeza de que esta classe deverá conter a tabela com a informação de todas as estufas e um MonitorEstufas. A sua estrutura será, portanto, a seguinte: public GereEstufas { private RegistoEstufas regEstufas; private TreeMap<String, Estufa> infoEstufas; ……… }

Porém, parece interessante que se possa considerar a implementação alternativa public GereEstufas { private TreeMap<Tempo, RegEstufa> regEstufas; private TreeMap<String, Estufa> infoEstufas; ……… }

na qual dispensamos o encapsulamento dado pela criação da classe RegistoEstufas e usamos diretamente o TreeMap<Tempo, RegEstufa>. Analisaremos esta questão depois de vermos quais as funcionalidades requeridas para a aplicação. O sistema de Gestão de Estufas, representado pela classe GereEstufas, deverá ser capaz de realizar as seguintes operações:

240

■■

Inserir um registo de estufa registado num dado tempo;

■■

Inserir a informação referente a uma nova estufa;

■■

Determinar o conjunto dos registos realizados numa dada hora;

© FCA – EDITORA DE INFORMÁTICA


COLEÇÕES DE JAVA: HashMap<K,V> E TreeMap<K,V>

6

■■

Determinar se, para uma dada hora, todas as estufas possuem registos;

■■

Dado um registo de estufa, verificar se a temperatura registada está entre a mínima e a máxima (ou seja, se não há risco por temperatura);

■■

Criar uma listagem com os códigos de todas as estufas com registos que indicam risco, ou seja, com uma temperatura fora dos limites;

■■

O mesmo que a operação anterior, mas para uma dada hora;

■■

O mesmo que a operação anterior, mas para uma dada hora e para um dado tipo de estufa;

■■

Determinar o total de perdas atuais tendo em conta todas as estufas que estão em risco de temperatura;

■■

O mesmo que a operação anterior, mas para um tipo de estufas dado;

■■

Selecionar, de todos os registos realizados, o conjunto dos registos de humidade;

■■

Determinar o conjunto dos registos de humidade numa dada hora;

■■

Determinar o primeiro registo de estufa do dia;

■■

Adicionar à tabela de estufas um novo conjunto de estufas;

■■

Guardar a atual tabela de registos num ficheiro de texto.

public abstract class Estufa { // Variáveis e Métodos de Classe private static double VALOR_BASE_AREA = 9.0; public static void set_VALOR_BASE_AREA(double valArea) { VALOR_BASE_AREA = valArea; } public static double get_VALOR_BASE_AREA() { return VALOR_BASE_AREA; } // Variáveis e Métodos de Instância private String codigo; private double tempMin, tempMax; private double area; public Estufa(String cod, double tMin, double tMax, double areaEst) { codigo = cod; tempMin = tMin; tempMax = tMax; area = areaEst; } public Estufa(Estufa estf) { codigo = estf.getCodigo(); tempMin = estf.getTempMin(); tempMax = estf.getTempMax(); area = estf.getArea(); } public String getCodigo() { return codigo; } public double getTempMin() { return tempMin; } public double getTempMax() { return tempMax; } public double getArea() { return area; } public double getValorBase() { return VALOR_BASE_AREA * area; } © FCA – EDITORA DE INFORMÁTICA

241


9cm x 24cm

16,7cm x 24cm

18mm

16,7cm x 24cm

9cm x 24cm

F. Mário Martins

C

M

Y

Este livro, com múltiplos exemplos práticos, apresenta as bases e os conceitos que permitem compreender e aplicar as várias fases do desenvolvimento iterativo de uma boa interface utilizador.

.. .. .

Classes e Instâncias; Encapsulamento, Modularidade e Reutilização; Todas as Coleções de Java (JCF); Hierarquia de Classes, Classes Abstratas, Herança, Interfaces e Polimorfismo; Streams de I/O.

CM

MY

CY

CMY

K

Dirigido aos estudantes de programação; aprofunda o estudo dos vários tipos de algoritmos expondo a análise experimental e formal da sua complexidade. Pseudocódigo compatível com C e Java.

Em cada capítulo apresenta-se uma síntese teórica dos assuntos abordados, as construções de Java necessárias ao projeto exemplo e, em seguida, vários exercícios que são analisados e completamente implementados em Java. Este livro tem como principais destinatários estudantes de nível secundário e universitário e profissionais de informática em geral.

Pela sua estrutura, pode ser usado quer como livro de base para as aulas práticas de laboratório, quer como livro de texto no caso de disciplinas com uma abordagem de ensino essencialmente prática. Principais temas abordados no livro:

. . . . . . .

Tipos Primitivos e Estruturas de Controlo; Arrays; Classes; Coleções de Java: ArrayList<E>, HashSet<E> e TreeSet<E>; Hierarquia de Classes, Herança, Classes Abstratas e Polimorfismo; Coleções de Java: HashMap<K,V> e TreeMap<K,V>; Streams de I/O.

Este livro disponibiliza ainda a correspondência dos principais termos técnicos para o português do Brasil. Livro indispensável na criação e desenvolvimento de jogos digitais: programação de jogos para web, dispositivos móveis e Windows 8. Com Python, Pygame, HTML5 e outros. Para estudantes e profissionais.

Código fonte de todos os exercícios e projetos do livro disponível em www.fca.pt, até este se esgotar ou ser publicada nova edição atualizada ou com alterações.

ISBN 978‐972‐722‐792‐1

9 789727 227921

Projetos de POO em JAVA

Este livro tem por objetivo, com base em princípios fundamentais da Engenharia de Software, apresentar regras e boas práticas na análise, conceção e desenvolvimento (projeto) de aplicações orientadas pelos objetos em geral, neste caso usando as construções da linguagem Java. O livro centra-se à volta da noção de projeto de software, sendo apresentados projetos sobre todos os assuntos essenciais à Programação Orientada pelos Objetos (POO), designadamente (além dos básicos):

Professor Associado do Departamento de Informática da Universidade do Minho. É responsável por disciplinas de licenciatura e de mestrado nas áreas de Paradigmas e Metodologias da Programação, Metodologias e Tecnologias de Objetos e Arquiteturas de Software. Os Modelos de Componentes e as Arquiteturas de Software Multicamada e Multifuncionais são as suas atuais áreas de interesse. Autor dos livros JAVA6 e Programação Orientada pelos Objectos, JAVA5 e Programação por Objectos e Programação Orientada aos Objectos em JAVA2, também publicados pela FCA.


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.