Prévia do material em texto
PROGRAMAÇÃO III PROFESSORES Dr. Edson Alves de Oliveira Junior Me. Andre Abdala Noel Me. Márcia Cristina Dadalto Pascutti Me. Rafael Alves Florindo Esp. Janaina Aparecida de Freitas Esp. Victor de Marqui Pedroso EXPEDIENTE C397 CENTRO UNIVERSITÁRIO DE MARINGÁ. Núcleo de Educação a Distância. NOEL, Andre Abdala; OLIVEIRA JR, Edson Alves de; FREITAS, Janaina Aparecida de; PASCUTTI, Márcia Cris- tina Dadalto; FLORINDO, Rafael Alves; PEDROSO, Victor de Marqui. Programação III. Andre Abdala Noel, Edson Alves de Oliveira Junior, Janaina Apa- recida de Freitas, Márcia Cristina Dadalto Pascutti, Rafael Alves Florindo e Victor de Marqui Pedroso. Maringá - PR.: UniCesumar, 2020. 216 p. “Graduação - EaD”. 1. Programação 2. Software 3. Sistema. EaD. I. Título. FICHA CATALOGRÁFICA NEAD - Núcleo de Educação a Distância Av. Guedner, 1610, Bloco 4 Jd. Aclimação - Cep 87050-900 | Maringá - Paraná www.unicesumar.edu.br | 0800 600 6360 Coordenador(a) de Conteúdo Danillo Xavier Saes Projeto Gráfico e Capa Arthur Cantareli, Jhonny Coelho e Thayla Guimarães Editoração Sabrina Maria Pereira de Novaes Design Educacional Rossana Costa Giani Revisão Textual Ariane Andrade Fabreti Ilustração Marta Sayuri Kakitani Fotos Shutterstock CDD - 22 ed. 005.1 CIP - NBR 12899 - AACR/2 ISBN 978-85-459-1712-0 Impresso por: Bibliotecário: João Vivaldo de Souza CRB- 9-1679 Diretoria Executiva Chrystiano Mincoff, James Prestes, Tiago Stachon Diretoria de Design Educacional Débora Leite Diretoria de Graduação Kátia Coelho Diretoria de Permanência Leonardo Spaine Diretoria de Pós-graduação, Extensão e Formação Acadêmica Bruno Jorge Head de Produção de Conteúdos Celso Luiz Braga de Souza Filho Gerência de Produção de Conteúdo Diogo Ribeiro Garcia Gerência de Projetos Especiais Daniel Fuverki Hey Supervisão do Núcleo de Produção de Materiais Nádila Toledo Supervisão de Projetos Especiais Yasminn Zagonel NEAD - NÚCLEO DE EDUCAÇÃO A DISTÂNCIA Reitor Wilson de Matos Silva Vice-Reitor Wilson de Matos Silva Filho Pró-Reitor de Administração Wilson de Matos Silva Filho Pró-Reitor Executivo de EAD William Victor Kendrick de Matos Silva Pró-Reitor de Ensino de EAD Janes Fidélis Tomelin Presidente da Mantenedora Cláudio Ferdinandi DIREÇÃO UNICESUMAR BOAS-VINDAS Neste mundo globalizado e dinâmico, nós tra- balhamos com princípios éticos e profissiona- lismo, não somente para oferecer educação de qualidade, como, acima de tudo, gerar a con- versão integral das pessoas ao conhecimento. Baseamo-nos em 4 pilares: intelectual, profis- sional, emocional e espiritual. Assim, iniciamos a Unicesumar em 1990, com dois cursos de graduação e 180 alunos. Hoje, temos mais de 100 mil estudantes espalhados em todo o Brasil, nos quatro campi presenciais (Maringá, Londrina, Curitiba e Ponta Grossa) e em mais de 500 polos de educação a distância espalhados por todos os estados do Brasil e, também, no exterior, com dezenasde cursos de graduação e pós-graduação. Por ano, pro- duzimos e revisamos 500 livros e distribuímos mais de 500 mil exemplares. Somos reconhe- cidos pelo MEC como uma instituição de exce- lência, com IGC 4 por sete anos consecutivos e estamos entre os 10 maiores grupos educa- cionais do Brasil. A rapidez do mundo moderno exige dos edu- cadores soluções inteligentes para as neces- sidades de todos. Para continuar relevante, a instituição de educação precisa ter, pelo menos, três virtudes: inovação, coragem e compromis- so com a qualidade. Por isso, desenvolvemos, para os cursos de Engenharia, metodologias ati- vas, as quais visam reunir o melhor do ensino presencial e a distância. Reitor Wilson de Matos Silva Tudo isso para honrarmos a nossa mis- são, que é promover a educação de qua- lidade nas diferentes áreas do conheci- mento, formando profissionais cidadãos que contribuam para o desenvolvimento de uma sociedade justa e solidária. P R O F I S S I O N A LT R A J E T Ó R I A Dr. Edson Alves de Oliveira Junior Pós-Doutorando em Experimentação em Computação Forense na PUC-RS. Possui doutorado em Ciências de Computação e Matemática Computacional pelo Instituto de Ciências Matemáticas e de Computação da Universidade de São Paulo (ICMC- -USP). Possui mestrado e graduação em Ciência da Computação pela Universidade Estadual de Maringá (UEM). Professor adjunto do Departamento de Informática (DIN) da Universidade Estadual de Maringá (UEM). Possui experiência na área de Ciência da Computação, com ênfase em Engenharia de Software, atuando, principal- mente, nos seguintes temas: Processos de Software, Linha de Produto de Software, Avaliação de Arquitetura de Software e de Linhas de Produto, Linha de Processo de Software, Gerenciamento de Variabilidades, Métricas e Modelos de Software, Frameworks, Modelagem e Metamodelagem UML, Ambientes de Desenvolvimento e Tecnologias Java. http://lattes.cnpq.br/8717980588591239. Me. Márcia Cristina Dadalto Pascutti Possui mestrado em Ciência da Computação pela Universidade Federal do Rio Gran- de do Sul (2002) e graduação em Processamento de Dados pela Universidade Es- tadual de Maringá (1989). Atualmente, é professora no Instituto Federal do Paraná - Campus Umuarama, e está cursando o doutorado na PUC-PR. Atua, principalmente, nos seguintes temas: arquitetura de computadores, engenharia de software, proces- samento de imagens, reconhecimento de padrões, visão computacional. http://lattes.cnpq.br/0297217008123332 Me. Andre Abdala Noel Mestre em Ciência da Computação pela Universidade Estadual de Maringá, com ênfase em sistemas de computação e bacharel em Ciência da Computação pela Universidade Estadual de Maringá. É professor e programador. Possui boa experiência em programação, aplicando, também, na docência superior, desde 2008. Autor do site Vida de Programador, mantém-se ativo na comunidade de desenvolvedores. http://lattes.cnpq.br/9035823171388697 P R O F I S S I O N A LT R A J E T Ó R I A Esp. Janaina Aparecida de Freitas Possui graduação em Informática pela Universidade Estadual de Maringá (2010) e especialização em MBA em Teste de Software pela UNICEUMA (2012). Atualmente, cursa o Programa de Mestrado em Ciência da Computação na Universidade Estadual de Maringá (UEM) e é graduanda de Letras – Português/Inglês na Unicesumar. Atua como professora mediadora, professora conteudista em gravação de aulas ao vivo e gravação de aulas conceituais nos cursos do NEAD – Núcleo de Educação a Distância – da Unicesumar, para os cursos de graduação de Sistemas para Internet, Análise e Desenvolvimento de Sistemas, Gestão da Tecnologia da Informação e Engenharia de Software, nas disciplinas de Engenharia de Software, Design Gráfico, Tópicos Especiais, Gerenciamento de Software, Design de Interação Humano-Computador, Projeto Implementação e Teste de Software. Além disso, tem experiência em inicia- tiva privada na área de Análise de Sistemas e Testes de Software. http://lattes.cnpq.br/4906244382612830 Esp. Victor de Marqui Pedroso Possui Pós-Graduação em Banco de dados Oracle e DB2 pelo Centro Universitário de Maringá (2009) e graduação em Tecnologia em Processamento de Dados pelo Centro Universitário de Maringá (2003). Tem experiência como analista de sistemas, documentador, homologador e programador de software. Possui experiência em desenvolvimento utilizando a ferramenta Delphi. Já trabalhou como Professor Me- diador e, atualmente, trabalha como Professor Formador dos cursos de Análise e Desenvolvimento de Sistemas e Sistemas para Internet, ministrando as disciplinas de Banco de Dados e Design de Interação. http://lattes.cnpq.br/8611697826182780 Me. Rafael Alves Florindo Mestrado em Gestão do Conhecimento nas Organizações na linha de pesquisa Edu- cação e Conhecimento pela PPGGCO – Unicesumar – Centro Universitário de Maringá (2017). É especialista em Desenvolvimento de Sistema para Web pela UEM – Universi- dade Estadual de Maringá (2008). Graduado em Ciênciada Computação pela FAI – Fa- culdades Adamantinenses Integrada. Pós-graduando em Docência no Ensino Superior: Tecnologias Educacionais e Inovação e Banco de Dados pela Unicesumar. Professor – Ensino Técnico pela SEED-PR desde 2009. Professor autor, formador e mediador do EAD - Ensino a Distância da Unicesumar desde 2014, nos Cursos de TI: Sistemas para Internet, Análise e Desenvolvimento de Sistemas, Gestão da Tecnologia da Informação e Engenharia de Software. Professor presencial da Faculdade Unifamma desde 2019, no curso de Engenharia de Software e Sistema de Informação. http://lattes.cnpq.br/7246554526271622 D A D I S C I P L I N AA P R E S E N TA Ç Ã O PROGRAMAÇÃO III Seja bem-vindo(a)! Prezado(a) acadêmico(a), é com muito prazer que lhe apresentamos o livro de Programação III. Este livro está organizado em cinco unidades e todas estão, estreitamente, relacionadas. Na Unidade 1, apresentaremos alguns conceitos referentes à disciplina. Você notará, durante a leitura das outras unidades, que esses conceitos são utilizados com frequência. A engenharia de software surgiu mediante a necessidade de tornar o desenvolvimento de software confiável, com etapas bem definidas, custo e cronograma previsíveis, fatos que não aconteciam até 1968, quando o termo engenharia de software foi proposto. Além disso, gosta- ríamos de ressaltar que o software compreende, além dos programas, toda a documentação referente a ele, e a engenharia de software é a disciplina que trata dessa documentação. Na Unidade 2, daremos início à aplicação dos conhecimentos da engenharia de software na linguagem de programação Java. Abordaremos, primeiramente, os conceitos relacionados a modificadores de acesso e a encapsulamento. Esses recursos formam um dos pilares da programação orientada a objetos, recurso este que garante a integridade e o controle de acesso aos atributos e métodos. Continuando a aplicação dos conceitos de engenharia de software, na Unidade 3, abarcaremos mais dois pilares da programação orientada a objetos: a herança e o polimorfismo. Ambos estão relacionados, uma vez que, a partir do recurso da herança, ou seja, quando o filho herda A P R E S E N TA Ç Ã O D A D I S C I P L I N A características do pai, é possível especializar alguma operação. Nesse caso, temos que o filho pode implementar uma ação que o pai desenvolve, contudo ela pode ser diferente ou igual. Na Unidade 4, abordaremos algumas regras de desenvolvimento. A primeira será as classes abstratas, que funcionarão como modelo de desenvolvimento para outras classes que estão recebendo herança dela, ou seja, a classe que herda de uma classe abstrata deve implementar os métodos que estão marcados para serem implementados nas classes filhas. Outra regra é a interface, esta rege um contrato entre analista e desenvolvedor; aqui, quando construímos uma interface e uma classe a implementa, ela é obrigada a implementar todos os métodos, funcionando, assim, como um contrato. Finalmente, chegamos à última unidade do nosso material. Esta unidade é o fechamento das etapas do processo de software, onde realizaremos a implementação de um estudo de caso por meio da realização da nova análise desse estudo, agora, na visão do desenvolvedor. Assim, chegamos ao final do nosso livro. Esperamos, sinceramente, que a sua leitura seja agradável e que este conteúdo possa contribuir para o seu crescimento pessoal, acadêmico e profissional. ÍCONES Sabe aquela palavra ou aquele termo que você não conhece? Este ele- mento ajudará você a conceituá-la(o) melhor da maneira mais simples. conceituando No fim da unidade, o tema em estudo aparecerá de forma resumida para ajudar você a fixar e a memorizar melhor os conceitos aprendidos. quadro-resumo Neste elemento, você fará uma pausa para conhecer um pouco mais sobre o assunto em estudo e aprenderá novos conceitos. explorando Ideias Ao longo do livro, você será convidado(a) a refletir, questionar e transformar. Aproveite este momento! pensando juntos Enquanto estuda, você encontrará conteúdos relevantes online e aprenderá de maneira interativa usando a tecno- logia a seu favor. conecte-se Quando identificar o ícone de QR-CODE, utilize o aplicativo Unicesumar Experience para ter acesso aos conteúdos online. O download do aplicativo está disponível nas plataformas: Google Play App Store CONTEÚDO PROGRAMÁTICO UNIDADE 01 UNIDADE 02 UNIDADE 03 UNIDADE 05 UNIDADE 04 FECHAMENTO MODELAGEM DE SISTEMAS 10 MODIFICADORES JAVA E ENCAPSULAMENTO 45 78 HERANÇA E POLIMORFISMO 115 CLASSES ABSTRATAS E INTERFACES 150 ESTUDO DE CASO: ANÁLISE CLÍNICA 202 CONCLUSÃO GERAL 1 MODELAGEM DE SISTEMAS PLANO DE ESTUDO A seguir, apresentam-se as aulas que você estudará nesta unidade: • Modelagem de sistemas • Ferra- mentas CASE (Computer-Aided Software Engineering) • Introdução à UML (Unified Modeling Langua- ge) • Conceitos básicos de orientação a objetos • Diagrama de classes. OBJETIVOS DE APRENDIZAGEM • Expor a importância da modelagem de sistemas • Entender as Ferramentas CASE (Computer-Aided Software Engineering) • Entender a UML (Unified Modeling Language ou Linguagem de Modelagem Unificada) • Apresentar os conceitos básicos de orientação a objetos • Entender a estrutura do sistema por meio de elementos, tais como classes, atributos e associações entre os objetos. PROFESSORES Dr. Edson Alves de Oliveira Junior Me. Andre Abdala Noel Me. Márcia Cristina Dadalto Pascutti Me. Rafael Alves Florindo Esp. Janaina Aparecida de Freitas Esp. Victor de Marqui Pedroso INTRODUÇÃO Caro(a) aluno(a), nesta primeira unidade, você verá uma revisão dos conceitos de modelagem de sistema. Este é o processo de elaboração de modelos abstratos de um sistema, normalmente, representado por meio de um diagrama, em que cada um desses modelos apresenta uma visão ou uma perspectiva diferente do sistema (SOMMERVILLE, 2011). Esses modelos, normalmente, são elaborados utilizando uma notação gráfica, que, em nosso caso, será a UML(Unified Modeling Language ou Lingua- gem de Modelagem Unificada). A UML define, em sua versão atual, 21 tipos de diagramas para o uso na modelagem de software. Nesta unidade, veremos, somente, o diagrama de classe. Se você quiser conhecer os outros, consulte alguns livros relacio- nados nas referências. Como nosso objetivo, aqui, é mostrar a modelagem de um sistema, utilizaremos, somente, esse diagrama. Primeiramente, exporemos a importância de realizar a modelagem de um sistema de software. Esta se baseia na utilização de notações gráficas e textuais com o objetivo de construir modelos que representem as partes essenciais de um sistema. Depois, passaremos a entender as Ferramentas CASE (Computer-aided Software Engineering) e UML (Unified Modeling Language ou Linguagem de Modelagem Unificada). Todos os artefatos de software produzidos durante a modelagem serão usados como base para as fases seguintes do ciclo de desenvolvimento de sistemas, como as fases de projeto e de implementação. Em seguida, apresentaremos a você os conceitos básicos de Orientação a Objetos (OO) para entender, por meio de elementos, a estrutura do sistema. Por último, mostraremos o diagrama de classes. Este é o mais importante e, também, o mais utilizado da UML, além de servir de apoio para a maioria dos diagramas. O diagrama de classes define a estrutura das classes identi- ficadas para o sistema, mostrando os atributos e os métodos de cada uma, além de estabelecer como elas se relacionam e trocam informações entre si. U N ID A D E 1 12 1 MODELAGEM DE SISTEMAS Caro(a) aluno(a), a necessidade de planejamento no desenvolvimento de sistemas de informação leva ao conceito de modelagem de software, ou seja, antes de o software ser concebido, deve-se criar um modelo para ele. Um modelo pode ser entendido como uma representação idealizada de um sistema a ser construído. São exemplos: maquetesde edifício, plantas de casa, fluxogramas etc. A modelagem de sistemas de software se baseia na utilização de notações gráficas e textuais com o objetivo de construir modelos que representem as partes essenciais de um sistema. São várias as razões para utilizar modelos na construção de sistemas: 1. No desenvolvimento do software, usamos desenhos gráficos denomina- dos diagramas, a fim de representar o comportamento do sistema. Esses diagramas seguem um padrão lógico e possuem uma série de elementos gráficos que carregam um significado pré-definido. 2. Apesar de um diagrama expressar diversas informações de forma gráfica, em diversos momentos, há a necessidade de adicionar informações em forma de texto, com o objetivo de explicar ou definir certas partes. A modelagem de um sistema em forma de diagrama, em conjunto com a informação textual, forma a documentação de um sistema de software. 3. O rápido crescimento da capacidade computacional das máquinas re- sultou na demanda de sistemas de software cada vez mais complexos, que tirassem proveito de tal capacidade. Por sua vez, o surgimento desses U N IC E S U M A R 13 sistemas gerou a necessidade de reavaliação no desenvolvimento de siste- mas. Desde o aparecimento do primeiro computador até os dias de hoje, as técnicas para a construção de sistemas computacionais têm evoluído para suprir as necessidades do desenvolvimento de software. A seguir, a Figura 1 ilustra um parâmetro histórico da evolução digital: O processo de desenvolvimento de software é uma atividade bastante complexa. Isto se reflete no alto número de projetos de software que não chegam ao fim ou que Os sistemas de software eram bastante simples e, dessa forma, as técnicas de modelagem também. Era a época dos fluxogramas e diagramas de módulos. Nesta época, houve a expansão do mercado computacional. Sistemas complexos começavam a surgir e, por consequência, modelos mais robustos foram propostos. Além disso, surge a programação estruturada e, no final da década, a análise e o projeto estruturado. Surge a necessidade de interfaces homem-máquina mais sofisticadas, o que originou a produção de sistemas de software mais complexos. A análise estruturada se conso- lidou na primeira metade da década, e, em 1989, Edward Yourdon lançou o livro Análise Estrutura- da Moderna, tornando-se referência no assunto. Inicia-se um novo paradigma de modelagem, a análise orientada a objetos, como resposta para as dificuldades encontradas na aplicação da análise estruturada em certos domínios de aplicação. O paradigma da orientação a objetos atinge a sua maturidade. Os conceitos de padrões de projetos (design patterns), frameworks de desenvolvimen- to, componentes e padrões de qualidade começam a ganhar espaço. Surge, também, a Linguagem de Modelagem Unificada (UML), que é a ferramenta de modelagem utilizada no desenvolvimento atual de sistemas. 1950/60 Era a época dos fluxogramas e diagramas de módulos 1970 Expansão do mercado tecnológico 1980 Sistema da interface de usuário 1990 Novo paradigma de modelagem de sistemas de software 1990 e atualidade Maturidade Digital Figura 1 - Parâmetros históricos da evolução dos modelos na construção de sistemas. Fonte: os autores. U N ID A D E 1 14 Modelagem de sistema é o processo de desenvolvimento de modelos abstratos de um sistema, em que cada modelo apresenta uma visão ou perspectiva diferente desse. A modelagem, geralmente, representa o sistema com algum tipo de notação gráfica que, atualmente, quase sempre, é baseada em notações de UML (linguagem de modelagem unificada, do inglês Unified Modeling Language). Os modelos são usados durante o pro- cesso de engenharia de requisitos para ajudar a extrair os requisitos do sistema; duran- te o processo de projeto, são usados para descrever o sistema aos engenheiros que o implementam; e, após isso, são usados para documentar a estrutura e a operação do sistema. Um modelo é uma abstração do sistema a ser estudado, e não uma representa- ção alternativa dele. Idealmente, uma representação deve manter todas as informações sobre a entidade representada. Uma abstração, deliberadamente, simplifica e seleciona as características mais salientes. Fonte: adaptado de Sommerville (2011, p. 96). explorando Ideias extrapolam recursos de tempo e de dinheiro alocados. Em um estudo clássico sobre projetos de desenvolvimento de software, realizado em 1994, constatou-se que: ■ Porcentagem de projetos que terminam dentro do prazo estimado: 10%. ■ Porcentagem de projetos que são descontinuados antes de chegarem ao fim: 25%. ■ Porcentagem de projetos acima do custo esperado: 60%. ■ Atraso médio nos projetos: um ano. Os modelos construídos na fase de análise devem ser, cuidadosamente, validados e verificados. O objetivo da validação é o de assegurar que as necessidades do cliente es- tão sendo atendidas. Nessa atividade, os analistas apresentam os modelos criados para representar o sistema aos futuros usuários a fim de que esses modelos sejam validados. Já a verificação objetiva analisar se os modelos construídos estão em conformi- dade com os requisitos definidos. Na verificação dos modelos, é analisada a exatidão de cada um, separadamente, bem como a consistência entre eles. A verificação é uma etapa típica da fase de projeto e é a próxima etapa do desenvolvimento de software. Caro(a) aluno(a), utilizaremos a UML para realizar a modelagem de sistemas. Nesse primeiro tópico, estudamos alguns conceitos relacionados à orientação de objetos e fizemos uma introdução à linguagem UML. Lembre-se de que os artefatos de software produzidos durante a modelagem servirão de base para a fase seguinte do ciclo de desenvolvimento de sistemas, ou seja, a fase de projeto. U N IC E S U M A R 15 2 FERRAMENTAS CASE (Computer-Aided Software Engineering) Uma ferramenta CASE é um software que pode ser utilizado para apoiar as ati- vidades do processo de software, como a engenharia de requisitos, o projeto, o desenvolvimento de programa e os testes. As ferramentas CASE podem incluir editores de projeto, dicionários de dados, compiladores, depuradores, ferramentas de construção de sistemas, entre outros (SOMMERVILLE, 2011). Sommerville (2011) expõe alguns exemplos de atividades que podem ser automatizadas utilizando a CASE. Dentre elas, estão: 1. O desenvolvimento de modelos gráficos de sistemas enquanto parte das especificações de requisitos ou do projeto de software. 2. A compreensão de um projeto utilizando um dicionário de dados que carrega informações sobre as entidades e a sua relação em um projeto. 3. A geração de interfaces com usuários a partir de uma descrição gráfica da interface, que é criada interativamente pelo usuário. 4. A depuração de programas pelo fornecimento de informações sobre um programa em execução. 5. A tradução automatizada de programas a partir da antiga versão de uma linguagem de programação, como a Cobol, para uma versão mais recente. A tecnologia CASE está, atualmente, disponível para a maioria das atividades de rotina no processo de software, proporcionando, assim, algumas melhorias na qualidade e na produtividade de software. U N ID A D E 1 16 Existem, basicamente, duas formas de classificação geral para as ferramentas CASE. A primeira delas divide-se em: Upper CASE, Lower CASE, Integrated- -CASE e Best in Class: ■ Upper CASE ou U-CASE ou Front-End: são as ferramentas voltadas para as primeiras fases do processo de desenvolvimento de sistemas, como a análise de requisitos, o projeto lógico e a documentação. São uti- lizadas por analistas e pessoas mais interessadas na solução do problema do que na implementação. ■ Lower CASE ou L-CASE ou Back-End: são, praticamente, o oposto das ferramentas Upper CASE. Elas oferecem suporte nas últimas fases do desenvolvimento, como a codificação, os testes e a manutenção.■ Integrated CASE ou I-CASE: são ferramentas que, além de englobarem características das Upper e Lower CASEs, oferecem, ainda, outras carac- terísticas, como controle de versão, por exemplo. Entretanto são utiliza- das somente em projetos de desenvolvimento muito extensos, por serem bastante caras e difíceis de operar. ■ Best in Class ou Kit de Ferramenta: semelhante às I-CASEs, os Kits de Fer- ramenta, também, acompanham todo o ciclo de desenvolvimento. Entretanto possuem a propriedade de conjugar a sua capacidade a outras ferramentas externas complementares, as quais variam de acordo com as necessidades do usuário. Para melhor entendimento, podemos compará-las a um computador sem o kit multimídia: as Best in Class seriam o computador que funciona nor- malmente, enquanto as outras ferramentas fariam o papel do kit multimídia, promovendo mais potencial de trabalho para a máquina. São mais usadas para o desenvolvimento corporativo, apresentando controle de acesso, versão e repositórios de dados, entre outras características. A segunda forma divide as ferramentas CASE em orientadas à função e em orien- tadas à atividade: ■ Orientadas à função: seriam as Upper CASE e Lower CASE. Baseiam- -se na funcionalidade das ferramentas, ou seja, são as que têm funções diferentes no ciclo de vida de um projeto. Por exemplo, a representação, apenas, do Diagrama de Entidades e Relacionamentos (DER) ou do Dia- grama de Fluxo de Dados (DFD). ■ Orientadas à atividade: seriam as Best in Class e as I-CASE, as quais pro- cessam atividades, tais como especificações, modelagem e implementação. U N IC E S U M A R 17 Além dos ambientes completos de engenharia de software, ferramentas de solução pon- tual que resolvem tudo, desde reunião de requisitos até refatoração de projeto/código e teste, continuarão a evoluir e se tornarão mais capazes em funcionalidade. (Roger Pressman e Bruce Maxim) pensando juntos 3 INTRODUÇÃO À UML (Unified Modeling Language) Segundo Booch, Rumbaugh e Jacobson (2006, p. 13), “a UML (Unified Modeling Language ou Linguagem de Modelagem Unificada) é uma linguagem-padrão para a elaboração da estrutura de projetos de software”, podendo ser utilizada para visualização, especificação, construção e documentação de artefatos de soft- ware, por meio do paradigma de orientação a objetos. Além disso, a UML é a linguagem-padrão de modelagem de software adotada, internacionalmente, pela indústria de engenharia de software (GUEDES, 2007). A UML não é uma linguagem de programação, mas uma linguagem de modelagem cuja meta é auxiliar os engenheiros de software a definirem as características do softwa- re, tais como: os seus requisitos, o seu comportamento, a sua estrutura lógica, a dinâmica de seus processos e, até mesmo, as suas necessidades físicas em relação ao equipamento em que o sistema deverá ser implantado. Todas essas características são definidas por meio da UML antes do início do desenvolvimento do software (GUEDES, 2007). U N ID A D E 1 18 De acordo com Booch, Rumbaugh e Jacobson (2006), a UML é apenas uma linguagem de modelagem e, também, é independente de processo de software, visto que pode ser utilizada em modelo cascata, em desenvolvimento evolucioná- rio ou em qualquer outro processo utilizado para o desenvolvimento do software. A notação UML utiliza diversos símbolos gráficos, existindo uma se- mântica bem definida para cada um deles, o que permite elaborar diversos modelos. Além disso, a UML tem sido empregada, de maneira efetiva, em sistemas cujos domínios abrangem os sistemas de informações corporativos, os serviços bancários e os financeiros, os transportes, os serviços distribuídos baseados na web, entre outros. No entanto ela não se limita à modelagem de software, pois pode modelar sistemas, tais como o fluxo de trabalho no sis- tema legal, a estrutura e o comportamento de sistemas de saúde e o projeto de hardware (BOOCH; RUMBAUGH; JACOBSON, 2006). Ferramentas Case baseadas na Linguagem UML Nesta unidade, já constatamos que as ferramentas CASE (Computer-Aided Soft- ware Engineering – Engenharia de Software Auxiliada por Computador, em por- tuguês) são softwares que, de alguma forma, colaboram na realização de uma ou mais atividades realizadas durante o processo de desenvolvimento de software. Agora, veremos alguns exemplos de ferramentas CASE que suportam a UML, a qual é, em geral, a sua principal característica. Existem várias delas no mercado, dentre as quais podemos destacar: ■ Rational Rose: esta ferramenta foi desenvolvida pela Rational Software Corporation, empresa que estimulou a criação da UML, sendo a primeira ferramenta CASE baseada nessa linguagem. Atualmente, a Rational Rose é bastante usada pelas empresas desenvolvedoras de software. Ela permite a modelagem dos diversos diagramas da UML e a construção de modelos de dados com a possibilidade de exportação para a construção da base de dados ou a realização de engenharia reversa de uma base de dados existente. Em 20 de fevereiro de 2003, a empresa Rational foi adquirida pela IBM, e a ferramenta foi renomeada como IBM Rational Architect. U N IC E S U M A R 19 ■ Astah Professional: é uma ferramenta para a criação de diagramas UML e possui uma versão gratuita, o Astah Community, bem como outras ver- sões pagas. A versão gratuita possui algumas restrições de funções, porém, para as que necessitaremos nesta unidade, ela será suficiente. Portanto, será essa a ferramenta que utilizaremos para modelar os diagramas da UML que aprenderemos. Anteriormente, ela era conhecida como Jude. ■ Visual Paradigm for UML ou VP-UML: esta ferramenta oferece uma versão que pode ser baixada gratuitamente, a Community Edition, porém ela não suporta todos os serviços e opções disponíveis nas versões stan- dard ou professionais da ferramenta. Para quem deseja praticar a UML, a versão gratuita é uma boa alternativa, apresentando um ambiente ami- gável e de fácil compreensão. ■ Enterprise Architect: esta ferramenta não possui edição gratui- ta como as anteriores, mas é uma das que mais oferecem recursos compatíveis com a UML 2. O importante, em nossos estudos, é que você consiga entender como se inicia a modelagem do seu sistema. Para tanto, queremos que a informação não fique, apenas, anotada em um documento, mas que você facilite o processo de desen- volvimento utilizando os recursos de modelagem e de construção de diagramas. U N ID A D E 1 20 A UML é, totalmente, baseada no paradigma de Orientação a Objetos (OO). Sendo assim, para compreendê-la corretamente, precisamos, antes, estudar alguns dos conceitos relacionados à orientação a objetos. Objetos Segundo Melo (2004), um objeto é qualquer coisa, em forma concreta ou abstrata, que exista física ou apenas conceitualmente no mundo real. São exemplos de ob- jetos: cliente, professor, carteira, caneta, carro, disciplina, curso, caixa de diálogo. Além disso, eles possuem características e comportamentos. Classes Uma classe é uma abstração de um conjunto de objetos que possuem os mesmos tipos de características e comportamentos, sendo representada por um retân- gulo que possui três divisões. A primeira divisão armazena o nome da classe; a segunda, os atributos (características); enquanto a terceira carrega os métodos. Geralmente, uma classe possui atributos e métodos, mas é possível encontrar aquelas com apenas uma dessas características ou, até mesmo, nenhuma, quando se trata de classes abstratas. A Figura 2 apresenta um exemplo de classe: 4 CONCEITOS BÁSICOS DE ORIENTAÇÃO a Objetos U N IC E S U M A R 21 De acordo com Melo (2004), o momento inicial para a identificação de classes, em um documento de re- quisitos, é assinalar os substantivos. Note que esses substantivos podem levar a objetos físicos (cliente, livro, computador) ou a objetos conceituais (reserva, cronograma, norma).Em seguida, é preciso identifi- car, somente, aqueles que possuem importância para o sistema, verificando o que, realmente, consiste em objeto e o que consiste em atributo. Ainda, é preciso fazer nova análise dos requisitos para identificar as classes implícitas no contexto trabalhado, excluir classes parecidas ou juntar duas classes em uma. Vale a pena ressaltar que esse processo será iterativo, sem a possibilidade de definir todas as classes de uma só vez. Atributos Uma classe, normalmente, possui atributos que representam as suas característi- cas, as quais costumam variar de um objeto a outro, como o nome de um objeto da classe cliente ou a cor em um objeto da classe carro, por exemplo. Tais aspectos são os responsáveis por diferenciar um objeto de outro da mesma classe. De acordo com Guedes (2007), na segunda divisão da classe, aparecem os atributos. Cada atributo deve possuir um nome e, eventualmente, o tipo de dado que armazena, por exemplo, integer, float ou char. Assim, a classe especifica a estrutura de um objeto sem informar quais serão os seus valores. Em relação ao objeto, esse corresponde à instância de uma classe, em determinado momento. Apresentaremos um exemplo para facilitar o seu entendimento: Uma classe pessoa possui os atributos: nome, sexo e data de nascimento. Essa classe pode ter um objeto ou uma instância com os seguintes valores para cada atributo, respec- tivamente: Márcia, feminino, 22/03/1969. Dessa forma, em um sistema, devemos trabalhar com as instâncias (objetos) de uma classe, pois os va- lores de cada atributo são carregados nas ins- tâncias (MELO, 2004). A figura, a seguir, mostra um exemplo de classe com atributos: Cliente Figura 2 - Exemplo de classe. Fonte: os autores. Figura 3 - Exemplo de classe com atributos / Fonte: os autores. Pessoa - nome : string - sexo : string - data_nascimento : date + operation1() : void U N ID A D E 1 22 Métodos Métodos são processos ou serviços realizados por uma classe e disponibilizados para o uso de outras classes em um sistema. Além disso, devem ficar armazenados na terceira divisão da classe. Os métodos são as atividades que a instância de uma classe pode executar (GUEDES, 2007). Um método pode ou não receber parâmetros (valores que são utilizados du- rante a execução do método) bem como retornar valores os quais podem representar o resultado da operação executada ou somente indicar se o pro- cesso foi concluído com sucesso. Assim, um méto- do representa um conjunto de instruções que são executadas quando o método é chamado. Um ob- jeto da classe cliente, por exemplo, pode executar a atividade de incluir novo cliente. A Figura 4 apre- senta um exemplo de uma classe com métodos: Visibilidade De acordo com Guedes (2007), a visibilidade é um símbolo que antecede um atributo ou um método e é utilizada para indicar o seu nível de acessibilidade. Existem, basicamente, três modos de visibilidade: o público, o protegido e o pri- vado. Saiba mais sobre cada um nos tópicos a seguir, na Figura 5: Figura 5 - Modos de visibilidade / Fonte: os autores. Cliente - nome : string - endereço : string - cidade : string - UF : char - CEP : char + incluirNovoCliente() : void + atualizarCliente() : void Figura 4 - Exemplo de classe com métodos / Fonte: os autores. O símbolo mais (+) indica visibilidade pública, ou seja, significa que o atributo ou o método pode ser utilizado por objetos de qualquer classe. O símbolo sustenido (#) indica que a visibilidade é protegida, ou seja, determina que apenas objetos da classe possuidora do atributo ou do método ou de suas subclasses podem acessá-lo. O símbolo menos (-) indica que a visibilidade é privada, ou seja, somente os objetos da classe possuidora do atributo ou do método poderão utilizá-lo. (+) (#) (-) U N IC E S U M A R 23 Note que, no exemplo da classe cliente, tanto os atributos quanto os métodos apresentam a sua visibilidade representada à esquerda de seus nomes. Assim, os atributos nome, sexo e data_nascimento possuem visi- bilidade privada, pois apresentam o símbolo de menos (-) à esquerda da sua descrição. Já os métodos IncluirNovoCliente() e AtualizarCliente() apresentam visibilidade pública, assim como indica o símbolo +, acres- centado à esquerda de sua descrição. Herança (generalização/especialização) A herança permite que as classes de um sistema compartilhem atributos e mé- todos, otimizando, assim, o tempo de desenvolvimento com a diminuição de linhas de código, o que facilita futuras manutenções (GUEDES, 2007). A herança (ou generalização/especialização) acontece entre classes gerais (chamadas de superclasses ou classes-mãe) e classes específicas (chamadas de subclasses ou classes-filha) (BOOCH; RUMBAUGH; JACOBSON, 2006). A herança significa que os objetos da subclasse podem ser utilizados em qualquer local em que a superclasse ocorra, e não vice-versa. A subclasse herda as propriedades da mãe, ou seja, os seus atributos e métodos, bem como pode possuir atributos e métodos próprios, além dos herdados. De acordo com Guedes (2007), a vantagem do uso da herança é que, quando uma classe é declarada com os seus atributos e métodos especí- ficos e, após isto, uma subclasse é derivada, não é necessário redeclarar os atributos e métodos já definidos. Em outras palavras, por meio da herança, a subclasse herda tais atributos e métodos automaticamente, permitindo a reutilização do código já pronto. Assim, é preciso, somente, declarar os atributos ou métodos restritos da subclasse, o que torna o processo de desenvolvimento mais ágil, além de facilitar as manutenções futuras, pois, em caso de alteração, será ne- cessário alterar, somente, o método da superclasse para que todas as sub- classes estejam, também, atualizadas. A Figura 6 apresenta um exemplo de herança, explicitando os papéis de superclasse e subclasse e, também, apresenta o símbolo de herança da UML, uma linha que liga as classes com um triângulo que toca a superclasse: U N ID A D E 1 24 Figura 6 - Exemplo de herança / Fonte: os autores. A figura apresentada mostra a superclasse cliente com os atributos nome, en- dereço, cidade, UF e CEP, bem como os métodos incluirNovoCliente() e atuali- zarCliente(). A subclasse PessoaFisica herda esses atributos e métodos, além de possuir os atributos CPF e RG e o método validarCPF(). A seta que aponta para a superclasse cliente indica a herança. A subclasse PessoaJuridica herda, também, todos os atributos e métodos da superclasse cliente, além de possuir os atributos CNPJ, inscrição_estadual e razão_social e o método validarCNPF(). Quando olhamos para a Figura 6, notamos que a classe cliente é a mais gené- rica e as classes PessoaFisica e PessoaJuridica são as mais especializadas. Então, podemos afirmar que generalizamos quando partimos das subclasses para a su- perclasse e especializamos quando fazemos o contrário, ou seja, quando partimos da superclasse para as subclasses. Polimorfismo O conceito de polimorfismo está associado à herança, pois trabalha com a re- declaração de métodos previamente herdados por uma classe. Esses métodos, embora parecidos, diferem-se, de alguma forma, da implementação utilizada na Cliente - nome : string - endereço : string - cidade : string - UF : char - CEP : char + incluirNovoCliente() : void + atualizarCliente() : void - cnpj : int - inscricao_estadual : int - razão_social: string + validarCnpj() : void PessoaJurídica - cpf: int - RG : int + validarCpf() : void PessoaFísica U N IC E S U M A R 25 superclasse, sendo necessário implementá-los, novamente, na subclasse. Todavia, a fim de não ter que alterar o código-fonte, acrescentando uma chamada a um método com nome diferente, redeclara-se o método com o mesmo nome decla- rado na superclasse (GUEDES, 2007). De acordo com Lima (2009), o polimorfismoé o princípio em que classes derivadas (as subclasses) e uma mesma superclasse podem chamar métodos que têm o mesmo nome (ou a mesma assinatura), mas que possuem comportamentos diferentes em cada subclasse, produzindo resultados diferentes, dependendo de como cada objeto implementa o método. Em outras palavras, podem existir dois ou mais métodos com a mesma nomenclatura, diferenciando-se na maneira como foram im- plementados. O sistema é o responsável por verificar se a classe da instância em questão possui o método declarado nela própria ou se o herda de uma superclasse (GUEDES, 2007). Por ser um exemplo bastante claro, para ilustrar o polimorfismo, apresentamos a Figura 7: Figura 7 - Exemplo de poliformismo / Fonte: os autores. No exemplo apresentado, há uma classe geral chamada Conta_Comum (que, neste caso, é a superclasse), a qual possui um atributo chamado saldo, que contém o valor total depositado em determinada instância da classe, e um método chamado saque. Esse método, somente, diminui o valor a ser debitado do saldo da conta se este possuir valor suficiente. Caso contrário, a operação não poderá ser realizada, ou seja, o saque deve ser recusado pelo sistema (GUEDES, 2007). Conta_Comum - saldo : double + saque() : void - aniversario : date Conta_Poupança - limite : double + saque() : void Conta_Especial U N ID A D E 1 26 5 DIAGRAMA DE CLASSES Os diagramas de classe são usados no desenvolvimento de um modelo de sistema orien- tado a objetos para mostrar as classes de um sistema e as associações entre elas. Em pou- cas palavras, uma classe de objeto pode ser pensada como a definição geral de um tipo de objeto do sistema. Uma associação é um link entre classes que indica algum relaciona- mento entre elas. Consequentemente, cada classe pode precisar de certo conhecimento de sua classe associada. Quando você está desenvolvendo modelos durante os estágios explorando Ideias O diagrama de classes tem como objetivo permitir a visualização das classes uti- lizadas pelo sistema e como elas se relacionam, apresentando a visão estática de como estão organizadas, preocupando-se, apenas, em definir a sua estrutura lógica (GUEDES, 2007). Ainda, de acordo com o estudioso, um diagrama de classes pode ser utilizado para modelar o modelo lógico de um banco de dados, parecendo-se, neste caso, com o diagrama de entidade-relacionamento (o modelo entidade-re- lacionamento, o qual você estuda na disciplina de banco de dados). No entanto deve ficar bem claro que o diagrama de classes não é utilizado, unicamente, para esta finalidade, e que uma classe não corresponde, necessariamente, a uma tabela. U N IC E S U M A R 27 iniciais do processo de engenharia de software, os objetos representam algo no mundo real, como um paciente, uma receita médica, um médico etc. Enquanto uma aplicação é desenvol- vida, geralmente, é necessário definir objetos adicionais de implementação que são usados para fornecer a funcionalidade requerida do sistema. Fonte: adaptado de Sommerville (2011, p. 90). Estrutura da classe Uma classe pode representar o repositório lógico dos atributos de uma tabe- la, mas a classe não é a tabela. Isto se deve ao fato de que os atributos de seus objetos são armazenados em memória enquanto uma tabela armazena os seus registros fisicamente, em disco. Podemos definir classe como a descrição de uma coleção de objetos que possuem propriedades (atributos, métodos e as- sociações), conforme mostra a Figura 8: Figura 8 - Estrutura da classe / Fonte: os autores. Relacionamentos As classes não trabalham sozinhas. Pelo contrário, elas colaboram umas com as outras, por meio de relacionamentos (MELO, 2004). No diagrama de clas- ses, temos alguns tipos de relacionamentos, como o de associação (que pode ser unária ou binária), de generalização ou de agregação, por exemplo. Na sequência, detalharemos esses e outros tipos. + Nome : string = Fernando +Idade : int = 18 + andar(entrada direção: Direção) : bool + dormir() : bool Pessoa Nome da classe Métodos São as operações que podem ser executadas sobre um objeto Atributos e tipo de dado Informação associada a um objeto U N ID A D E 1 28 Associação De acordo com Melo (2004), a associação é um relacionamento que conecta duas ou mais classes, demonstrando a colaboração entre as instâncias de classe. Pode, também, existir o relacionamento de uma classe com ela mesma e, neste caso, tem-se uma associação unária ou reflexiva. A seguir, apresentaremos, na Figura 9, um exemplo de uma associação entre classes: Figura 9 - Exemplo de associação / Fonte: os autores. Associação unária ou reflexiva Segundo Guedes (2007), a associação unária ocorre quando existe o rela- cionamento da instância de uma clas- se com instâncias da mesma classe, conforme ilustra a Figura 10: No exemplo apresentado, temos a classe funcionário, cujos atributos são código e nome bem como o código do possível chefe do funcionário. Pelo fato de esse chefe, também, ser um funcionário, a associação chamada “chefia” indica possí- vel relação entre uma ou mais instâncias dessa classe com outras instâncias da mesma classe, ou seja, tal associação determina que um funcionário pode ou não chefiar outros. Além disso, essa associação faz com que a classe possua o atributo codChefe para armazenar o código do funcionário, atributo esse que é o respon- sável pela instância do funcionário em questão. Desse modo, após consultar uma instância da classe funcionário, pode-se utilizar o atributo codChefe da instância consultada para pesquisar outra instância da classe (GUEDES, 2007). - Codigo : int - Nome : char + CalcPreco() : void + CalcImposto() : void Produto - NumeroVenda: int + Data() : int Venda Associação 0..* 0..* - codigo : int - nome : char - codChefe : int Funcionario Chefia 0..* Figura 10 - Exemplo de associação unária. Fonte: os autores. U N IC E S U M A R 29 Associação binária A associação binária conecta duas classes por meio de uma reta que liga uma classe a outra. A Figura 11 demonstra um exemplo de associação binária: Figura 11 – Exemplo de associação binária / Fonte: os autores. No exemplo, um objeto da classe cidade pode ou não se relacionar com instâncias da classe cliente, conforme demonstra a multiplicidade 0..*. Entretanto, se existir um objeto da classe cliente, ele terá que se relacionar com um objeto da classe cidade, pois, pelo fato de que não foi definida a multiplicidade na extremidade da classe cidade, isto significa que ela é 1..1. Após termos explicado os relaciona- mentos em um diagrama de classes, explicaremos o conceito de multiplicidade. Agregação Uma agregação pode ocorrer, somente, entre duas classes. De acordo com as re- gras da UML, esse tipo de relacionamento ou associação é identificável a partir da notação de uma linha sólida entre duas classes: a classe denominada “todo-agre- gado” recebe um diamante vazio, enquanto a outra ponta da linha é ligada à classe denominada “parte-constituinte”. Ambas podem “viver” de forma independente, ou seja, não existe ligação forte entre as duas. Objetos da parte constituinte ou da parte agregada são independentes em termos de vida, mas ambas são partes do todo único. Essa análise dependerá do domínio do problema em estudo. Para sabermos se um relacionamento é de agregação, basta perguntarmos se, em primeiro lugar, ele comporta a estrutura todo-parte. Em seguida, questionamos se o objeto constituinte-parte “vive” sem o objeto agregado. Se a resposta for sim a ambas Cliente - nome : string - sexo : char - data_nascimento : Date - endereco : String - CEP : int - nome : String - UF : String - DDD : int Cidade 0..* U N ID A D E 1 30 as perguntas, teremos uma agregação. A Figura 12 apresenta a notação para esta se- mântica. Em determinado domínio de problema que trata de apartamentos e con- domínios, observamosque um apartamento tem depósitos. Quando nos referimos a um depósito, podemos perguntar a qual apartamento ele pertence. Por outro lado, determinado proprietário pode ter decidido vender um depósito para alguém que sequer mora no prédio. Assim, teremos os dois objetos, neste exemplo, viven- do separadamente, sem incômodo algum. Poderíamos ter a situação em que um aparta- mento é interditado ou passa por extensa reforma. Durante algum tempo, não temos movimentação naquele local, mas o depósito que faz parte daquele jogo apartamento-depósito continua sendo usado livremente. Assim, o mesmo exemplo valeria para garagens de um apartamento: Composição Uma composição ocorre quando temos uma situação semelhante à da agrega- ção entre duas classes, mas os objetos da classe parte não podem viver quando o todo é destruído. Esse tipo de relacionamento é identificável pela notação de uma linha sólida entre duas classes: a classe denominada todo-composta, a qual recebe um diamante preenchido, enquanto a outra ponta da linha é ligada à classe denominada “parte-componente”. Ambas as classes “vivem” unidas de forma dependente, ou seja, existe uma ligação forte entre as duas. Os objetos-parte não podem ser destruídos por um objeto diferen- te do objeto-todo ao qual estão relacionados (GUEDES, 2011). Essa análise depen- derá do domínio do problema em estudo. Para sabermos se um relacionamento é de composição, basta perguntarmos se, em primeiro lugar, cabe a estrutura todo-parte. Em seguida, questionamos se o objeto parte “vive” sem o objeto todo. Se a resposta for sim para a primeira pergunta e não para segunda, teremos uma composição. clsApartamento - dMetragem : double - IcountApartamento: double + obterMetragem() : void + obterNrAptsInstanciados() : int clDeposito Figura 12 - Uma agregação en- tre duas classes. Fonte: os autores. U N IC E S U M A R 31 A Figura 13 apresenta uma visão da notação para esse tipo de relacio- namento. Em relação a este domínio de problema, precisamos pensar o pré- dio como um todo. Isso parece plausível, pois, nesse domínio de problema, necessariamente, um prédio deveria existir para que haja um apartamento. Caso não pudesse qualificar um prédio, nada poderia ser feito em relação àquele apartamento. Não se pode fazer nada com um apartamento, vender ou alugar, enquanto ele não possuir um endereço. O endereço é o do pré- dio, o apartamento possui, apenas, complemento. Assim, para os fins deste software, não seria possível instanciar um objeto clsApartamento, caso não fosse designado um objeto clsPredio: Figura 13 - Composição entre duas classes / Fonte: os autores. Generalização/especialização Esta associação identifica as classes-mãe (superclasses), as quais são chamadas gerais, e as classes-filhas (subclasses), as chamadas especializadas, demonstrando a ocorrência de herança e, possivelmente, de métodos polimórficos nas classes especializadas. A seguir, na Figura 14, há um exemplo entre classes que demonstra o uso de generalização/especialização: clsApartamento - dMetragem : double - IcountApartamento: int + obterMetragem() + obterNumAptsInstanciados() : int clsDeposito clsPredio - Nome : String - Endereco : String U N ID A D E 1 32 Figura 14 - Generalização/especialização entre duas classes. Fonte: Guedes (2011, p. 114). Multiplicidade De acordo com Guedes (2007), a multiplicidade determina o número mínimo e máximo de instâncias envolvidas em cada uma das extremidades da associação, permitindo, também, especificar o nível de dependência de um objeto com os ou- tros. Quando não existe multiplicidade explícita, entende-se que ela é 1..1, o que significa que, somente, uma instância dessa extremidade da associação relaciona-se com as instâncias da outra extremidade. A Tabela 1, a seguir, demonstra alguns dos diversos valores de multiplicidade que podem ser utilizados em uma associação: Conta_Comum # nro_conta : long # dt_abertura : Date # dt_encerramento : Date # situracao : int # senha : int # saldo : double + abrir_conta(int : int) : long + consultar_Conta(long : int) : int + validar_Senha(int : int) : int + saldo_Conta() : double + extrato_Conta() : String + sacar_Valor(double : int) : int + depositar_Valor(long : int, double : int) : int + encerrar_Conta() : int - limite_conta : double + abrir_conta(int : int, double : int) : long + sacar_valor(double : int) : int + juros_Conta(double : int) : double Conta_Especial - dt_aniversario : Date + renda_conta(Date : int, double : int) : double Conta_Poupanca U N IC E S U M A R 33 Multiplicidade Significado 0..1 No mínimo, zero (nenhum), no máximo, um. Indica que os objetos das classes associadas não precisam, obrigatoria- mente, estar relacionados, mas, se houver relacionamento, indica que apenas uma instância da classe se relaciona com as instâncias da outra classe. 1..1 Um e somente um. Indica que apenas um objeto da classe se relaciona com os objetos da outra classe. 0..* No mínimo, nenhum, no máximo, muitos. Indica que pode ou não haver instâncias da classe participando do relacionamento. * Muitos. Indica que muitos objetos da classe estão envolvi- dos no relacionamento. 1..* No mínimo, um, no máximo, muitos. Indica que há, pelo menos, um objeto envolvido no relacionamento e que pode haver muitos objetos envolvidos. 2..5 No mínimo, dois, no máximo, cinco. Indica que existem, pelo menos, duas instâncias envolvidas no relacionamento e que podem ser três, quatro ou cinco as instâncias envolvidas, porém não mais do que isso. Tabela 1 - Exemplos de multiplicidade / Fonte: adaptada de Guedes (2011, p. 108). As associações que possuem multiplicidade do tipo muitos (*), em qualquer de suas extremidades, forçam a transmissão do atributo-chave na classe da outra extremida- de da associação. Entretanto esse atributo-chave não aparece desenhado na classe. Figura 15 – Multiplicidade / Fonte: os autores. Pessoa Empresa 1..* * trabalha para multiplicidade associação U N ID A D E 1 34 Classe associativa Quando um relacionamento possui multiplicidade, ou seja, muitos (*) em suas duas extremidades, é necessário criar uma classe associativa para guardar os ob- jetos envolvidos nessa associação. Na classe associativa, são definidos o conjunto de atributos que participam da associação, e não as classes que participam do relacionamento. Nesse caso, pelo menos os atributos-chave devem fazer parte da classe associativa, mas ela, também, pode ter atributos próprios. Uma classe associativa é representada por uma reta tracejada que parte do meio da associação e atinge uma classe. A Figura 16 apresenta um exemplo de classe associativa que possui atributos próprios, além dos atributos-chave das classes que participam da associação. Contudo é necessário reforçar que, neste caso, esses atributos-chave não são representados no diagrama. Figura 16 - Exemplo de classe associativa / Fonte: os autores. No exemplo apresentado, uma instância da classe curso pode se relacionar com muitas instâncias da classe disciplina, bem como uma instância da classe disci- plina pode se relacionar com muitas instâncias da classe curso. Como existe a multiplicidade nas extremidades de ambas as classes da associação, não há como reservar atributos para armazenar as informações decorrentes da associação, já que não é possível determinar um limite para a quantidade de disciplinas que um curso pode ter e nem saber a quantos cursos uma disciplina pode pertencer. Assim, é preciso criar uma classe para guardar essa informação, além das informações próprias da classe, que são a carga horária da disciplina no curso e a Curso - nome : int - area : int Disciplina_Curso - carga_horaria : int - serie : int Disciplina - nome : int Disciplina_Curso 1..* 1..* U N IC E S U M A R 35 série a qual essa disciplina estará vinculada. Os atributos-chave das classescurso e disciplina são transmitidos de forma transparente pela associação, não sendo necessário, portanto, escrevê-los como atributos da classe associativa. Segundo Guedes (2007), as classes associativas podem ser substituídas por classes normais, que, neste caso, são chamadas de classes intermediárias, mas que desempenham, exatamente, a mesma função das associativas. A Figura 17 mostra o mesmo exemplo da figura anterior, porém com a classe intermediária no lugar da classe associativa: Figura 17 - Exemplo de classe intermediária / Fonte: os autores. Curso - nome : int - area : int Disciplina_Curso - carga_horaria : int - serie : int Disciplina - nome : int 1..* 1..* U N ID A D E 1 36 CONSIDERAÇÕES FINAIS No decorrer desta unidade, estudamos a importância da modelagem de um siste- ma e como ela é a parte fundamental de todas as atividades de desenvolvimento, dentro de um processo de software, as quais levam à implantação de um bom software. Esses modelos, normalmente, são representados por meio de diagramas em que é utilizada uma notação gráfica, a qual, em nosso caso, foi a UML (Unified Modeling Language ou Linguagem de Modelagem Unificada). Iniciamos a unidade aprendendo que a modelagem de software se baseia na utilização de notações gráficas e textuais com o objetivo de construir modelos que representem as partes essenciais de um sistema. Na sequência, estudamos alguns conceitos das Ferramentas CASE (Computer-Aided Software Engineering) e da UML. Aprendemos, também, que todos os artefatos de software produzidos durante a modelagem, dentre eles, o diagrama de classe, podem ser usados como base para as fases seguintes do ciclo de vida do programa. Aprendemos que, pelo fato de a UML ser, totalmente, baseada no paradigma de Orientação a Objetos (OO), foi necessário, para compreendê-la, estudar alguns dos conceitos relacionados a tal orientação. Portanto, na sequência, abordamos os conceitos básicos desse paradigma para entender, por meio de elementos, a estrutura do sistema. Por último, relembramos o diagrama de classes, sendo o mais importante e utilizado da UML, pois é ele que define a estrutura das classes identificadas para o sistema, mostrando os atributos e os métodos de cada uma das classes bem como os seus relacionamentos. Na próxima unidade, você estudará conceitos de encapsulamento e mo- dificadores de acesso denominados public, protected e private. Entenderá a diferença dos modificadores static, abstract e final e, também, o que são os construtores de classes. Preparados para seguir em frente? Bons estudos! 37 na prática 1. Uma classe é um conjunto de objetos que possuem os mesmos tipos de caracterís- ticas e comportamentos, sendo representada por um retângulo que pode possuir até três divisões. Partindo desse conceito, elabore um diagrama de classes com duas classes que tenham as características citadas a seguir. Lembre-se que classes não trabalham sozinhas, portanto, construa, entre elas, um relacionamento 1..* (no mínimo, um, no máximo, muitos): Nome da classe: FUNCIONÁRIO Descrição: Contém dados cadastrais dos funcionários Campo Tipo Visibilidade Chave Descrição Matricula Integer Pública PK Matrícula CpfCnpj String Pública CPF ou CNPJ NomeFunc String Pública --- Nome do funcionário DtAdmissao Date Pública --- Data da admissão Métodos: +IncluirNovoFunc +BuscarFunc +ExcluirFunc Nome da Classe: DEPENDENTE Descrição: Contém dados cadastrais do dependente Campo Tipo Visibilidade Chave Descrição IdDep Integer Privada PK Número do dependente NomeDep String Privada --- Nome do dependente DtNasci- mento Date Privada --- Data de nascimento IdFunc Integer Pública FK Código do funcionário Métodos: +IncluirNovoDep +ExcluirDep 38 na prática 2. Faça um diagrama de classes para um sistema de locações de ônibus, levando em consideração os seguintes requisitos: • A empresa tem muitos ônibus. Cada ônibus tem os seguintes atributos: número de placa, cor, ano, tipo de combustível, número de portas, quilometragem, Re- navame valor de locação. • Cada ônibus tem um modelo e uma marca, mas um modelo pode relacionar-se a muitos ônibus e uma marca pode referir-se a muitos modelos, embora cada modelo só tenha uma marca específica. • Um ônibus pode ser alugado por muitos clientes, em momentos diferentes, e um cliente pode alugar muitos ônibus. É preciso saber quais estão locados ou não. Sempre que locar um ônibus, é necessário armazenar a data e a hora de sua locação e, quando for devolvido, a data e a hora da devolução. Com base nesse cenário, identifique as classes, os atributos, os métodos e os relacionamentos. 3. O diagrama de classe é uma representação da estrutura e das relações das classes que servem de modelo para objetos. Partindo disso, analise o diagrama de classe, a seguir, e assinale a alternativa correta: a) Entre as entidades ônibus e passageiro, temos um rela- cionamento de composição. b) Entre as entidades ônibus e passageiro, temos um rela- cionamento de agregação. c) Entre as entidades ônibus e passageiro, temos um relacio- namento de generalização/especialização (herança). d) Entre as entidades ônibus e passageiro, temos um rela- cionamento unário. e) Entre as entidades ônibus e passageiro, temos um rela- cionamento de realização. Onibus Passageiro 39 na prática 4. Maria controla, em Excel, a sua lista mensal de compras de produtos. Na planilha, ela cadastra: • Nome do produto. • Unidade de compra (quilos, litros, metros etc.). • Quantidade prevista para o mês. • Quantidade comprada. • Preço estimado (que é atualizado todo mês). • Valor total da compra. A quantidade comprada pode variar em função da sobra do produto de um mês para o outro ou da necessidade de um gasto menor ou maior no mês. Com base nesse cenário, identifique as classes, os atributos, os métodos e os relacionamentos. 5. Os símbolos menos (-) e mais (+), na frente dos atributos e métodos, representam a sua visibilidade. Esta, por sua vez, é utilizada para indicar o nível de acessibilidade de determinado atributo ou método, sendo, sempre, representada à esquerda. A partir da informação apresentada, descreva os principais três modos de visibilidade utilizados na UML. 40 aprimore-se A UML – Unified Modeling Language ou Linguagem de Modelagem Unificada – é uma linguagem visual utilizada para modelar softwares baseados no paradigma de orientação a objetos. É uma linguagem de modelagem de propósito geral que pode ser aplicada a todos os domínios de aplicação. Essa linguagem tornou-se, nos úl- timos anos, a linguagem-padrão de modelagem adotada internacionalmente pela indústria de engenharia de software. Deve ficar bem claro, porém, que a UML não é uma linguagem de programa- ção, e sim uma linguagem de modelagem, uma notação, cujo objetivo é auxiliar os engenheiros de software a definirem as características do sistema, tais como seus requisitos, seu comportamento, sua estrutura lógica, a dinâmica de seus proces- sos e até mesmo suas necessidades físicas em relação ao equipamento sobre o qual o sistema deverá ser implantado. Tais características podem ser definidas por meio da UML antes do software começar a ser realmente desenvolvido. Além disso, cumpre destacar que a UML não é um processo de desenvolvimento de software e tampouco está ligada a um de forma exclusiva, sendo totalmente independente, po- dendo ser utilizada por muitos processos de desenvolvimento diferentes ou mesmo da forma que o engenheiro considerar mais adequada. A UML surgiu da união de três métodos de modelagem: o método de Booch, o método OMT (Object Modeling Technique) de Jacobson, e o método OOSE (Objec- t-Oriented Software Engineering) de Rumbaugh. Estes eram, até meados da déca- da de 1990, os métodos de modelagem orientada a objetos mais populares entre os profissionais da área de desenvolvimento de software. A união desses métodos contoucom o amplo apoio da Rational Software, que a incentivou e financiou. O esforço inicial do projeto começou com a união do método de Booch ao OMT de Jacobson, o que resultou no lançamento do Método Unificado no final de 1995. Logo em seguida, Rumbaugh juntou-se a Booch e Jacobson na Rational Software, e seu método OOSE começou também a ser incorporado à nova metodologia. O trabalho 41 aprimore-se de Booch, Jacobson e Rumbaugh, conhecidos popularmente como “Os Três Ami- gos”, resultou no lançamento, em 1996, da primeira versão da UML propriamente dita. Tão logo a primeira versão foi lançada, muitas empresas atuantes na área de modelagem e desenvolvimento de software passaram a contribuir para o projeto, fornecendo sugestões para melhorar e ampliar a linguagem. Finalmente, a UML foi adotada, em 1997, pela OMG (Object Management Group ou Grupo de Gerencia- mento de Objetos), como uma linguagem-padrão de modelagem. A versão 2.0 da linguagem foi oficialmente lançada em julho de 2005, encontran- do-se, atualmente, na versão 2.3 beta. A documentação oficial da UML pode ser con- sultada no site da OMG em www.omg.org ou mais exatamente em www.uml.org. POR QUE MODELAR SOFTWARE? Qual a real necessidade de se modelar um software? Muitos “profissionais” podem afirmar que conseguem determinar todas as necessidades de um sistema de in- formação de cabeça, e que sempre trabalharam assim. Qual a real necessidade de se projetar uma casa? Um pedreiro experiente não é capaz de construí-la sem um projeto? Isso pode ser verdade, mas a questão é muito mais ampla, envolvendo fatores extremamente complexos, como levantamento e análise de requisitos, pro- totipação, tamanho do projeto, complexidade, prazos, custos, documentação, ma- nutenção e reusabilidade, entre outros. Existe uma diferença gritante entre construir uma pequena casa e construir um prédio de vários andares. Obviamente, para se construir um edifício é necessário, em primeiro lugar, desenvolver um projeto muito bem-elaborado, cujos cálculos têm de estar extremamente corretos e precisos. Além disso, é preciso fornecer uma estimativa de custos, determinar em quanto tempo a construção estará concluída, avaliar a quantidade de profissionais necessária à execução da obra, especificar a 42 aprimore-se quantidade de material a ser adquirida para a construção, escolher o local onde o prédio será erguido etc. Grandes projetos não podem ser modelados de cabeça, nem mesmo a maioria dos pequenos projetos pode sê-lo, exceto, talvez, aqueles extremamente simples. Na realidade, por mais simples que seja, todo e qualquer sistema deve ser mode- lado antes de se iniciar sua implementação, entre outras coisas, porque os sistemas de informação frequentemente costumam ter a propriedade de “crescer”, isto é, au- mentar em tamanho, complexidade e abrangência. Muitos profissionais costumam afirmar que sistemas de informação são “vivos”, porque nunca estão completamen- te finalizados. Na verdade, o termo correto seria “dinâmicos”, pois normalmente os sistemas de informação estão em constante mudança. Tais mudanças são devidas a diversos fatores, como, por exemplo: • Os clientes desejam constantemente modificações ou melhorias no sistema. • O mercado está sempre mudando, o que força a adoção de novas estraté- gias por parte das empresas e, consequentemente, de seus sistemas. • O governo seguidamente promulga novas leis e cria novos impostos e alí- quotas ou, ainda, modifica as leis, os impostos e alíquotas já existentes, o que acarreta a manutenção no software. Assim, um sistema de informação precisa ter uma documentação extremamente de- talhada, precisa e atualizada para que possa ser mantido com facilidade, rapidez e correção, sem produzir novos erros ao corrigir os antigos. Modelar um sistema é uma forma bastante eficiente de documentá-lo, mas a modelagem não serve apenas para isso: a documentação é apenas uma das vantagens fornecidas pela modelagem. Fonte: Guedes (2011, p. 19-21). 43 eu recomendo! UML 2 – Uma Abordagem Prática Autor: Gilleanes Thorwald Araújo Guedes Editora: Novatec Sinopse: a UML – Unified Modeling Language ou Linguagem de Modelagem Unificada – é uma linguagem utilizada para modelar softwares baseados no paradigma de orientação a objetos, aplica- da, principalmente, durante as fases de análise de requisitos e de projeto de software. A UML consagrou-se como a linguagem-padrão de modelagem adotada, internacionalmente, pela indústria de Engenharia de Software e, assim, há amplo mercado para profissionais que a dominam. Este livro procura ensinar ao leitor, por meio de exemplos práticos, como modelar softwares pela UML. A lingua- gem é ensinada mediante a apresentação de seus muitos diagramas, detalhando o propósito e a aplicação de cada um deles e os elementos que os compõem, as suas funções e formas de aplicação. A obra enfatiza, ainda, a importância da UML para a Engenharia de Software, além de abordar o paradigma de orientação a objetos, um conceito imprescindível para a compreensão correta da linguagem. livro 44 eu recomendo! Este vídeo mostra uma introdução aos conceitos de orientação a objetos. http://www.youtube.com/watch?v=MnJLeYAno4o&feature=relmfu. O vídeo mostra a importância da modelagem de sistemas bem como da elabora- ção do diagrama de casos de uso. http://www.youtube.com/watch?v=hfN6n5fJfLc&feature=relmfu. conecte-se UML – Guia do Usuário Autor: Grady Booch, James Rumbaugh e Ivar Jacobson Editora: Campus Sinopse: há quase 10 anos, a Unified Modeling Language (UML) é o padrão para visualizar, especificar, construir e documentar os arte- fatos de um sistema de software. A UML 2.0 vem para assegurar as contínuas popularidade e viabilidade da linguagem. As suas simpli- cidade e expressividade permitem que os usuários modelem tudo, desde sistemas empresariais de informação, passando por aplicações distribuídas baseadas na Web até sistemas embutidos de tempo real. Nesta nova edição, totalmente revista, os criadores da linguagem fornecem um tutorial dos aspectos centrais da UML. Começando com um modelo conceitual da UML, o livro aplica, progressivamente, a linguagem em uma série de problemas de modelagem cada vez mais complexos, usando diversos domínios de aplicação. A abordagem em tópicos é recheada de exemplo, o que fez da primeira edição um recurso indispensável. Entretanto, o con- teúdo reflete as mudanças na notação e no uso exigidos pela UML 2.0. livro 2 PLANO DE ESTUDO A seguir, apresentam-se os tópicos que você estudará nesta unidade: • O que é encapsulamento? • Modificadores de acesso: default, public, protected e private • Utilizando modificadores de acesso (getters e setters) • Modificadores static, abstract e final • Construtores de classe. OBJETIVOS DE APRENDIZAGEM • Entender o conceito de encapsulamento • Estudar os modificadores de acesso default, public, protec- ted e private • Controlar o acesso a métodos, atributos e construtores por meio de modificadores • En- tender a diferença dos modificadores static, abstract e final • Entender o que são construtores de classes. MODIFICADORES JAVA E ENCAPSULAMENTO PROFESSORES Dr. Edson Alves de Oliveira Junior Me. Andre Abdala Noel Me. Márcia Cristina Dadalto Pascutti Me. Rafael Alves Florindo Esp. Victor de Marqui Pedroso Esp. Janaina Aparecida de Freitas INTRODUÇÃO Caro(a) aluno(a), após estudarmos a importância da modelagem de um sistema em UML, e como ela é a parte fundamental de todas as atividades dentro de um processo de software, nesta segunda unidade, você verá a revisão dos conceitos de encapsulamento, de modificadores de acesso e construtores em Java. A ideia de encapsulamento remete ao fato de que é possível agru- par estados e comportamentos de um objeto em um mesmo módulo e, ainda, controlar as suas propriedades e o seu acesso, ou seja, trata dos padrões de visibilidade de acessos para as classes,os atributos e os métodos. Para estudar o encapsulamento, escreveremos métodos de acesso que são mais conhecidos como modificadores de acesso. Estes atuarão em elementos de uma classe, utilizando os chamados métodos setters (armazenamento) e getters (resgate). Nos modificadores de acesso setters e getters, a linguagem Java permite o uso dos modificadores static, final e abstract. O modificador static permite que um método de uma classe seja invocada sem ter a instância de uma. O modificador final bloqueará a herança da classe ou o reuso de um método em outra classe. O modificador abstract permite modelar uma classe de forma que ela seja um modelo para as outras que a estendem. Por fim, abordaremos o momento em que uma classe é instanciada, ou seja, quando criamos um objeto de uma classe que, automaticamente, é executado o seu método construtor, que pode inicializar atributos ou outras instruções essenciais para o início da classe. Dessa forma, con- trolaremos como os objetos de uma classe serão inicializados e de qual forma podemos melhor aproveitá-los. Para mostrar a você como utilizar estes recursos, empregaremos alguns exemplos que usam essas técnicas de modificadores de acesso e de cons- trutores de classe. Discutiremos, ao longo da unidade, um exemplo para a apresentação do tema e, também, outro exemplo, em que será construído um projeto completo, desenvolvido com as técnicas apresentadas. U N IC E S U M A R 47 1 O QUE É ENCAPSULAMENTO? Classes (e seus objetos) encapsulam, isto é, contêm seus atributos e métodos. Os atri- butos e métodos de uma classe (e de seu objeto) estão intimamente relacionados. Os objetos podem se comunicar entre si, mas eles, em geral, não sabem como outros ob- jetos são implementados – os detalhes de implementação permanecem ocultos dentro dos próprios objetos. Esse ocultamento de informações, como veremos, é crucial à boa engenharia de software. Fonte: Deitel e Deitel (2017, p. 9). conceituando Caro(a) aluno(a), quando se fala de encapsulamento, você, provavelmente, ima- gina um objeto fechado, dentro de uma cápsula, e isto não é muito diferente na programação orientada a objetos, pois a ideia principal do encapsulamento envolve omitir os membros de uma classe, além de esconder como funcionam as rotinas ou as regras de negócio. Dessa forma, o programador deve se atentar às funcionalidades da classe, e não como ela foi implementada. Isto é bom, uma vez que há a expansão na modularidade do seu projeto. Sendo assim, realizar o encapsulamento de um projeto é fundamental para a possibilidade de minimizar o impacto de problemas referentes a alterações do U N ID A D E 2 48 projeto, uma vez que não é preciso alterar uma regra de negócio em vários lugares, mas, sim, em apenas um lugar, já que tal regra está encapsulada. Para exemplificar, focaremos no problema de realização de operações bancá- rias em um caixa eletrônico. No caixa, é possível: sacar dinheiro, depositar, verifi- car saldo, verificar extrato, pagar contas e fazer transferências. Para utilizar o caixa eletrônico, não é necessário conhecer como as operações foram implementadas em nível de programação, mas, sim, saber o que cada operação afeta em sua conta. Analise a Figura 1, a seguir, e verifique a classe Conta, com atributos e méto- dos para uma Conta Corrente. Note, por exemplo, que o método transferir rece- be, como parâmetro, o número da conta (numContaDestino) e um valor a ser transferido (valor). Para utilizar esse método, o programador não precisa saber como o criador dessa classe o implemen- tou, basta saber que esse método é respon- sável por enviar um valor para outra conta corrente, e que essa transferência implica subtração do saldo da conta remetente. Ainda em relação à Figura 1, que ilustra um diagrama de Classe UML, o símbo- lo soma (+) indica que tanto os atributos quanto os métodos são públicos. Isso significa que eles podem ser acessados e modificados de qualquer outra classe do projeto, e isto não é uma boa prática de programação, uma vez que os seus atributos estão expostos e qualquer outro objeto pode alterar os valores dos ele- mentos das classes, comprometendo, assim, a coesão do seu projeto. Por exemplo, na Figura 2: //Criação da referência a Conta ContaCorrente cc = new Conta(); //Modificação do atributo saldo de forma direta cc.saldo = 0; System.out.println("Saldo Conta: R$"+cc.saldo); Para solucionar este problema, faz-se necessária a utilização de modificadores de acesso, sendo que eles farão o papel de restringir ou autorizar o acesso a determi- nados atributos ou métodos das classes, e isto será visto a partir da próxima seção. Conta + saldo : float + titular : String + cpfTitular : String + numConta : int + sacar(valorSaque : float) : void + depositar(valorDep : float) : void + verificarSaldo() : void + transferir(numContaDestino : int, valor : int) : float Figura 1 - Classe Conta com atributos e métodos públicos / Fonte: os autores. U N IC E S U M A R 49 2 MODIFICADORES DE ACESSO: Default, Public, Protected e Private Segundo Deitel e Deitel (2017), projeto e pacotes de projeto são conceitos básicos da programação em Java: Um projeto é a base para o desenvolvimento em Java, é nele que estarão todos os paco- tes, as classes, as interfaces, as imagens da sua aplicação (2017). O rico conjunto do Java de classes predefinidas é agrupado em pacotes – chamados de grupos de classes. Estes são referidos como biblioteca de classes Java, ou Interface de Programação de Aplicativo Java (API Java). Fonte: os autores. conceituando É a partir dos modificadores de acesso que poderemos restringir ou não o acesso aos atributos e métodos de uma classe. Na programação orientada a objetos, os modificadores de acesso mais utilizados são o public e o private. Antes de abordarmos os modificadores, é importante frisar alguns conceitos básicos sobre a programação em Java: projeto e pacotes do projeto. Quando trabalhamos com o Netbeans para a criação de um projeto, basta aces- sarmos o menu “Arquivo” e, em seguida, selecionar a opção “Novo Projeto”. Feito isto, aparecerá uma tela, como você pode verificar na Figura 2, a seguir: U N ID A D E 2 50 Figura 2 - Criando um projeto em Java no Netbeans / Fonte: os autores. Como o Netbeans é uma IDE que suporta diversas linguagens de programação, ao criar o projeto, é necessário, previamente, selecionar a linguagem e, em segui- da, o tipo de projeto. Sendo assim, criaremos um Aplicativo Java selecionando a opção “Aplicativo Java”. Feito isso, uma nova tela solicitará o preenchimento do nome do projeto, como você pode verificar na Figura 3: Figura 3 - Configurando o nome do projeto / Fonte: os autores. U N IC E S U M A R 51 Após essas etapas, o seu projeto estará criado e poderá ser visualizado na Paleta “Projetos” do Netbeans, como pode ser ve- rificado na Figura 4. Você pode notar que um projeto envolve pacotes e bibliotecas que, talvez, a sua aplicação necessite utilizar. Figura 4 - Projeto em Java Fonte: os autores. Para criar um novo pacote na pasta “Pacotes de código-fonte”, por exemplo, é neces- sário clicar sobre a pasta com o botão direito e selecionar a opção: “Novo” -> “Pacote Java” e, em seguida, dar um nome ao seu novo pacote, como mostra a Figura 5. Figura 5 - Criando um novo pacote Fonte: os autores. Agora que você já entendeu o conceito de pacotes, iniciaremos a teoria sobre modificadores de acesso. Temos quatro deles: default, public, private e protected. O modificador default não precisa ser declarado, ou seja, atributos ou mé- todos que não têm a declaração de um modificador de acesso são considerados default (do inglês, significa “padrão”). Como exemplo, você pode notar, no código, a criação de um atributo com modificador de acesso default. No pacote em que está localizada a classe criada, o atributo com essemodificador pode ser acessa- do de forma direta por qualquer classe, agora, se a classe que instanciar a classe estiver em outro pacote, este não será acessível de forma direta. U N ID A D E 2 52 //exemplo de atributo default String valorTransitorio; Atributos e métodos públicos (public) podem ser acessados e modificados de qualquer classe do projeto a partir da instância do objeto, ou seja, é possível, até de outros pacotes, ter acesso a atributos/métodos do tipo public, de forma direta. Para declarar esse tipo de modificador, é necessário utilizar a palavra reservada public antes de indicar o tipo de variável, isso pode ser verificado no exemplo de código-fonte, a seguir. //exemplo de atributo public public int idade; //exemplo de método public public int media(int valor1, int valor2){ public float media; media = (valor1 + valor2) / 2; return media } Já atributos e métodos privados (private) são acessados somente pela pró- pria classe, independentemente do pacote em que a classe esteja. O código fonte, a seguir, mostra como criar um atributo privado. Métodos privados só podem ser acessados pela própria classe, ou seja, eles não são visíveis e nem acessados por outros objetos. //exemplo de atributo private private String nomePessoa; //exemplo de método private private String[] splitNome(String n){ return n.split(""); } Outro modificador de acesso não tão comumente utilizado é o modificador pro- tected (protegido, em português). Este funciona como um modificador público U N IC E S U M A R 53 para elementos que estão no mesmo pacote e, em outros pacotes, ele só é visível para classes que herdam a classe que possui o atributo protegido. A criação de atributos/métodos protegidos se faz por meio da palavra reservada protected antes da criação do atributo/método. //exemplo de atributo protected protected String nomeCliente; //exemplo de método protected protected String[] splitNome(String n){ return n.split(""); } Com base nas explicações dadas até aqui sobre os modificadores, é possível mon- tar uma tabela que resume se o atributo/método é acessível ou não, dependendo de sua localização no projeto. Modificador Na própria classe Em outro pacote No mesmo pacote Herança (em pacote diferente) Public Acessível Acessível Acessível Acessível Private Acessível Não Não Não Protected Acessível Não Acessível Acessível Default Acessível Não Acessível Não Tabela 1 - Modificadores de acesso e suas visibilidades / Fonte: os autores. É importante que você, como futuro(a) programador(a), entenda bem esta tabela e a tenha por perto para eventuais consultas. Sendo assim, na próxima seção, mostraremos, na prática, como utilizar os modificadores de acesso de modo a manter o encapsulamento de suas aplicações. U N ID A D E 2 54 3 UTILIZANDO MODIFICADORES DE ACESSO (Getters e Setters) Figura 6 - Exemplo de projeto, seus pa- cotes e códigos-fonte / Fonte: os autores. Tendo como base um projeto-exemplo de caixa eletrônico, cujo objetivo é realizar operações em contas bancárias, elucidaremos a utilização dos modificadores de acesso e, além disso, traremos o conceito dos getters e setters. Para isto, veja como foi projetada a aplicação na Figura 6: Como você pode notar, o projeto possui três pacotes: “caixaeletronico”, “cliente” e “conta”. Cada pacote tem suas classes e suas particularidades que descrevere- mos, uma a uma, a seguir. O pacote “caixaeletronico” possui a classe “CaixaEletronico” e esta é a clas- se inicial do projeto, é nela que se ini- cia a execução da aplicação, pois esta contém o método principal (main) e ela será o nosso gerenciador de contas. U N IC E S U M A R 55 O pacote “cliente” possui classes que estruturam os objetos Clientes que po- derão ser pessoa física (Fisica.java) ou jurídica (Juridica.java). Será possível, tam- bém, estruturar o formato do endereço de cada pessoa (Endereco.java). Por fim, o pacote “conta” possui a classe Conta.java que armazena as infor- mações de uma conta. No diagrama, a seguir, apresentado na Figura 7, é possível ter uma visão geral de cada classe do projeto. Figura 7 - Diagrama de classe do projeto de caixa eletrônico / Fonte: os autores. Tendo, como base, os diagramas da Figura 7, primeiramente, entenderemos o Diagrama de Classes em questão: note que, antes dos nomes dos atributos e mé- todos das classes, existe um sinal (-, + ou #), em UML. O sinal - significa que se trata de um atributo/método que utiliza modificador de acesso private, o sinal + indica o modificador public, e o sinal # indica o modificador protected. Note, ainda, que as classes Física e Jurídica se ligam à classe Pessoa por meio de uma seta grande e vazia, e esta ligação significa que Física e Jurídica são “filhas” de Pessoa, ou seja, herdam todas as propriedades da classe Pessoa. Pessoa # nomePessoa : String # endereco : Endereco # telefone : String Fisica - cpf : String Juridica - cnpj : String Juridica - saldo : float - numConta : int - titular : Pessoa - tipoConta : int Endereco - rua : String - numero : int - cidade : String - uf : String Caixa Eletronico - c : Conta - contasCadastradas : ArrayList<Conta> + sacar(valorSaque : float) : void + depositar(valorDep : oat) : void + verificarSaldo() : void + transferir(numContaDestino : Conta, valor : float) : void U N ID A D E 2 56 Agora, você deve estar se perguntando: “se houve restrição do acesso a alguns atributos das classes apresentadas, como será realizada a alteração das informações, quando fo- rem necessárias?”. A resposta é, relativamente, simples: basta criar métodos públicos que controlarão os acessos a essas variáveis, ou seja, será criada uma interface de acesso para cada atributo privado (private) da classe em questão, é aí que entra o conceito de getters e setters na programação orientada a objetos, e esse é um mecanismo de acesso a dados encapsulados. pensando juntos Sendo assim, aprenderemos a criar métodos que alteram os valores dos atri- butos privados; a estes métodos damos o nome de setters, ou seja, eles “seta- rão” os valores desses atributos. Para criar um método setter, primeiro, faz-se necessário analisar o tipo do atributo. Veremos, por exemplo, o atributo (saldo) da classe Conta; por se tratar de um do tipo float (real), o método para setá-lo deverá receber, como parâmetro, um elemento, também, float. Como é um método que não terá retorno, então, utiliza-se o tipo de retorno como void. Além disso, o padrão para métodos set- ters é sempre utilizar a palavra “set” e, em seguida, concatenar com o nome da variável. No caso em tela, teríamos o método de nome setSaldo, que terá a sua implementação da seguinte forma: //metodo setSaldo – setter para o atributo saldo public void setSaldo(float s){ this.saldo = s; } //metodo setNumConta – setter para o atributo numConta public void setNumConta(int n){ this.numConta = n; } U N IC E S U M A R 57 Além do método de alterar o valor do atributo, precisamos utilizar o método para “resgatar” o valor deste atributo, a esses métodos chamamos de getters e a característica deste tipo de método é que ele retorna o valor do atributo para o elemento que o chama. Os métodos “get” são precedidos pela palavra “get” e o nome do atributo, e eles devem retornar elementos do mesmo tipo de atributo em questão. Utilizando o mesmo exemplo do atributo Saldo e o atributo numConta, teremos os métodos getters a seguir: //metodo getSaldo – getter para o atributo saldo public float getSaldo(){ return this.saldo; } //metodo getNumConta – getter para o atributo numConta public int getNumConta(){ return this.numConta; } Como exemplo, a conta foi reimplementada e, utilizando os métodos getters e setters de maneira adequada, ela ficará como apresentado no código a seguir. Note que, para o atributo saldo, a implementação do getter e do setter não foi feita,tendo em vista que existem três operações básicas que devem, em um ambiente de caixa eletrônico, “controlar” essa variável. Essas operações são: sacar, transferir ou depositar, que serão implementadas posteriormente. U N ID A D E 2 58 package conta; import cliente.Pessoa; public class Conta { private float saldo; private Pessoa titular; private int numConta; private int tipoConta; public int getNumConta() { return numConta; } public void setNumConta(int numConta) { this.numConta = numConta; } public int getTipoConta() { return tipoConta; } public void setTipoConta(int tipoConta) { this.tipoConta = tipoConta; } public Pessoa getTitular() { return titular; } public void setTitular(Pessoa titular) { this.titular = titular; } } Dica da ferramenta: no Netbeans, é possível a criação “automática” dos métodos getters e setters. Para isto, no código-fonte de sua classe, após a declaração dos atributos, pressio- ne a tecla Alt+Insert e escolha a opção “getters e setters...” e, então, selecione os atributos que deseja gerar os métodos. Fonte: os autores. pensando juntos U N IC E S U M A R 59 4 MODIFICADORES STATIC, ABSTRACT e Final Além dos modificadores de acesso, o Java permite a utilização de outros modifi- cadores, são eles: static, final e abstract. Cada um deles será abordado em seções, pois eles têm características bem diferentes entre si. Modificador static O modificador static pode ser aplicado a variáveis e a métodos, e a principal característica dele é que, se tratando de atributos, todos os objetos compartilham do mesmo espaço de memória e, tratando-se de método, este pode ser acessado sem a necessidade de instância do objeto. Você, provavelmente, já criou alguma aplicação em Java e se deparou com o méto- do “public static void main (String args[])”, e deve ter se perguntado: “o que significam todas estas palavras antes do nome do método principal?”. Então, vamos por partes. O modificador de acesso public indica que este método pode ser visualizado de qualquer classe do projeto, o modificador static indica que, para acessar o método da classe, não é necessário instanciá-lo, e o que acontece é que métodos U N ID A D E 2 60 e variáveis static são alocados em memória antes que qualquer objeto seja criado. Por essa razão, um atributo não static, originário de uma classe ausente no mé- todo static, não pode ser acessado de forma direta, pois ele só existirá a partir da instância do objeto. Por exemplo, no código a seguir, da classe “Exemplo”, temos duas variáveis: valor (int) e valor2(static int). Note que, dentro do método static (public static void main(String[] args)), ao tentar atribuir um inteiro 30 à variável valor, o NetBeans acusará o erro com os seguintes dizeres: “non-static variable valor cannot be referenced form a static context”, traduzindo, isto significa que a variável static valor não pode ser referenciada em um contexto static. package exemplo; public class Exemplo { int valor; static int valor2; public static void main(String[] args) { valor = 30; valor2 = 10; } } Para resolver esse erro, temos duas saídas: uma é transformar o atributo valor em static, a outra é criar um objeto (instância) do tipo “Exemplo” e, então, acessar e modificar a variável valor. Veja, a seguir, como ficariam as duas soluções. package exemplo; public class Exemplo { static int valor; static int valor2; public static void main(String[] args) { valor = 30; valor2 = 10; } } U N IC E S U M A R 61 package exemplo; public class Exemplo { int valor; static int valor2; public static void main(String[] args) { Exemplo e = new Exemplo(); e.valor = 30; valor2 = 10; } } Uma variável estática é compartilhada por todas as instâncias de uma classe, ou seja, em vez de cada instância da classe ter uma cópia dessa variável, esta é, apenas, uma, compartilhada por todas as instâncias. Para a existência de uma variável estática, sequer é necessária a criação de uma instância da classe que tenha a va- riável, é necessário, somente, que a classe seja carregada na Máquina Virtual Java, já que esta é criada e inicializada no momento de carga da classe. Exemplificando, imagine que haja um limite para realizar transferências entre contas e que esse limite seja igual para todas as contas. Para resolver este proble- ma, basta declarar o atributo (limiteTransferencia) como static; dessa forma, o limite é alterado para todas as referências de conta. Veja a Figura 8, a seguir, em que é ilustrada a variável (limiteTransferencia) como um atributo compartilhado. Figura 8 - Ilustração de variável static limiteTransferencia / Fonte: os autores. Conta c1 Conta c2 saldo = 200,40 titular = “Joaquim” numConta = 3540 tipoConta = 1 saldo = 400,40 titular = “Maria” numConta = 35520 tipoConta = 1 Conta.limiteTransferencia=3000 U N ID A D E 2 62 Dica de programação: quando se declara um atributo do tipo final, usa-se, como padrão, o seu nome em caixa alta, por se tratar de um atributo constante. pensando juntos O modificador final O modificador final restringe, ainda mais, o acesso aos elementos de uma classe. Ele faz com que o atributo não possa ser modificado em tempo de execução, ou seja, cria-se uma variável que terá um valor constante, do início ao término da execução da aplicação. Para classes, indica que estas não poderão ser herdadas (não poderão ter filhos) e, para métodos, indica que estes são impossibilitados de serem sobrescritos (usar técnicas de polimorfismo). Variáveis de instância do tipo final podem ser declaradas em conjunto com o modificador static. Nesse caso, se este for declarado, na inicialização da variá- vel, o valor atribuído a ele será o mesmo para todos os objetos e não poderá ser modificado durante a execução do projeto. Se uma variável static final não for inicializada, ocorrerá erro de compilação. Podemos, então, utilizar o exemplo do limite de transferência para ilustrar a utilização do modificador final. public class Conta{ … private final static float LIMITETRANSFERENCIA=3000; … } Métodos final não podem ser sobrescritos, ou seja, eles, em uma superclasse (clas- se pai), não podem ser reimplementados na subclasse (classe filha). Os métodos declarados como private são, implicitamente, final, porque não é possível sobres- crever em uma subclasse. Uma classe final não pode ser superclasse, ou seja, não pode ter classes que herdam as suas propriedades. Portanto, quando uma classe é final, implicitamen- te, todos os métodos são final. U N IC E S U M A R 63 O modificador abstract O modificador abstract é aplicado, somente, a métodos e a classes. Métodos abs- tratos não fornecem implementações e, em classes abstratas, não é possível a criação de objetos da classe e, normalmente, possuem um ou mais métodos abstratos. O objetivo de criação de classes abstratas é fornecer uma superclasse apropriada a partir da qual outras classes podem herdar e, assim, compartilharem um design comum. Veja, a seguir, a criação da classe (Pessoa) como uma classe abstrata. Basea- da no projeto do caixa eletrônico, essa classe possui, também, um método abstrato para cadastro. A declaração deste método “força”, nas subclasses, a programação dele. package cliente; public abstract class Pessoa { protected String telefone; protected String nomePessoa; protected Endereco e = new Endereco(); public abstract void cadastra(); public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public String getNomePessoa() { return nomePessoa; } public void setNomePessoa(String nomePessoa) { this.nomePessoa = nomePessoa; } public Endereco getE() { return e; } public void setE(Enderecoe) { this.e = e; } } U N ID A D E 2 64 Logo, a seguir, é apresentada a “tentativa” de criação de um objeto de uma classe abstrata, no detalhe da mensagem do IDE (Figura 9), em que uma classe abstrata não pode ser instanciada. package caixaeletronico; import cliente.Pessoa; public class Caixa Eletrônico { public static void main(String[] args) { Pessoa p = new Pessoa();//erro nesta linha - Pessoa is abstract; cannot be instantiated } } Figura 9 - Dica de erro ao instanciar classe abstrata / Fonte: os autores. Criaremos uma subclasse para a classe Pessoa. Note que o método “cadastra” precisou ser implementado na classe Física. package cliente; import java.util.Scanner; public class Fisica extends Pessoa { private String cpf; //implementação do método abstrato é imprescindível @Override public void cadastra(){ //leitura via teclado Scanner tec = new Scanner(System.in); System.out.println("Digite o nome ”); nomePessoa = tec.nextLine(); System.out.println("Digite o telefone"); telefone = tec.nextLine(); System.out.println("Digite o cpf"); cpf = tec.nextLine(); Pessoa is abstract; cannot be instantiated ---- (Alt-Enter mostra dicas) " U N IC E S U M A R 65 Muitos devem se perguntar para que serve esta anotação. Quando criamos uma sobre- posição, costumamos utilizar a anotação @Override, que identifica que esse método está sendo reescrito. Quando usamos a anotação, o código, além de legível, obriga o compila- dor a aplicar as regras de reescrita para essa marcação. Fonte: os autores. explorando Ideias O que são esses construtores? Um construtor é o primeiro “método” executado sempre que uma classe é instanciada. Quando se utiliza a palavra-chave new, o construtor será executado e inicializará o objeto. Fonte: os autores. explorando Ideias e.cadastra(); } } Um construtor não é um método, pois este não possui a declaração de retorno. Lembre- -se, porém, disso: toda classe em Java tem, pelo menos, um construtor, exceto interfaces. 5 CONSTRUTORES DE CLASSE U N ID A D E 2 66 Existem classes que, quando inicializadas, requerem algum tipo de parâmetro, por exemplo, objetos do tipo Scanner (para realizar leitura de informações para o projeto), que você pode conferir no código, a seguir. Scanner tec = new Scanner (System.in); Quando fazemos isto, estamos inicializando a classe com os dados parâmetros. O construtor inicializa as variáveis de instância da classe, como também executa códigos necessários para a inicialização de uma classe. Em outras palavras, no construtor, você pode determinar o que será realizado assim que o seu objeto for instanciado. Vamos utilizar o exemplo da Classe Pessoa e criar um construtor para ela: public abstract class Pessoa { protected String telefone; protected String nomePessoa; protected Endereco e = new Endereco(); public Pessoa(){ super(); System.out.println("Executando o construtor de Pessoa"); } ...//outros métodos da classe } Note que o construtor é similar a um método, mas ele não tem nem tipo de retorno, nem void. Outro fato importante: um construtor sempre terá o mesmo nome da classe. Modificadores de acesso podem ser atribuídos aos construtores, inclusive, o private. Se o construtor não for criado junto ao código-fonte da classe, o com- pilador criará, automaticamente, um construtor para a sua classe. Quando uma classe é instanciada, o método super() dela é chamado, mesmo que não seja declarado, pois, em algum momento, a classe terá que ser inicializada. Lembre-se que todas as classes em Java são filhas da classe Object, ou seja, Object é a mãe de todas as classes (ela fornece métodos como equals e toString, por exemplo). Ainda, em nosso exemplo apresentado na Figura 8, em que objetos do tipo Física são filhos da classe Pessoa, quando criamos uma instância de Física, os U N IC E S U M A R 67 construtores são executados em forma de pilha: Física chama Pessoa, que cha- ma Object. Ou seja, o construtor de Física será executado por último. Analise os códigos a seguir, logo após a saída do compilador, na Figura 10. public class Fisica extends Pessoa{ private String cpf; public Fisica(){ System.out.println("Pessoa Fisica"); } } public class CaixaEletronico { public static void main(String[] args) { Fisica f = new Fisica(); } } Figura 10 - Saída do compilador quando se instancia a classe Física / Fonte: os autores. Note que, ao realizar a instância da classe Física, o construtor da classe Pessoa, também, foi acionado. Uma classe pode possuir mais de um construtor. Você deve estar se pergun- tando: mas como isto é possível? Para tal, é preciso criar construtores com ar- gumentos diferentes, dessa forma, criam-se diversas formas de inicializar um objeto. Como exemplo, utilizaremos a classe Física, passando, como parâmetro, no momento da inicialização da classe, o nome do titular da conta. Analise os códigos a seguir e, depois, a saída do compilador, na Figura 11. U N ID A D E 2 68 package cliente; import java.util.Scanner; public class Fisica extends Pessoa{ private String cpf; public Fisica(){ System.out.println("Pessoa Fisica"); } public Fisica(String nome){ nomePessoa = nome; } } //...outros métodos da classe package caixaeletronico; import cliente.Fisica; public class CaixaEletronico { public static void main(String[] args) { Fisica f = new Fisica(); Fisica f2 = new Fisica("Joaquim"); System.out.println(f2.getNomePessoa()); } } Figura 11 - Saída do console / Fonte: os autores. U N IC E S U M A R 69 Não há limite para a criação de construtores, você pode criar, em uma classe, quantos forem necessários, mas o que deve diferenciar os construtores são os parâmetros. Por exemplo, se um construtor requer uma String, então, não se pode criar outro construtor solicitando uma String, por mais que sejam variáveis dife- rentes, porém, se for um construtor solicitando uma String, e outro, duas Strings, é possível, ou seja, o tipo e o número de parâmetros serão os determinantes para que o compilador saiba qual construtor deve ser chamado. Um ponto importante é que, enquanto o construtor não for executado, ne- nhum acesso à variável de instância ou método será possível, isto quer dizer que um objeto não pode ser utilizado até ser inicializado, o que é óbvio, mas, também, significa que você não pode tentar sabotar o construtor do objeto antes de chamar super(), como pode ser visto no código a seguir: package cliente; import java.util.Scanner; public class Fisica extends Pessoa{ private String cpf; public Fisica(){ System.out.println("Pessoa Fisica"); } public Fisica(String nome){ nomePessoa = nome; super(); //error } } //...outros métodos da classe } A chamada de super(), no construtor da classe Física, retorna um erro de compi- lação, em que será mostrado ao programador uma mensagem de que a chamada ao construtor de super() deve ser, no construtor, a primeira sentença. U N ID A D E 2 70 CONSIDERAÇÕES FINAIS Nesta unidade, aprendemos os conceitos dos pilares da orientação a objetos, o encapsulamento. É por ele que se possibilita garantir os níveis de proteção dos atributos e métodos com o uso dos modificadores de acessos: public, private e protected. Para acessar os atributos e métodos que estão encapsulados, utilizamos os modificadores privados por meio dos métodos públicos getters e setters. Além dos modificadores de acesso, aprendemos a utilizar o modificador final para criar constantes, métodos que não podem ser sobrescritos e classes que não podem ter filhos. Sendo assim, esse modificador é importante, pois ele pode ser utilizado para garantir a esterilidade de uma classe ou de um método. Vimos o modo de funcionamento do modificador static, que compartilha o mesmoespaço de memória para atributos desse tipo e, também, como os métodos estáticos são alocados em memória antes da instância da classe e que podem ser chamados utilizando, somente, o nome da classe e do método. O es- tático é muito utilizado para realizar validações de campos, pois nem sempre é necessário instanciar uma classe inteira, sendo assim, invoca, apenas, o método que realizar a operação necessária. Não menos importante, aprendemos sobre o modificador abstract, que faz com que a classe não possa ser instanciada, e os métodos abstratos, apenas, in- dicam o que deve ser implementado nas classes filhas. Dessa forma, temos que as classes abstratas servem de modelo para as demais classes que as estendem. Por fim, vimos como funcionam os construtores em Java, assim como a forma de inicializar os seus objetos utilizando tais construtores. Estes podem ser inicializados vazios ou com valores passados por parâmetros ou por valor padrão. Recurso este muito utilizado quando é necessário instanciar valores padrões na classe, mas cuidado: esses valores devem ser essenciais para todo o código, caso contrário, sempre que instanciado, a classe deverá fornecer valores a métodos que não os utilizam. Na próxima unidade, apresentaremos a você os conceitos que envolvem outro pilar da programação orientada a objetos: a herança. Este recurso fornece subsídios que permitem herdar características e comportamentos de classes antecessoras. Tal processo permite o reuso de códigos e, ao mesmo tempo, a sua personalização, incrementando o que é necessário, além daquilo já herdado de seu antecessor. 71 na prática 1. Os modificadores de acesso são elementos que envolvem a programação orien- tada a objetos. A partir destes, é possível ocultar detalhes internos de uma classe, como atributos (variáveis) e métodos (funções) de um objeto, ou seja, aplicar o conceito de encapsulamento na prática. Com esses modificadores, é possível de- terminar quais níveis de visibilidade que um atributo ou um método possui em relação à sua classe e às demais do projeto. Os modificadores de acesso disponí- veis são: public, private e protected. Sobre os modificadores de acesso no encapsulamento de atributos e métodos, analise as afirmações a seguir: I - Um atributo com o modificador private permite ser acessado por um método público de uma classe que estende a sua classe. II - Um atributo com o modificador public permite ser acessado diretamente por qualquer classe que a instancie, ou que tenha estendido de sua classe. III - Um atributo com o modificador protected permite ser acessado por um método público de uma classe que não estende a sua classe. IV - Uma forma de acessar os atributos com modificadores private e protected é imple- mentar os métodos assessores, desde que estes estejam com visibilidade public. Assinale a alternativa correta: a) Apenas I está correta. b) Apenas I e II estão corretas. c) Apenas II e IV estão corretas. d) Apenas I, II e IV estão corretas. e) Apenas I, III e IV estão corretas. 72 na prática 2. Com base em nossos estudos, analise o código a seguir: package cadpessoa; public class Pessoa { private String pai; public String getPai() { return pai; } public void setPais(String pai) { this.pai = pai;} public Pessoa (String pai){ this.setPais(pai); System.out.println("Meu pai é:"+this.getPai()); } public static void main(String[] args) { ____________ } } De acordo com o código anterior, assinale a alternativa que mostra a instância da classe no objeto pessoa1 e passe, por parâmetro, o valor "José": a) pessoa1 = new Pessoa("José"). b) Pessoa pessoa1 = new Pessoa("José"). c) Pessoa pessoa1 = new cadPessoa("José"). d) cadPessoa pessoa1 = new Pessoa("José"). e) cadPessoa.pessoa1 = new Pessoa("José"). 73 na prática 3. Analise o código fonte a seguir: package cadpessoa; public class Resumo { public String conteudo = "Estou estudando o "; public String getConteudo() { return conteudo;} public void setConteudo(String conteudo) { this.conteudo = conteudo; } public static void main(String[] args) { Resumo obj = new Resumo(); System.out.println(obj.getConteudo()); obj.setConteudo("resumo de Matemática"); System.out.println(obj.getConteudo()); } } De acordo com a classe anterior, assinale a alternativa correta para a saída em tela. a) Estou estudando o Estou estudando o b) resumo de Matemática Estou estudando o c) resumo de Matemática resumo de Matemática d) Estou estudando o resumo de Matemática e) Estou estudando o resumo de Matemática 74 na prática 4. Analise a imagem a seguir e identifique qual é a classe, quais são os atributos e os métodos. 5. Depois que Romeu terminou o sistema de PetShop, ele percebeu que esqueceu de implementar um dos recursos de orientação a objetos, e este mecanismo está causando sérios transtornos de controle de acesso, ou seja, o mecanismo que ele esqueceu provê proteção de acesso aos membros internos de um objeto. Diante do contexto anterior, cite qual foi o mecanismo que Romeu esqueceu de implementar e como ele poderá fazer esta implementação. Casa - janela : int - porta : int - quartos : int + inserir() : void + listar() : void + excluir() : void 75 aprimore-se ORIENTAÇÃO A OBJETOS Inovações tecnológicas surgidas na área de Informática têm criado uma necessida- de de utilização e manipulação de informações que antigamente não eram utiliza- das. Os tipos de dados complexos, como os objetos, passaram a ser manipulados através das linguagens de programação, que passaram a receber a conotação de Linguagem de Programação Orientada a Objetos. A programação estruturada, em se tratando, principalmente, de manutenção de sistemas, possui taxas de reutilização muito baixas, dificultando a manutenção dos programas anteriormente desenvolvidos. A orientação a objetos tem como objetivo principal modelar o mundo real, e garantir que as taxas de manutenibilidade (manutenção) serão maiores diante deste contexto. Isso é possível, pois utilizando uma linguagem de programação orientada a objetos con- segue-se obter um desenvolvimento mais rápido, visto que este desenvolvimento ocorre em módulos, em blocos de códigos correspondentes aos objetos e seus acoplamentos. Através da orientação a objetos pode-se obter uma maior qualidade e agilidade no desenvolvimento, pois o fator reusabilidade (reutilização) permite que se re-uti- lize outros objetos que foram anteriormente desenvolvidos e podem ser facilmente incorporados na aplicação. A reusabilidade também garante uma manuseabilidade melhor do programa, pois os testes referentes aos componentes, já foram previa- mente executados, garantindo assim a utilização coesa dos objetos. A orientação a objetos surgiu na década de 60, baseada na Teoria dos Tipos de Álgebra, mas somente na década de 90 começou a ser amplamente utilizada computacionalmente. Ela tem como princípio fundamental representar o mundo real e a forma de se interagir com os objetos. Mas, o que é um objeto? Atualmente, há milhares de milhares de objetos que se pode tocar e perceber. Agora mesmo, você está tocando em um objeto denominado livro e posso garantir que deste livro que você está nas mãos, somente há um exem- plar. Há um modelo deste livro, que é responsável pela criação do mesmo em série, mas este livro, que está nas suas mãos, idêntico, que possui um grifo de lápis (caso você já tenha dado um) somente há um, este nas suas mãos. É esta a característica principal dos objetos, eles são únicos e somente há um único objeto no mundo. Fonte: Claro e Sobral (2008, p. 8). 76 eu recomendo! Java 8 – Ensino Didático. Desenvolvimento e Implementação de Aplicações Autor: Sérgio Furgeri Editora: Érica e Saraiva Sinopse: a Java tem se desenvolvido muito nos últimos anos, fato que a tem colocado entre as linguagens de programação mais usadas. Milhares de empresas no Brasil e no mundo têm se apri-morado e desenvolvido as mais diversas soluções. Isto tem ocasionado, cada vez mais, a procura por profissionais qualificados e certificados em Java. Adquirir co- nhecimentos acerca da orientação a objetos, juntamente com a linguagem Java, se faz necessário a qualquer profissional da área de computação. Este livro ex- plora as funcionalidades básicas disponíveis no Java 8, fornecendo conhecimen- to inicial para a elaboração e o desenvolvimento de aplicações nessa linguagem, apresentando os aspectos fundamentais relacionados à orientação a objetos, à criação da interface do usuário, às aplicações com arquitetura do tipo cliente/ servidor hospedadas na internet, ao acesso a banco de dados com MySQL e à geração de gráficos. Aborda a estrutura básica da linguagem e os seus pacotes fundamentais. Possui, também, exercícios ao final de cada capítulo para a fixação do aprendizado, além de outros materiais de apoio. livro 77 eu recomendo! Nesta aula de programação orientada a objetos (POO), aprenderemos quais são os seus três pilares e estudaremos o primeiro deles: o encapsulamento da POO. https://www.youtube.com/watch?v=x4JfzV0Wb5w Veja, na série “Começando aos 40”, conhecimentos básicos para iniciantes em programação. https://www.youtube.com/watch?v=sx4hAHhO9CY conecte-se Java – Ensino Didático. Desenvolvimento e Implementação de Aplicações Autor: Sérgio Furgeri Editora: Érica Sinopse: a obra aborda os aspectos essenciais da linguagem Java, considerando a versão 9, o que proporciona ao leitor o entendi- mento dos principais conceitos referentes à orientação a objetos, ao acesso ao banco de dados, à geração de gráficos e ao uso do Java na internet. Fornece noções essenciais voltadas à criação de aplicações do tipo cliente/servi- dor com acesso ao banco de dados MySQL, usando o container Java Tomcat 9. Também é compatível com JShell e com NetBeans. livro 3 HERANÇA E POLIMORFISMO PLANO DE ESTUDO A seguir, apresentam-se as aulas que você estudará nesta unidade: • O que é herança? • Como imple- mentar herança • O que é polimorfismo?. OBJETIVOS DE APRENDIZAGEM • Entender o conceito de herança • Aplicar o conceito de herança • Entender e aplicar o conceito de polimorfismo. PROFESSORES Dr. Edson Alves de Oliveira Junior Me. Andre Abdala Noel Me. Márcia Cristina Dadalto Pascutti Me. Rafael Alves Florindo Esp. Victor de Marqui Pedroso Esp. Janaina Aparecida de Freitas INTRODUÇÃO Caro(a) aluno(a), estudamos os conceitos de encapsulamento, modifica- dores de acesso e construtores, em Java, recursos estes que prezam pelo controle de acesso aos elementos da classe bem como o momento de par- tida delas. Nesta terceira unidade, estudaremos os conceitos de herança, polimorfismo e interface. Primeiramente, abordaremos a herança, a qual possibilita que classes com características e comportamentos semelhantes possam ser implemen- tadas de forma agrupada, ou seja, por meio de superclasses e subclasses (classes filhas). As subclasses são consideradas especializadas, pois terão tudo o que a classe pai possui, mas com suas características e seus com- portamentos próprios. Esse recurso, além de agrupar classes semelhantes, visa a reaproveitar o código, uma vez que, se as classes estão herdando as características e os comportamentos, é como se estivéssemos reescreven- do todos os códigos na classe filha. Contudo esta reescrita fica apenas em tempo de execução no momento da instância do objeto. Ao herdar os atributos e comportamentos, os níveis de visibilidades são herdados também. Dessa forma, faz-se necessária a implementa- ção dos métodos de acesso setters e getters para os modificadores de visibilidade private e protected. Na sequência, abordaremos o conceito de polimorfismo, que é aplicado nas classes herdeiras. Esse conceito é encontrado nos métodos que contêm a mesma assinatura de sua classe mãe. Nesse caso, o polimorfismo permite que seja reutilizado parte do método da classe pai, porém este é criado com comportamentos distintos, de acordo com a origem do objeto. Para mostrar a você como utilizar esses recursos, abordaremos alguns exemplos que empregam as técnicas de herança e polimorfismo. Um será o exemplo discutido durante a apresentação do tema, e o outro mostrará um projeto complementar desenvolvido com as técnicas citadas. U N ID A D E 3 80 1 O QUE É HERANÇA? Começaremos a nossa unidade mostrando uma perspectiva do mundo real. Imagine um peixe, como na Figura 1, a seguir. Qualquer espécie animal possui características próprias que definem, com precisão, em qual espécie o animal se enquadra. Utilizaremos como exemplo os peixes: eles possuem brânquias (para respirar), barbatana/nadadeira para a locomoção e boca para a alimentação. De maneira geral, todos os peixes possuem essas características, agora, se nos aprofundarmos no mundo dos peixes e analisarmos as características de cada espécie, como: tipo de escama, tamanho e coloração do animal, se ele é de água doce ou salgada, número de dentes, entre outras características, teremos uma infinidade de espécies que se diferem, como o peixe-palhaço, que possui carac- terísticas específicas à sua raça. Por exemplo, o tamanho reduzido, a coloração alaranjada, as manchas brancas e pretas distribuídas pelo seu corpo. Um filhote de peixe-palhaço possui as mesmas características de seus pais, avós, bisavós e outras gerações, ou seja, ele herda todas as características da raça e, além disso, ele possui todas as características comuns a qualquer peixe. U N IC E S U M A R 81 Figura 1 - Peixe-palhaço Mantendo o raciocínio, podemos dizer que um peixe-palhaço é uma raça da espécie peixe; se fôssemos transformar esse conceito na programação orientada a objetos, basta transformar o peixe-palhaço em uma classe de nome PeixePalhaco, e ela herdaria as características dos Peixes, representada pela classe Peixe. Dessa forma, podemos afirmar que o PeixePalhaco é uma subclasse da su- perclasse Peixe. Em outras palavras, objetos do tipo PeixePalhaco terão todas as características de objetos do tipo Peixe, porém terão algumas especificidades que os diferem de outros tipos de peixe. Veja, na Figura 2, o esquema de herança que ilustra o caso em tela. Figura 2 - Superclasse Peixe e respectivas subclasses / Fonte: os autores. PeixePalhaco - numeroListras : int PeixeBagre Tucunare Pacu Peixe - tipoPele: String - numDentes : int + nadar() : void + comer() : void U N ID A D E 3 82 Todas as espécies de peixes, ilustradas na Figura 2, herdam todos os atributos e métodos da classe Peixe, ou seja, herdam as suas características e os seus comportamentos se houver, e é este o papel da herança na orientação a objetos. De maneira geral, a herança permite a criação de novas classes (subclasses) a partir daquelas já existentes (superclasses), “herdando” características existentes na classe a ser estendida. Essa técnica implica em grande reaproveitamento de código existente, uma vez que não há a necessidade de reimplementarão de métodos já criados nas superclasses. Como você pode notar, criamos um projeto e, nele, criamos as classes a seguir: Em relação a superclasses e subclasses, podemos dizer que as segundas são especializações das primeiras que, por sua vez, são ge- neralizações de subclasses. Em ou- tras palavras, subclasses são mais especializadas do que as suas su- perclasses, as quais são mais gené- ricas. As subclasses herdam todas as características de suas superclasses, como suas variáveis, seus métodos e suas visibilidades. A linguagem Java permite o uso de herança simples, mas não permite a imple- mentação de herança múltipla, o que significa que uma classe pode herdar mé- todos e atributos de mais de uma classe. Para superar essa limitação, o Java faz uso de interfaces, conteúdo que estudaremos na Unidade 4. Em Java, a palavra que define que uma classe herda as característicasde outra é extends, e ela deve ser utilizada assim que a classe for criada. Veja, nos códigos a seguir, um exemplo que mostra onde deve ser empregada a palavra extends. package peixes; public class Peixe { //atributos private String tipoPele; private int numDentes; //métodos Figura 3 - Herança / Fonte: os autores. U N IC E S U M A R 83 public void nadar(){ System.out.println("Mecher as barbatanas"); } public void comer(){ System.out.println("Procurar comida e comer"); } public String getTipoPele() { return tipoPele; } public void setTipoPele(String tipoPele) { this.tipoPele = tipoPele; } public int getNumDentes() { return numDentes; } public void setNumDentes(int numDentes) { this.numDentes = numDentes; } } package peixes; public class PeixePalhaco extends Peixe { //implementação da classe peixe palhaco private int numeroListras; public int getNumeroListras() { return numeroListras; } public void setNumeroListras(int numeroListras) { this.numeroListras = numeroListras; } } U N ID A D E 3 84 Da forma como foram implementados esses códigos, a classe PeixePalhaco herda todos os atributos e métodos da classe Peixe, mas com a diferença de possuir um atributo a mais, numeroListras, que é uma especificidade do peixe-palhaço em relação a outros tipos de sua espécie. Ainda utilizando dois últimos códigos, elaboraremos, agora, uma classe prin- cipal que cria uma instância de peixe-palhaço em peixe1 e chama/invoca os seus métodos nadar() e comer(). package peixes; public class Principal { public static void main(String[] args) { PeixePalhaco peixe1 = new PeixePalhaco(); peixe1.nadar(); peixe1.comer(); } } No código a seguir, será produzido, no compilador, uma saída: Mexer barbatanas Procurar comida e comer Note que os métodos nadar() e comer() não foram implementados na classe PeixePalhaco, somente na classe Peixe. Pelo fato de PeixePalhaco ser uma sub- classe de Peixe, os métodos da classe serão herdados, automaticamente, por objetos do tipo PeixePalhaco. U N IC E S U M A R 85 2 COMO IMPLEMENTAR HERANÇA Agora que você já entendeu o conceito-base para herança, analisaremos a Figura 4, a seguir, que ilustra o projeto como um todo a partir de um diagrama de classes: Figura 4 - Projeto de caixa eletrônico / Fonte: os autores. Pessoa # nomePessoa : String # endereco : Endereco # telefone : String Fisica - cpf : String Juridica - cnpj : String Juridica - saldo : float - numConta : int - titular : Pessoa - tipoConta : int Endereco - rua : String - numero : int - cidade : String - uf : String Caixa Eletronico - c : Conta - contasCadastradas : ArrayList<Conta> + sacar(valorSaque : float) : void + depositar(valorDep: float) : void + verificarSaldo() : void + transferir(numContaDestino : Conta, valor : float) : void U N ID A D E 3 86 Baseando-se no problema de que, em um banco, existem dois tipos de pessoas: física e jurídica, podemos projetar os nossos objetos como a classe Pessoa, a qual seria a superclasse, e as classes Pessoa Física e Pessoa Jurídica como subclasses. Além disso, podemos criar a classe Pessoa como abstrata, já que este nome não se refere a ninguém especificamente, só de forma genérica. Sendo assim, teremos os seguintes códigos para cada classe Pessoa: package cliente; import java.util.Scanner; public abstract class Pessoa { protected String telefone; protected String nomePessoa; protected Endereco e = new Endereco(); public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("Digite o nome"); nomePessoa = tec.nextLine(); System.out.println("Digite o telefone"); telefone = tec.nextLine(); e.cadastra(); } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public String getNomePessoa() { return nomePessoa; } public void setNomePessoa(String nomePessoa) { this.nomePessoa = nomePessoa; U N IC E S U M A R 87 } public Endereco getE() { return e; } public void setE(Endereco e) { this.e = e; } } Para a classe Física, temos: package cliente; import java.util.Scanner; public class Fisica extends Pessoa { private String cpf; //sobrescrita do método cadastra @Override public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("--- Cadastro de Pessoa Física ---"); super.cadastra(); System.out.println("Digite o cpf"); cpf = tec.nextLine(); } public String getCpf() { return cpf; } public void setCpf(String cpf) { this.cpf = cpf; } } U N ID A D E 3 88 Para a classe Jurídica, teremos: package cliente; import java.util.Scanner; public class Juridica extends Pessoa{ private String cnpj; //sobrescrita do método cadastra @Override public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("--- Cadastro de Pessoa Jurídica ---"); super.cadastra(); System.out.println("Digite o cnpj"); cnpj = tec.nextLine(); } public String getCnpj() { return cnpj; } public void setCnpj(String cnpj) { this.cnpj = cnpj; } } Para a classe Endereço, teremos: package cliente; import java.util.Scanner; public class Endereco { private String rua; private int numero; private String cidade; private String uf; U N IC E S U M A R 89 public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("--- Cadastro do Endereço ---"); System.out.println("Digite o nome da rua"); rua = tec.nextLine(); System.out.println("Digite o número do estabeleci- mento"); numero = Integer.parseInt(tec.nextLine()); System.out.println("Digite o nome da cidade"); cidade = tec.nextLine(); System.out.println("Digite o nome da Sigla do Es- tado"); uf = tec.nextLine(); } //implementar os métodos setters e getters } Segue a classe Conta: package conta; import cliente.Pessoa; import java.util.Scanner; public class Conta{ private double saldo; private int numConta; private Pessoa titular = new Pessoa(); private int tipoConta; public Conta() { this.setSaldo(0.0); } public double getSaldo() { return saldo; U N ID A D E 3 90 } public void setSaldo(double saldo) { this.saldo = saldo; } public int getNumConta() { return numConta; } public void setNumConta(int numConta) { this.numConta = numConta; } public int getTipoConta() { return tipoConta; } public void setTipoConta(int tipoConta) { this.tipoConta = tipoConta; } public Pessoa getTitular() { return titular; } public void setTitular(Pessoa titular) { this.titular = titular; } public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("Digite o número da conta"); numConta = Integer.parseInt(tec.nextLine()); U N IC E S U M A R 91 Lembrando um pouco a teoria dos modificadores, temos que os atributos da classe Pes- soa estão indicados com o modificador de acesso protected, e isto significa que as classes filhas de Pessoa terão acesso a esses atributos de forma direta. Fonte: os autores. explorando Ideias System.out.println("Tipo da conta: [1 - Conta Corrente][2 - Conta Poupanca]"); tipoConta = Integer.parseInt(tec.nextLine()); System.out.println("Dados da Pessoa"); titular.cadastra(); } public void imprimeC(){ System.out.println("Número da Conta: "+get- NumConta()); if(getTipoConta()==1){ System.out.println("Tipo de conta: Conta Corrente");}else if(getTipoConta()==2){ System.out.println("Tipo de conta: Conta Poupança"); } titular.imprime(); } } Note que, no momento de declaração das classes Física e Jurídica, existe a palavra reservada extends, e é ela que indica se uma classe é ou não subclasse de outra. Dessa forma, temos que as classes Física e Jurídica são filhas da classe Pessoa e, neste momento, todos os atributos e métodos implementados em Pessoa, auto- maticamente, farão parte das classes filhas. U N ID A D E 3 92 Volte no código da classe Pessoa e confira quais são os atributos e métodos dessa classe. Feito isto, você já sabe quais são os atributos que a classe Física possui: te- lefone, nomePessoa, endereço e CPF. Assim como os atributos da classe Jurídica: telefone, nomePessoa, endereço e CNPJ. Além dos atributos, as classes, também, possuem os métodos implementados na classe Pessoa. O código, a seguir, mostra quais são os métodos e atributos visíveis dentro da classe Física. Agora, preste atenção ao código a seguir, quando precisamos “chamar” al- gum método que faz parte da superclasse Pessoa. Note que é necessário utilizar a palavra reservada super, que nos dá acesso aos métodos e atributos da classe pai. Além disso, quando criarmos uma instância de Física, podemos, também, ter acesso a todos os métodos da classe Pessoa, como pode ser visto no código a seguir (note que os métodos getters e setters da classe Pessoa têm visibilidade pública por meio da classe Física, que, também, é uma classe Pessoa). package caixaeletronico; import cliente.Fisica; public class CaixaEletronico { public static void main(String[] args) { Fisica f = new Fisica(); f.cadastra(); System.out.println(f.getNomePessoa()); } } O mecanismo de herança nos fornece um benefício notável no desenvolvimento de aplicações. Ao concentrarmos características comuns em uma classe e derivar- mos as classes mais específicas a partir dela, nós estamos preparados para a adição de novas funcionalidades ao sistema. Se mais adiante, uma nova propriedade comum tiver que ser adicionada, não precisaremos efetuar alterações em todas as classes. Basta alterar a superclasse e todas as outras classes derivadas serão, automaticamente, atualizadas. U N IC E S U M A R 93 3 O QUE É POLIMORFISMO? Em Java, o conceito de polimorfismo se manifesta apenas nas chamadas dos métodos. A possibilidade de polimorfismo se dá pelo fato de que métodos podem ser sobrescritos pelas subclasses (métodos com o mesmo nome e números de argumentos), ou seja, se o método da superclasse não é suficiente ou não se aplica à classe filha, ele pode ser escrito novamente, tendo comportamento completamente diferente ao da superclasse. Fonte: os autores. conceituando Polimorfismo significa várias (“poli”) formas (“morfo”). Em orientação a objetos, polimorfismo é a capacidade pela qual duas ou mais classes derivadas da mesma superclasse podem invocar métodos que têm a mesma identificação e assinatura (o mesmo nome de método), mas que possuem comportamentos distintos (de acordo com a forma de implementação em cada subclasse). O interpretador Java se encarrega de chamar, corretamente, o método a ser execu- tado em tempo de execução. Existe, ainda, um mecanismo de sobrecarga em que dois métodos de uma classe podem ter o mesmo nome, porém com assinaturas diferentes (tipos de retorno ou tipos de argumentos diferentes), entretanto, essa sobrecarga não recebe o nome de polimorfismo. U N ID A D E 3 94 Como dito anteriormente, tal situação não gera conflito, pois o compilador é capaz de detectar qual método deve ser escolhido a partir da análise dos tipos dos argumentos do método. Nesse caso, diz-se que ocorre a ligação prematura para o método correto. Em Java, todas as determinações de métodos a executar ocorrem por meio da ligação tardia (ocorrência em tempo de execução), exceto em dois casos: métodos final, que não podem ser redefinidos, e private, que, também, não podem ser redefinidos e, portanto, possuem as mesmas características de métodos final. Para que o polimorfismo seja implementado de maneira correta, é necessário que os métodos tenham exatamente a mesma identificação (assinatura), sendo utilizado o mecanismo de redefinição de métodos, que é o mesmo de sobrescrita (@Override) de métodos em classes derivadas. A redefinição ocorre quando um método, cuja assinatura já tenha sido especificada, recebe nova definição, ou seja, um novo corpo em uma classe derivada. É importante observar que, quando o polimorfismo é utilizado, o comporta- mento que será adotado por um método será definido apenas durante a execu- ção. Embora, em geral, este seja um mecanismo que facilita o desenvolvimento e a compreensão do código orientado a objetos, há algumas situações em que o resultado da execução pode ser não intuitivo. Para ilustrar todo este processo, verificaremos o método cadastra, que está presente nas três classes do nosso exemplo (Pessoa, Física e Jurídica), e acrescen- tar, nessas classes, a implementação do método imprime. O código, a seguir, indica a classe abstrata Pessoa, que possui atributos protegidos (podem ser visualizados por seus herdeiros) e métodos de cadastra() e imprime() implementados. package cliente; import java.util.Scanner; public abstract class Pessoa { protected String telefone; protected String nomePessoa; protected Endereco e = new Endereco(); public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("Digite o nome"); nomePessoa = tec.nextLine(); System.out.println("Digite o telefone"); U N IC E S U M A R 95 telefone = tec.nextLine(); e.cadastra(); } public void imprime(){ System.out.println("Nome: "+getNomePessoa()); System.out.println("Telefone: "+getTelefone()); } //métodos setters e getters } No código a seguir, temos a implementação da subclasse Física, que conta com a implementação do método cadastra() e imprime(). Note que, antes da assina- tura dos métodos, existe uma anotação (@Override) indicando que o método está sobrescrevendo outro. Caso haja a necessidade de utilização do método da superclasse, é necessário invocá-lo como acontece na linha “super.cadastra()”. Dessa forma, o método da classe Pai, também, será executado. package cliente; import java.util.Scanner; public class Fisica extends Pessoa { private String cpf; //sobrescrita do método cadastra e imprime @Override public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("--- Cadastro de Pessoa Física ---"); super.cadastra(); System.out.println("Digite o cpf"); cpf = tec.nextLine(); } @Override public void imprime(){ System.out.println("Pessoa Física!"); super.imprime(); U N ID A D E 3 96 System.out.println("Nome: "+getCpf()); } //métodos setters e getters } A implementação da classe Jurídica é análoga à implementação da classe Física, a diferença são as mensagens que serão apresentadas ao usuário do aplicativo e o atributo CNPJ, existente somente na classe Jurídica. O código, a seguir, mostra a implementação da classe Jurídica. package cliente; import java.util.Scanner; public class Juridica extends Pessoa{ private String cnpj; //sobrescrita do método cadastra e imprime @Override public void cadastra(){ Scanner tec = new Scanner(System.in); System.out.println("--- Cadastro de Pessoa Jurídica ---"); super.cadastra(); System.out.println("Digite o cnpj"); cnpj = tec.nextLine(); } @Override public void imprime(){ System.out.println("Pessoa Física!"); super.imprime(); System.out.println("Nome: "+getCnpj()); } //métodos setters e getters } Após implementarmos as classes filhas (Física e Jurídica) e os métodos sobrescri- tos (cadastra() e imprime()),desenvolveremos,no método principal do projeto, no qual se encontra na classe CaixaEletronico, que está no pacote caixaeletronico, U N IC E S U M A R 97 um menu de opções. O objetivo do desenvolvimento desse menu é possibilitar a manipulação dos objetos do tipo Pessoa, oferecendo a opção para que o usuário do aplicativo selecione qual o tipo de Pessoa que deseja cadastrar. O menu funcionará da seguinte forma: 1. Caso escolha a opção 1, será criado um objeto do tipo Física dentro do vetor de Pessoas. 2. Caso a opção escolhida seja o item 2, um objeto do tipo Jurídica será criado. 3. Caso o usuário queira imprimir os elementos cadastrados até então, ele pode selecionar a opção número 3. Note que o método cadastra do é chamado, apenas, uma vez, isto é possível por- que as subclasses sobrescrevem um método da superclasse e, assim, esse método pode ter comportamentos diferentes de acordo com o objeto de origem, ou seja, isto é o polimorfismo! Confira, a seguir, a implementação deste código: package caixaeletronico; import cliente.Fisica; import cliente.Juridica; import cliente.Pessoa; import java.util.Scanner; public class CaixaEletronico { static Pessoa p[] = new Pessoa[10]; public static int ultimo = 0; public static void main(String[] args) { Scanner tec = new Scanner(System.in); int opcao = 0 ; while(opcao!=4){ System.out.println("Digite: "); System.out.println("1 - Cadastrar Pessoa Física"); System.out.println("2 - Cadastrar Pessoa Jurídica"); System.out.println("3 - Imprimir Clientes"); System.out.println("4 - Sair"); U N ID A D E 3 98 opcao = tec.nextInt(); tec.nextLine(); switch(opcao){ case 1: p[ultimo] = new Fisica(); break; case 2: p[ultimo] = new Juridica(); break; case 3: imprimeP(); break; case 4: System.out.println("Sair..."); break; default: System.out.println("Valor inválido"); break; } if(opcao==1 || opcao ==2){ p[ultimo].cadastra(); ultimo++; } } } } Outra aplicação do polimorfismo, ainda no exemplo em questão, é com o método de impressão, como todas as classes filhas, que, também, sobrescrevem o méto- do imprime da superclasse. Este método pode ser chamado e se comportará de acordo com o objeto de origem. Mas se não houver sobrescrita do método? Como o compilador se compor- ta? Nestes casos, ele invoca o método da classe Pai e executa-o. Veja, no código a seguir, a implementação do método de impressão. Note que não é necessário verificar qual tipo de Pessoa que precisa ser impressa, em tempo de execução, isto é resolvido. Coloque este método antes do método main(); U N IC E S U M A R 99 public static void imprimeP(){ for(int i = 0; i < ultimo; i++){ p[i].imprime(); } } Estudo de Caso – Animais A seguir, mostraremos um projeto que implementa herança e polimorfismo e, dessa forma, ilustrará os conceitos aprendidos até aqui. Os animais do zoológico da cidade de São Paulo precisam ser catalogados pela idade e pelo nome. O zoológico precisa que, além disso, sejam catalogadas as formas de locomoção dos animais, assim como o som que eles emitem, pois a ideia é transformar este software em algum elemento que mostre aos visitantes algumas curiosidades sobre os seres que ali vivem. Para isso, a administração do zoológico chamou Oswaldo, um programador Java, para desenvolver um soft- ware que auxiliasse nesta catalogação. Oswaldo, então, começou a fazer a análise dos requisi- tos e percebeu que se tratava de um caso em que a utilização de técnicas de poli- morfismo e herança era imprescindível para que o projeto pudesse ser expan- dido mais tarde, além de auxiliar no proces- so de manutenção. Dessa forma, Oswaldo criou uma classe abstrata chamada “Animal”, e nela colocou as informações principais solicitadas pelo zoológico, para a catalogação. Confira a modelagem na Figura 5, a seguir: No código a seguir, a classe Animal foi criada como uma classe abstrata, pois “Animal” é uma abordagem geral e precisa ser especificada. Essa classe possui dois atributos: nome Figura 5 - Exemplo de catalogação / Fonte: os autores. U N ID A D E 3 100 e idade do animal, além dos métodos abstratos seLocomove() e emiteSom(), que deverão, obrigatoriamente, serem implementados nas classes que herdarem a classe Animal. package animal; abstract public class Animal { private String nome; private int idade; abstract public void seLocomove(); abstract public void emiteSom(); public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public int getIdade() { return idade; } public void setIdade(int idade) { this.idade = idade; } } Então, Oswaldo criou as classes Cachorro, Cavalo e Preguiça, que herdam as propriedades de Animal e implementam os métodos abstratos. A seguir, apresentaremos os métodos desenvolvidos pelo programador para resolver esta necessidade do zoológico. A Classe Cachorro herda todos os atributos e métodos da classe Animal e é obrigada a implementar os métodos seLocomove() e emiteSom(). Note, ainda, que a classe cachorro possui um construtor que recebe, como parâmetro, o nome e a idade do Animal. O código a seguir ilustra a criação dessa classe. package animal; public class Cachorro extends Animal { public void Cachorro(String nome, int idade){ U N IC E S U M A R 101 super.setNome(nome); super.setIdade(idade); } @Override public void seLocomove(){ System.out.println("Cachorro: "+super.getNome()); System.out.println("Idade: "+super.getIdade()); System.out.println("Cachorro correndo, com suas 4 patas"); } @Override public void emiteSom(){ System.out.println("Au Au !"); } } A classe Cavalo, assim como a classe Cachorro, herda todos os atributos e métodos da classe Animal, além de ser obrigada a implementar os métodos seLocomove() e emiteSom(), porém a implementação difere da classe Cachorro, o que resulta em com- portamento diferente, isto é o polimorfismo dos métodos seLocomove() e emiteSom(). package animal; public class Cavalo extends Animal{ public void Cavalo(String nome, int idade){ super.setNome(nome); super.setIdade(idade); } @Override public void seLocomove(){ System.out.println("Cavalo: "+super.getNome()); System.out.println("Idade: "+super.getIdade()); System.out.println("Cavalga, Marcha, Trota"); } @Override public void emiteSom(){ System.out.println("Nhiiiiiiiiii ri ri rin !"); } } U N ID A D E 3 102 Por fim, temos, a seguir, a implementação da classe Preguiça de acordo com os padrões de implementação dos outros animais. package animal; public class Preguica extends Animal { public void Preguica(String nome, int idade){ super.setNome(nome); super.setIdade(idade); } @Override public void seLocomove(){ System.out.println("Bicho Preguiça: "+super.getNome()); System.out.println("Idade: "+super.getIdade()); System.out.println("Subindo na Árvore"); } @Override public void emiteSom(){ System.out.println("GRRRRRRRrrrrrr"); } } A seguir, temos a implementação da classe principal que cria e manipula o vetor de Animais. Note que, no código de criação do vetor, criam-se ponteiros para cada Animal, mas, ainda, não se sabe qual animal será colocado em cada posi- ção. Quem determinará o tipo do animal é o usuário, de acordo com a ordem de inserção por meio do método cadastraAnimal(). Este método questiona o usuário sobre qual tipo de animal será armazenado,sendo que o método fica encarregado de criar o objeto do animal desejado. Depois de cadastrado, é possível verificar qual animal ocupa cada posição do vetor, por meio do método imprimeAnimais(). Veja que cada objeto sabe exatamente como deve ser o seu comportamento ao chamar os métodos de seLocomove() e emiteSom(). package animal; import java.util.Scanner; public class AnimalPrincipal{ Animal vetAni[] = new Animal[30]; U N IC E S U M A R 103 static int tam=0; public static void main(String[] args) { int opcao=0; Scanner scan = new Scanner(System.in); AnimalPrincipal ap = new AnimalPrincipal(); while(opcao!=3){ System.out.println("Digite a opcao desejada:"); System.out.println("1 - Cadastrar"); System.out.println("2 - Listar"); System.out.println("3 - Sair"); opcao = scan.nextInt(); scan.nextLine(); switch(opcao){ case 1: ap.cadastraAnimal(); break; case 2: ap.imprimeAnimais();break; case 3: System.out.println("Saindo."); break; default: System.out.println("Opção inválida"); break; } } } public void cadastraAnimal(){ Scanner scan = new Scanner(System.in); System.out.println("Digite o tipo de animal:"); System.out.println("1 - Cachorro"); System.out.println("2 - Cavalo"); System.out.println("3 - Preguica"); U N ID A D E 3 104 int i = scan.nextInt(); scan.nextLine(); if (i==1 || i==3 || i==2){ System.out.println("Digite o nome do animal"); String n = scan.nextLine(); System.out.println("Digite a idade do animal"); int id = scan.nextInt(); scan.nextLine(); if (i==1) vetAni[tam]= new Cachorro(n,id); else if(i==2) vetAni[tam]= new Cavalo(n,id); else if(i==3) vetAni[tam]= new Preguica(n,id); tam++; } } public void imprimeAnimais(){ Scanner scan = new Scanner(System.in); for (int i=0;i<tam;i++){ System.out.println("Codigo do Animal: "+i); vetAni[i].seLocomove(); } System.out.println("Digite o código do animal que deseja ver"); int cod = scan.nextInt(); scan.nextLine(); vetAni[cod].seLocomove(); vetAni[cod].emiteSom(); } } Execute o projeto Animal e teste as funcionalidades das técnicas de herança e polimorfismo aqui apresentadas. U N IC E S U M A R 105 CONSIDERAÇÕES FINAIS Nesta unidade, estudamos conceitos importantes para a criação de projetos orientados a objetos. Herança e polimorfismos que ajudam, e muito, no desen- volvimento de aplicações que são transformadas do mundo real para o mundo computacional. Esses mecanismos possibilitam mais flexibilidade no projeto, expandindo, assim, a reutilização de códigos. Por meio da herança, vimos que é possível herdar métodos e atributos de clas- ses pai, e que o Java consegue identificar qual é o tipo de objeto trabalhado. Ainda sobre o recurso de herança, vimos que, ao aplicar a generalização, a visibilidade dos atributos e métodos não são alterados, ou seja, se o atributo da classe pai for público, ele permanece público. Dessa forma, evitamos a reescrita de códigos e, ainda, garantimos que os atributos e os métodos continuem protegidos pelo recurso de encapsulamento. A partir da implementação da herança, é possível aplicar o recurso de poli- morfismo, e percebemos a capacidade de sobrescrita do método, além de enten- dermos como um método, implementado em uma superclasse e sobrescrito nas subclasses, pode agilizar o desenvolvimento, uma vez que não se faz necessária a criação de mecanismos de identificação e desvios para “chamar” o método correto. Durante esta unidade, continuamos e desenvolvemos novos estudos de caso, com o objetivo de colocar em prática os conteúdos abordados no de- correr do texto. Na próxima unidade, abordaremos os conceitos de classes abstratas e interfaces, recursos estes estendidos ou implementados, respecti- vamente, nas classes do sistema. As classes abstratas servem como um modelo para as subclasses que a esten- dem, obrigando ou não a implementação de alguns métodos nas classes filhas. Enquanto a interface serve de contrato para as classes que a implementam, obri- gando-as a implementar os métodos da interface. 106 na prática 1. Crie uma classe chamada “Grupo” com um método imprimeDescricao() apenas im- primindo o texto “Grupo”. Depois, crie uma classe chamada SubGrupo, herdando de Grupo, use o polimorfismo e reescreva o método imprimeDescricao(). Este método deverá imprimir o texto “Sub-Grupo”. Crie uma classe chamada Produto, herdando de SubGrupo, use polimorfismo e reescreva o método imprimeDescricao(). Este método deverá imprimir o texto “Produto”. Obs.: use a anotação @override. 2. Faça uma análise da modelagem a seguir e implemente: 3. De acordo com a modelagem do exercício anterior, instancie as classes Livros e Revista, de modo que imprima os dados na função mostrarGeral() de forma independente. Materiais # assunto : string # titulo : string + mostrarGeral() : void Livros - editora : string - edicao : int - isbn : string - autor : string + mostrarGeral() : String Revista - colecao : String - editora : String + mostrarGeral() : String 107 na prática 4. Analise a classe a seguir. De acordo com classe Ouvidoria, implemente-a de forma que o usuário digite os valores de entrada e estas passem pelo método construtor da classe. Este deve ter implementado o toString(). 5. Analise a classe a seguir. De acordo com o contexto anterior, implemente a classe Pagamento de forma que o usuário possa entrar com as informações dos atributos. É necessário que instancie a classe em um vetor de quatro posições. Ouvidoria - nome : String - email : String - assunto : String + Ouvidoria(nome : String, email: String, assunto : String) : void + toString() : void Pagamento - valor : double - titulo : String - proprietario : String + inserir() : void 108 aprimore-se REVISITANDO A ORIENTAÇÃO A OBJETOS: ENCAPSULAMENTO NO JAVA Façamos uma aposta. Tenho certeza que você, ao ver a classe abaixo, consegue perceber um problema nela: class Pedido { public String comprador; public double valorTotal; // outros atributos } Sim. Os atributos estão todos públicos! Isso vai exatamente contra uma das nossas primeiras lições quando aprendemos Java: atributos devem ser privados e precisa- mos de getters e setters para acessá-los. Vamos então fazer essa mudança no código. class Pedido { private String comprador; public double valorTotal; // outros atributos public String getComprador() { return comprador; } public void setComprador(String comprador) { this.comprador = comprador; } public double getValorTotal() { return valorTotal; } public void setValorTotal(double valorTotal) { this.valorTotal = ValorTotal; } // outros getters e setters } 109 aprimore-se Agora está melhor, certo? Ainda não. Deixamos escapar na verdade o grande prin- cípio que está por trás da ideia de colocar atributos como privados. Do jeito que a classe Pedido está nesse momento, podemos fazer coisas como: Pedido p = new Pedido(); // muda valor do pedido para 200 reais! p.setValorTotal(p.getValorTotal() + 200.0); Mas onde está o problema? Imagine outras 10 classes que fazem a mesma coisa: de alguma forma, elas manipulam o valor total do pedido. Agora imagine que a regra de negócio do pedido mude: todo item comprado ganha desconto de 5% se o valor dele for superior a 1000 reais. Implementar essa mudan- ça não será tarefa fácil. Precisaríamos fazê-la em diferentes classes do sistema. Pedido Classe 1 Classe 2 Classe 3 Classe N todas elas manipulam valorTotal Pedido Classe 1 p.setValorTotal(....) a mudança deverá ser feita em todas elas! Classe 2 p.setValorTotal(....) Classe 3 p.setValorTotal(....) Classe N p.setValorTotal(....)110 aprimore-se Quanto tempo demoraremos para mudar o sistema? Não sabemos exatamente aonde devemos fazer as mudanças já que elas estão espalhadas pelo código. Esse, aliás, é um dos grandes problemas de códigos legados: uma simples mudança pre- cisa ser feita em tantas classes e, na prática, sempre esquecemos algum ponto, e nosso sistema frequentemente quebra. A classe Pedido não foi bem desenhada. Demos acesso direto ao atributo valor- Total, um atributo importante da classe. Veja que o modificador private nesse caso não adiantou de nada, já que demos também um setter para ele. Vamos tentar dimi- nuir o acesso ao atributo, criando métodos mais claros para a operação de depósito: class Pedido { private String comprador; public double valorTotal; // outros atributos public String getComprador() { return comprador; } public double getValorTotal() { return valorTotal; } public void adiciona(Item item) { if(item.getValor() < 1000) this.valorTotal += item.getValor(); else this.valorTotal += item.getValor() * 0.95; } } Agora, para adicionarmos um item no Pedido, faremos uso desse novo comportamento: Pedido p = new Pedido(); p.adiciona(new Item("Chuveiro Elétrico", 500.0)); Mas qual a diferença entre os dois códigos abaixo? 111 aprimore-se Item item = new Item("Super Geladeira", 1500.0); // antiga if (item.getValor() > 1000) { c1.setValorTotal(c1.getValorTotal() + item.getValor() * 0.95); } else { c1.setValorTotal(c1.getValorTotal() + item.getValor()); } // nova c1.adiciona(item); Veja que na primeira linha de código, sabemos exatamente COMO funciona a adi- ção de um novo item no pedido: devemos pegar o valor total e somar o valor novo com desconto de 5% se ele for maior que 1000. Já na segunda linha de código, não sabemos como esse processo funciona. Quando sabemos O QUÊ um método faz (igual ao método adiciona, sabemos que ele adiciona um item no pedido, por causa do nome dele), mas não sabemos exatamente como ele faz, dizemos que esse comportamento está encapsulado! A partir do momento que as outras classes não sabem como a classe principal faz o seu trabalho, significa que as mudanças ocorrerão apenas em um lugar! Afinal, elas estão escondidas (encapsuladas)! Pedido Classe 1 p.adiciona(....) a mudança agora acontece só no Pedido! Classe 2 p.adiciona(....) Classe 3 p.adiciona(....) Classe N p.adiciona(....) adiciona() está encapsulado! 112 aprimore-se Ou seja, para implementar a regra de negócios nova, bastaria mexermos em um único lugar: public void adiciona(Item item) { if (item.getValor() > 1000) this.valorTotal += item.getValor(); else this.valorTotal += item.getValor() * 0.95; // nova regra de negócio aqui } No fim, a real utilidade do private é esconder acesso de atributos que precisam ser acessados de maneira mais inteligente. Mas veja que de nada adianta colocar todos os atributos como private e criar getters e setters para todos eles. Deixamos o en- capsulamento “vazar” do mesmo jeito. Esconda os atributos, mas pense em comportamentos inteligentes para acessá- -los. Uma ótima maneira para saber se o comportamento está encapsulado é olhar para o código que faz uso dele! Se conseguirmos dizer o que o método faz, mas sem dizer como ele faz, então podemos afirmar que o comportamento está encapsulado! Muitas vezes deixamos esses princípios passarem. Se quiser revisitar essas e ou- tras boas práticas de Orientação a Objetos junto com os instrutores da Caelum, há mais posts por aqui, como um específico sobre esse problema dos getters e setters, o excesso de ifs e o relacionamento bidirecional entre classes. Quer praticar tudo isso com video aulas, respostas dos instrutores e correção dos seus exercícios? Con- fira nosso novo curso online de boas práticas de orientação a objetos! Fonte: Aniche (2012, on-line)1. 113 eu recomendo! Java – A Referência Completa Autor: Herbert Schildt Editora: Alta Books Sinopse: em Java – A Referência Completa, tradução da oitava edição, o autor best-seller de livros de programação, Herb Schildt, mostra o necessário para desenvolver, compilar, depurar e exe- cutar programas Java. Atualizado para esta plataforma, Edição Padrão 7 (Java SE7), o guia detalhado abrange toda linguagem Java, incluindo a sua sintaxe, as suas palavras-chave e os seus princípios fundamentais de progra- mação. Também serão encontradas, no livro, informações de elementos-chave da biblioteca Java API. JavaBeans, servlets, applets e swing são estudados e exemplos reais demonstram o Java em ação. Além disso, as novas funções do Java SE 7, como o try-with-resources, strings em switch, inferência de tipo com o operador diamante, NIO.2 e os Frameworks Fork/Join são discutidos em detalhes. livro 114 eu recomendo! Note que o uso de herança aumenta o acoplamento entre as classes, isto é, o quanto uma depende de outra. A relação entre as classes mãe e filha é muito forte e isto faz com que o programador das classes filhas tenha que conhecer a implementação da classe mãe e vice-versa. Desse modo, fica difícil fazer uma mudança pontual no sistema. Por exemplo, imagine se tivermos que realizar al- guma alteração em nossa classe Funcionário, mas não quiséssemos que todos os funcionários sofressem a mesma mudança. Precisaríamos passar por cada uma das filhas de Funcionário, verificando se ela se comporta como deveria ou se pre- cisamos sobrescrever o tal método modificado. Este é um problema da herança, e não do polimorfismo, o que resolveremos mais tarde, com a ajuda das interfaces. https://www.caelum.com.br/apostila-java-orientacao-objetos/heranca-reescrita- -e-polimorfismo/#polimorfismo A herança é um princípio da POO que permite a criação de novas classes a partir de outras previamente criadas. Estas são chamadas de subclasses, ou classes de- rivadas; e as já existentes, que deram origem às subclasses, são chamadas de su- perclasses, ou classes base. Desse modo, é possível criar uma hierarquia, tornan- do, assim, algumas classes mais amplas e outras, mais específicas. Uma subclasse herda métodos e atributos de sua superclasse; apesar disso, pode escrevê-los novamente para uma forma mais específica de representar o comportamento do método herdado. http://www.devmedia.com.br/entendendo-e-aplicando-heranca-em-java/24544 Veja, neste artigo, os conceitos, a utilização, a implementação e a importância do polimorfismo. Veremos, também, onde o polimorfismo é utilizado e de que forma ele pode ajudar a termos projetos melhores. http://www.devmedia.com.br/uso-de-polimorfismo-em-java/26140 conecte-se 4 CLASSES ABSTRATAS e Interfaces PLANO DE ESTUDO A seguir, apresentam-se as aulas que você estudará nesta unidade: • O que é classe abstrata? • O que são interfaces? • Comparando interfaces e classes abstratas. OBJETIVOS DE APRENDIZAGEM • Entender o que é classe abstrata • Entender o que é interface • Relacionar interfaces e classes abstratas. PROFESSORES Prof. Dr. Edson A. Oliveira Junior Prof. Me. Rafael Alves Florindo INTRODUÇÃO Caro(a) aluno(a), nesta unidade, compreenderemos, conceitual e pratica- mente, o que são classes abstratas e interfaces. Implementaremos, também, sistemas simples para cada tópico estudado. O objetivo focal é elucidar as dúvidas referentes a esses dois conceitos importantes no paradigma de programação orientada a objetos. Primeiramente, iniciaremos o nosso estudo com o recurso de classes abstratas. A finalidade de uma classe abstrata, em uma modelagem UML, é a de funcionar como um modelo para novas classes que poderão ser estendidas a partir dela, ou seja, para as suas subclasses. Ao contrário das interfaces, as classes abstratas podem conter campos (atributos) que não são static e final, e elas podem conter métodos não abstratos implementa- dos. Tais classes abstratas são semelhantes às interfaces, exceto porque que elas fornecem uma implementação parcial, deixando as subclassescom- pletarem a execução. Se uma classe abstrata contém, apenas, declarações de métodos abstratos, ela deve ser declarada como interface e não como uma classe abstrata. Na sequência, após internalizarmos os conceitos referentes às classes abstratas, estudaremos as interfaces. Estas podem ser implementadas por classes em qualquer lugar na hierarquia, quer estejam ou não relacionadas umas com as outras. Em comparação, a classe abstrata é uma subclasse de classes semelhantes (as partes implementadas da abstrata), mas que, também, tem algumas diferenças (os métodos abstratos). As interfaces não possuem atributos, apenas métodos e, dessa forma, cumprem com o papel de contrato entre o desenvolvedor e a modelagem. Após vermos os recursos apresentados, os colocaremos em prática, durante o nosso estudo, em algumas aplicações. Bom estudo! U N IC E S U M A R 117 1 O QUE É CLASSE ABSTRATA? Uma classe abstrata é desenvolvida para representar classes e conceitos abstratos. Ela é sempre uma superclasse que não permite que nenhum objeto seja criado a partir dela, ou seja, não pode ser instanciada. O uso das classes abstratas é dirigido para a construção de classes que constituirão um modelo, isto é, classes abstratas servirão como especificações básicas de novas, que serão implementadas por meio do mecanismo de herança. Assim, uma classe abstrata deve ser estendida, ou seja, deve ser a classe-base de outra, mais específica, que contenha os detalhes que não puderam ser incluídos na superclasse (abstrata). Para formalizar que uma classe seja abstrata, usamos a palavra reservada abstract antes da palavra reservada class (DEITEL; DEITEL, 2017). Vejamos um exemplo, na Figura 1: public abstract class ItemAbstrato { } Figura 1 - Classe abstrata / Fonte: os autores. Métodos abstratos são declarados com o modificador abstract. Se uma classe tiver algum método abstrato, ela, também, deverá, obrigatoriamente, ser declarada com o modificador abstract. Os métodos de uma classe abstrata, classificados como abstratos, devem terminar sempre com ; (ponto e vírgula), e a classe que a estender deverá implementá-los. Vejamos um exemplo, na Figura 2, de método abstrato: U N ID A D E 4 118 public abstract class ItemAbstrato { public abstract void cadastrar (); } Figura 2 - Classe abstrata / Fonte: os autores. Note que esses métodos não têm uma implementação, isto é, não possuem um corpo delimitado por chaves com qualquer código. Uma classe, também, pode ser declarada abstrata, mesmo que tenha um método não abstrato, ou a combinação de métodos abstratos e não abstratos. Veja o exemplo na Figura 3, a seguir: public abstract class ItemAbstrato { public abstract void cadastrar (); public void topo (){ System.out.println("Topo!"); } } Figura 3 - Classe abstrata / Fonte: os autores. Como, geralmente, as classes abstratas pertencem ao nível superior de uma hierarquia de classes, recomenda-se que contenham tanto código quanto possível, deixando para as suas subclasses, apenas, as implementações específicas dos métodos abstratos. É importante lembrar que uma classe abstrata que herda de outra não preci- sará fornecer implementação de todos os métodos abstratos herdados. Modelando um projeto com classes abstratas Agora que entendemos o que é classe abstrata, implementaremos um simples sistema de locadora de DVDs e CDs, utilizando os mais variados recursos que as classes abstratas oferecem. Lembrando que o sistema a ser desenvolvido é, meramente, instrutivo para que você possa compreender o funcionamento deste componente importante da linguagem orientada a objetos. É essencial observar que o uso do conceito de classes abstratas precisa ser mode- lado nas atividades de análise e design do projeto, caso contrário, se usarmos a técnica U N IC E S U M A R 119 de codificação direta, talvez, nunca apareça a necessidade de usar esse conceito. Na Figura 4, apresentamos a representação do diagrama de classe do sistema de locadora. Figura 4 - Diagrama de classe do sistema de locadora / Fonte: os autores. A modelagem anterior demonstra uma árvore de herança com uma classe abs- trata (ItemAbstrato) e duas concretas (Dvds e Cds). Observe que tanto a classe quanto os métodos abstratos estão representados com a fonte em itálico, e este comportamento representa um padrão nas modelagens UML. Implementando um Projeto com Classes Abstratas Seguiremos a modelagem da figura anterior e implementaremos um sistema simples de locadora de DVDs e CDs. Criando a classe abstrata ItemAbstrato Primeiramente, criaremos um projeto no NetBeans chamado ItemAbstrato. Des- marque o campo Criar Classe Principal; em seguida, crie uma classe nova com o nome ItemAbstrato e coloque como nome do pacote locadora. ItemAbstrato - codigo : int - titulo : String - dataEmprestimo : String - dataDevolucao : String - situacaoItem : String + cadastrar() : void + emprestar() : void + devolver() : void Dvd + Dvd() : void + vender() : void + cadastrar() : void + emprestar() : void + devolver() : void + imprimir() : void Cds + vender() : void + cadastrar() : void + imprimir() : void U N ID A D E 4 120 Os métodos set são, comumente, chamados de métodos modificadores porque, geral- mente, alteram um valor. Métodos get são, comumente, chamados métodos acessores ou métodos de consulta. Um método predicado testa se uma condição é verdadeira ou falsa. Fonte: Deitel e Deitel (2017, p. 277). conceituando Figura 5 - Criando a classe ItemAbstrato / Fonte: os autores. Agora que temos a nossa classe, insira a palavra abstract antes de class ItemAbs- trato e, também, os seguintes atributos e métodos abstratos da figura a seguir. Após isto, gere os respectivos gets e sets dos atributos. Figura 6 - Classe abstrata ItemAbstrato / Fonte: os autores. U N IC E S U M A R 121 Observe que a nossa classe ItemAbstrato apresenta a definição de uma classe abs- trata que representa um item de uma locadora. Nesta classe, definimos atributos e métodos comuns a um DVD e a um CD. Como a nossa intenção é analisar o comportamento de classes abstratas, não serão implementados, aqui, todos os atributos e objetos que poderiam existir em uma locadora real, mas, sim, o ele- mentar para compreendermos quando devemos usar classes abstratas. Criando a classe Dvd que herdará a classe ItemAbstrato Agora, criaremos outra classe chamada Dvd, que herdará a classe ItemAbstrato. Para isto, basta acrescentar a palavra-chave extends e, logo após, o nome da classe que desejamos herdar os métodos e atributos, no caso, ItemAbstrato. Veja, na figura a seguir, como ficou a nossa classe: Figura 7 - Classe Dvd / Fonte: os autores. Observe, na figura anterior, que o NetBeans IDE faz uma marcação no nome da classe Dvd, indicando que devemos implementar os métodos da classe pai, ItemAbstrato. Para isto, basta posicionar o ponteiro do mouse sobre o nome da classe e pressionar as teclas alt+enter para abrir o menu, que implementará todos os métodos automaticamente, ou você pode uti- lizar o alt+insert e selecionar quais métodos você deseja implementar. Lembrando que uma classe é obrigada a implementar todos os métodos abstratos da classe pai. U N ID A D E 4 122 Figura 8 - Classe Dvd / Fonte: os autores. Após gerar os métodos, a nossa classe Dvd ficará igual ao código a seguir: Figura 9 - Classe Dvd / Fonte: os autores. Observe que foram gerados todos os métodos abstratos da classe ItemAbstrato. Caso o NetBeans IDE gere, automaticamente, um código para cada método, basta igno- rá-los e, para isto, é só deletá-los ou comentá-los. Note que foi gerada uma anotação acima dos métodos: @Override. Embora não seja necessário usar essa anotação, re- comendamos que o faça, pois você terá a vantagem de o compilador verificar algum erro de ortografia e/ou erro na combinação dos parâmetros no método da classe pai e, também, tornará o seucódigo mais fácil de ser compreendido. U N IC E S U M A R 123 Agora, é necessário implementar o código de cada método. Como o nosso ob- jetivo é o estudo das classes abstratas, aqui, simplificaremos o código. Veja, a seguir, a implementação de um projeto com classes abstratas no código para cada método: package Locadora; import java.text.SimpleDateFormat; import java.util.Date; public class Dvd extends ItemAbstrato { SimpleDateFormat sdf = new SimpleDateFormat("dd/mm/ yyyy"); @Override public void cadastrar() { this.setCodigo(1); this.setTitulo("Senhor dos Anéis"); this.setSituacaoItem("L"); System.out.println( "\nDVD Cadastrado: " + "\nCod: " + this.getCodigo() + "\nTítulo: " + this.getTitulo() + "\nSituação: " + this.getSituacaoItem()); } @Override public void emprestar() { setSituacaoItem("E"); setDataEmprestimo(sdf.format(new Date())); System.out.println( "\nDVD Emprestado:" + this.getTitulo() + "\nSituação:" + this.getSituacaoItem() + "\nData empréstimo:" + this.getDataEm- prestimo()); } @Override public void devolver() { setSituacaoItem("L"); U N ID A D E 4 124 setDataDevolucao(sdf.format(new Date())); System.out.println( "\nDVD Devolvido:" + this.getTitulo() + "\nSituação:" + this.getSituacaoItem() + "\nData empréstimo: " + this.getDataDe- volucao()); } public void imprimir(){ System.out.println("Imprimir Lista de DVDs utilizando método concreto da classe DVD"); } } Observe, no código anterior, que utilizamos todos os atributos da classe ItemAbs- trato e, também, implementamos um novo método concreto na classe DVD, o im- primir( ). Note que estamos utilizando a classe SimpleDateFormat("dd/ mm/yyyy"), instanciando-a no objeto SimpleDateFormat sdf = new SimpleDateFormat("dd/mm/yyyy");. A classe SimpleDateFormat é usada para a formatação e a análise de datas. Permite formatar a data em texto, analisando o texto em data e a normalização. Depois de instanciada a classe, basta chamar o método format() e, dentro, passar um parâmetro para a função. Criando a classe CD que herdará a classe DVD Agora, criaremos mais uma classe chamada Cds, e estenderemos da classe DVD, conforme pode ser visto na Figura 10: Figura 10 - Classe Cds / Fonte: os autores. U N IC E S U M A R 125 Observe que, desta vez, o NetBeans não destacou o nome da classe forçando a implementação dos métodos da classe herdada, pois esta não é uma classe abs- trata, como é a classe ItemAbstrato. Porém, assim como a classe Dvd, que herda, diretamente, da classe abstrata, a classe Cds passa a ter a possibilidade de reutilizar todos os atributos e métodos da classe ItemAbstrato e Dvd bem como definir as suas particularidades e usá-las. Dessa forma, implementaremos a classe CDs conforme o código: package Locadora; public class Cds extends Dvd { @Override public void cadastrar() { this.setCodigo(1); this.setTitulo("The Best of Joy Division"); this.setSituacaoItem("L"); System.out.println( "\nCD Cadastrado: " + "\nCod: " + this.getCodigo() + "\nTítulo: " + this.getTitulo() + "\nSituação: " + this.getSituacaoItem()); } @Override public void imprimir(){ System.out.println("Imprimir Lista de CDs utilizando método concreto da classe DVD"); } public void vender(){ System.out.println("CD vendido utilizando mé- todo concreto da classe CDs"); } } Observe que estamos utilizando métodos da classe ItemAbstrato e, também, da classe Dvd, além de escrever métodos exclusivos da classe Cds, como o méto- do vender( ). A classe Cds herda esses métodos, pois eles são públicos e concretos. U N ID A D E 4 126 Regras das Classes e dos Métodos Abstratos Alguns pontos interessantes das imagens anteriores exemplificam algumas das regras de criação das classe e dos métodos abstratos, que são: ■ Os métodos construtores não podem ser declarados abstratos. Mesmo que a classe abstrata não possa ser instanciada, os seus construtores po- dem inicializar os campos da classe que serão usados por subclasses, o que é imprescindível em todos os casos, praticamente. ■ Métodos declarados como abstratos não podem ser privados (private). ■ Classes abstratas não podem conter métodos estáticos (static). ■ Os campos de uma classe abstrata serão herdados pelas classes des- cendentes e poderão ser usados por instâncias delas, a não ser que sejam declarados private. Criando a classe Programa para executar o nosso sistema Para finalizar o nosso sistema, criaremos mais uma classe com o método main, chamada Programa. Nela, criaremos um menu e um método de escolha para que possamos chamar todos os métodos implementados em nosso exemplo. A seguir, no código dessa classe: package Locadora; import java.util.Scanner; public class Programa { public static void main(String[] args) { int opcao; Dvd dvd = new Dvd(); Cds cds = new Cds(); while(true){ System.out.println(); System.out.println("------------------"); System.out.println("0 - Sair"); System.out.println("1 - Cadastrar DVD"); System.out.println("2 - Emprestar DVD"); U N IC E S U M A R 127 System.out.println("3 - Devolver DVD"); System.out.println("4 - Cadastrar CD"); System.out.println("5 - Vender CD"); System.out.println("6 - Imprimir CDs"); System.out.println("Opção: "); Scanner scan = new Scanner(System.in); opcao = scan.nextInt(); System.out.println("------------------"); if(opcao == 0){ System.exit(0); } switch(opcao){ case 1: dvd.cadastrar(); break; case 2: dvd.emprestar(); break; case 3: dvd.devolver(); break; case 4: cds.cadastrar(); break; case 5: cds.vender(); break; case 6: cds.imprimir(); break; } } } } U N ID A D E 4 128 Explicando o código Observe que criamos um laço de repetição com o método while( ), sendo a condição de parada a variável opção igual a 0. Fizemos uso, também, da classe java.util.Scanner, que permite a leitura de dados vindos do teclado. Agora que você já implementou o código anterior, execute o programa e visualize os resultados na saída do NetBeans IDE. Importante lembrar que o uso de classes abstratas é realizado com classes que têm métodos e atributos em comum ou, pelo menos, a maioria. Em nosso exemplo, a mídia DVD possui muito em comum com CDs. Além dos atributos, os métodos podem ser utilizados para ambas as classes. O nosso programa de locadora se limitou a cadastrar e a vender os CDs, o que não impediria de colo- cá-los para serem alugados, assim como os DVDs. Há uma série de situações, em engenharia de software, em que é importante que diferentes grupos de programadores concordem com um “contrato”, o qual, por sua vez, expõe a forma como o software interagirá. Cada grupo deve ser capaz de escrever o seu código, sem qualquer conhecimento de como o código do outro grupo está escrito. De modo geral, isto se aplica ao importante componente da orientação a objetos, conhecido como interfaces. 2 O QUE SÃO INTERFACES? U N IC E S U M A R 129 A interface é um recurso da orientação a objeto, utilizado em Java, que define ações, obrigatoriamente, executadas, mas que cada classe pode executar de forma diferente (DEITEL; DEITEL, 2017). A seguir, temos o exemplo de uma interface denominada InterfaceControle:public interface InterfaceControle { } Na linguagem de programação Java, uma interface é um tipo de referência semelhante a uma classe, que pode ter apenas constante, assinaturas de mé- todos e tipos aninhados, não há corpo de método. Interfaces não podem ser instanciadas, elas só podem ser implementadas por classes ou estendidas por outras interfaces. Por que isto acontece? Esse fenômeno se deve ao fato de que muitos objetos (classes) podem possuir a mesma ação (método), mas podem executá-la de maneira diferente. Usando um exemplo bem remoto, podemos ter uma interface chamada Elemen- toDiagrama, que possui a assinatura do método desenhar() e redimen- sionar(). Ou seja, toda classe de ElementoDiagrama que for implementada deve dizer que a implementação dos métodos desenhar() e redimensio- nar() funcionam. Portanto, se temos uma classe chamada Retangulo, e outra chamada Circulo, ambas implementando a interface ElementoDiagrama, então, nestas duas classes, devemos codificar a forma como cada um fará o desenhar() e o redimensionar(). Vejamos o diagrama de classe a seguir: Figura 11 - Implementação da interface ElementoDiagrama / Fonte: os autores. Retangulo - comprimento : double - altura : double + desenhar() : void + redimensionar() : void Circulo - raio : double + desenhar() : void + redimensionar() : void ElementoDiagrama <<interface>> + desenhar() : void + redimensionar() : void U N ID A D E 4 130 Características das interfaces De acordo com Deitel e Deitel (2017), uma interface tem as seguintes características: ■ Todos os métodos definidos são, implicitamente, do tipo public ou abstract. Por esta razão, ao declarar um método em uma interface, não é necessário fornecer a palavra-chave public. Figura 12 – InterfaceControle / Fonte: os autores. ■ Uma interface pode estender mais de uma interface. É importante lembrar que uma classe pode estender, somente, de outra classe. Figura 13 - Estendo InterfaceControle / Fonte: os autores. A classe que implementa uma interface deve, obrigatoriamente, implementar todos os métodos definidos na interface. Figura 14 - A obrigatoriedade de implementar uma interface / Fonte: os autores. Implementando os métodos, temos: Figura 15 - Implementando o método conectarBanco / Fonte: os autores. U N IC E S U M A R 131 ■ Uma interface é, formalmente, uma classe abstrata, somente com atributos constantes (final) e estáticos (static) e, ainda, métodos sem corpo. Estes deverão ser implementados pelas classes que implementarão a interface. É importante observar que os atributos, na interface, precisam ser inicializados. Vejamos um exemplo na Figura 16: Figura 16 - Inicializando variáveis estáticas na interface / Fonte: os autores. Voltando ao nosso exemplo de Interface ElementoDiagrama, crie um novo projeto Java, com o nome de ElementoDiagrama e, depois, crie três pacotes e as suas respectivas classes. Após ter criado a estrutura de pacotes e classes, vamos codificá-los. Iniciamos pela interface Ele- mentoDiagrama. Note que foram imple- mentados dois métodos: public void desenhar(); e public void re- desenhar(); ambos com (;) no final da assinatura do método. Dessa forma, as clas- ses que implementam esta interface deverão codificar estes métodos. package Interfaces; public interface ElementoDiagrama { public void desenhar(); public void redesenhar(); } Figura 17 - Estrutura de pacotes do projeto ElementoDiagrama. Fonte: os autores. U N ID A D E 4 132 Após codificada a interface anterior, temos a classe public class Retan- gulo, que está implementando a interface implements ElementoDia- grama, junto com a codificação dos métodos public void desenhar() {} e public void redesenhar(){}. package Classes; import Interfaces.ElementoDiagrama; public class Retangulo implements ElementoDiagrama{ @Override public void desenhar() { System.out.println("Desenhando um Retângulo"); } @Override public void redesenhar() { System.out.println("Redesenhando um Retângulo"); } } Após codificada a interface, temos a classe public class Circulo, que está implementando a interface implements ElementoDiagrama, junto com a codificação dos métodos public void desenhar(){} e public void redesenhar(){}. package Classes; import Interfaces.ElementoDiagrama; public class Circulo implements ElementoDiagrama{ @Override public void desenhar() { System.out.println("Desenhando um Círculo"); } @Override public void redesenhar() { System.out.println("Redesenhando um Círculo"); } } U N IC E S U M A R 133 Para testar a interface e as classes, editaremos a classe principal que está no pacote Programa. package Programa; import Classes.Retangulo; import Classes.Circulo; public class Principal { public static void main(String[] args) { Retangulo ret = new Retangulo(); Circulo cir = new Circulo(); ret.desenhar(); ret.redesenhar(); cir.desenhar(); cir.redesenhar(); } } Modelando um Projeto com Interfaces Após compreendermos as interfaces e o seu comportamento, implementaremos um simples sistema de cadastro de Blu-Rays, utilizando a interface como principal recurso. As interfaces são um conjunto de operações que definem os servi- ços de uma classe ou de um componente. Em nosso caso, as in- terfaces existirão ape- nas na classe. Veremos, a seguir, na Figura 18, o diagrama de classe do nosso projeto de cadastro de Blu-Rays. BluRay - listaBluRay : Vector + cadastrarBluRay(obj : ItemBluRay) : void ItemBluRay - preco : double - nomeCliente : String InterfaceBluRay <<interface>> + imprimirLista() : void + adicionarLista(obj : ItemBluRay) : void + PROMOCAO : double 0..*0 Figura 18 - Diagrama de classe do sistema cadastro de Blu-Ray. Fonte: os autores. U N ID A D E 4 134 A Figura 18 mostra as características básicas para representar interfaces. A linha tracejada e com uma ponta de flecha vazada demonstra que a classe Blu-ray implementa a interface. Outra forma de mostrar que uma classe implementa uma interface é dese- nhar uma linha com um círculo em uma das extremidades, com o nome dessa interface. As interfaces e os seus métodos, em Java, sempre são abstratos. Estra- nhamente, ambos não aparecem em itálico, como ocorre com as classes abstratas e os métodos abstratos em classes. Implementando um Projeto com Interfaces Seguiremos a modelagem da figura anterior e implementaremos um sistema de vendas de Blu-Rays. Criando a classe ItemBluRay Primeiramente, criaremos um projeto, no NetBeans, chamado BluRayIn- terface; assim, desmarque o campo Criar Classe Principal. Em seguida, crie uma classe nova com o nome ItemBluRay e coloque como nome do pacote BluRayInterface. Na classe ItemBluRay, criaremos dois atributos e implementaremos os métodos gets e sets. package BluRayInterface; public class ItemBluRay { private double preco; private String nomeCliente; public double getPreco() { return preco; } public void setPreco(double preco) { this.preco = preco; } U N IC E S U M A R 135 public String getNomeCliente() { return nomeCliente; } public void setNomeCliente(String nomeCliente) { this.nomeCliente = nomeCliente; } } Esta classe será o nosso objeto para armazenarmos os dados salvos em nosso cadastro. Criando a interface InterfaceBluRay Para criar uma interface, no NetBeans IDE, basta clicar com o botão direito do mouse sobre o pacote do projeto e, no menu, escolher Novo > Interface Java... e adicionar InterfaceBluRay como nome da interface. Em seguida, clique em Finalizar. Nesta interface, criaremos um atributo estático e dois méto- dos, conforme a figura a seguir: package BluRayInterface; public interface InterfaceBluRay { final static double PROMOCAO = 20; void adicionarLista(ItemBluRay obj); void imprimirLista(); } Observeque estamos utilizando um atributo do tipo final e static. A instrução final indica que a classe, o método ou a variável, assim declarada, tem uma única atribuição que se mantém constante, ou seja, não pode ser alterada no decorrer do processamento. A instrução static é utilizada para criar uma variável que poderá ser acessada por todas as instâncias de objetos desta classe como uma variável comum. Em nosso exemplo, atribuímos para a variável estática PROMOCAO o valor de 20, e ela será responsável por gerar o desconto em nosso sistema. U N ID A D E 4 136 A vantagem em ter um atributo estático e final é que, ao alterar a variável estática, todo o sistema receberá o novo valor atualizado. Observe que os méto- dos estão sem os modificadores de acesso public e, conforme apresentado ante- riormente, não é necessário declarar os modificadores de acesso, pois todos os métodos e atributos de uma interface são públicos. Criando a classe Blu-Ray e estendendo a interface Agora, crie a classe Blu-Ray e acrescente a palavra reservada implements e a nossa interface InterfaceBluRay. Observe abaixo que o NetBeans IDE destacou a nossa classe para que seja criado todos os métodos exigidos pela interface. Ela obriga o uso de todos os métodos declarados, porém é você quem decide como implementá-los. package BluRayInterface; import java.util.Scanner; import java.util.Vector; public class Bluray implements InterfaceBluRay{ private Vector<ItemBluRay>ListaBluray = new Vec- tor<ItemBluRay>(); @Override public void adicionarLista(ItemBluRay obj) { this.ListaBluray.add(obj); } @Override public void imprimirLista() { System.out.println("Lista de Blu Ray"); for(int i = 0; i < this.ListaBluray.size(); i++){ System.out.println(); System.out.println("Nome do Cliente"); System.out.println(this.ListaBluray.get(i).getNo- meCliente()); System.out.println("Preço do Blu-Ray"); System.out.println(this.ListaBluray.get(i).getPre- U N IC E S U M A R 137 co()); } System.out.println("--------------------"); System.out.println(); } } Nesta classe, criamos um vetor de ItemBluray para armazenar os objetos ItemBluRay e, assim, obter uma lista. O método adicionarLista() recebe, por parâmetro, o objeto salvo na classe Programa que será implementado a seguir. O método imprimirLista() apresenta a relação de BluRay salvos no programa. Ainda falta um método para realizarmos os cadastros de todos os Blu-Rays. Ele será criado diretamente na classe, pois ele não foi implementado na interface, conforme o código a seguir: package BluRayInterface; import java.util.Scanner; import java.util.Vector; public class Bluray implements InterfaceBluRay{ private Vector<ItemBluRay>ListaBluray = new Vec- tor<ItemBluRay>(); @Override public void adicionarLista(ItemBluRay obj) { this.ListaBluray.add(obj); } @Override public void imprimirLista() { System.out.println("Lista de Blu Ray"); for(int i = 0; i < this.ListaBluray.size(); i++){ System.out.println(); System.out.println("Nome do Cliente"); System.out.println(this.ListaBluray.get(i).getNomeCliente()); System.out.println("Preço do Blu-Ray"); U N ID A D E 4 138 System.out.println(this.ListaBluray.get(i).getPreco()); } System.out.println("--------------------"); System.out.println(); } public void cadastrarBlueRay(ItemBluRay obj){ Scanner scan = new Scanner(System.in); System.out.println("Nome do cliente"); obj.setNomeCliente(scan.nextLine()); System.out.println("Preço:"); obj.setPreco(scan.nextDouble()); } } O método cadastrarBluRay() recebe, por parâmetro, o objeto ItemBluRay da classe Programa, que será implementado a seguir. Criando a Classe Programa Para finalizar, criaremos a classe Programa para executar o nosso projeto. Confira o código para essa classe: package BluRayInterface; import java.util.Scanner; public class Programa { public static void main(String[] args) { Bluray objBluray = new Bluray(); ItemBluRay objItem; while(true){ objItem = new ItemBluRay(); System.out.println("1 - Cadastrar Blu-Ray"); System.out.println("2 - Imprimir lista de Blu-Ray"); U N IC E S U M A R 139 System.out.println("3 - Cadastrar Blu-Ray com Desconto"); System.out.println("4 - Fim"); System.out.println("Entre com a opção desejada: "); Scanner scan = new Scanner(System.in); int opcao = scan.nextInt(); if(opcao == 4) break; switch(opcao){ case 1: objBluray.cadastrarBlueRay(objItem); objBluray.adicionarLista(objItem); break; case 2: objBluray.imprimirLista(); break; case 3: objBluray.cadastrarBlueRay(objItem); System.out.println("Valor do Blu-Ray com promoção de: " + InterfaceBluRay.PROMOCAO); double valorDesconto = objItem.getPre- co() - (objItem.getPreco()* InterfaceBluRay.PROMOCAO)/100; objItem.setPreco(valorDesconto); objBluray.adicionarLista(objItem); System.out.println(); System.out.println(); break; default: System.out.println("Opção Inválida"); break; } } } } U N ID A D E 4 140 Explicando o código Na classe Programa, criamos uma instância da classe Blu-Ray, chamada objBlu- ray, lembrando que instância e objeto são sinônimos. A criação do objeto envolve o operador new(), que foi o utilizado no código anterior. Em seguida, criamos a variável do tipo ItemBluRay, chamado objItem. Criamos o método while(), que controla a saída ou a permanência no pro- grama e, logo em seguida, instanciamos a variável do tipo objItem, que será responsável por salvar as informações de cada item de Blu-Ray no objeto Bluray. Utilizamos, também, a classe Scanner() para adquirir todas as opções do menu digitadas pelo usuário. Observe que, por meio do objeto objBluray, temos acesso a todos os métodos da classe Bluray. E, na linha System.out.println("Valor do Blu- -Ray com promoção de: " + InterfaceBluRay.PROMOCAO); fazemos uso, pela primeira vez, do atributo estático que criamos na Interface- BluRay, lembra? Na linha double valorDesconto = objItem. getPreco() - (objItem.getPreco() * InterfaceBluRay. PROMOCAO) / 100; aproveitamos este recurso para realizar o cálculo de desconto ao cadastrar o Blu-Ray. Em nossa interface, definimos que o valor do desconto seria 20%. Caso, um dia, o desconto mude, basta alterar o valor da va- riável estática e todos os programas que fazem uso dela serão atualizados auto- maticamente. Pronto! Agora é só executar o programa e visualizar os resultados. U N IC E S U M A R 141 Se você leu a seção de classes abstratas, talvez esteja se perguntando por que os projetistas da linguagem Java se deram ao trabalho de introduzir o conceito de interfaces? Por que aquela que criamos, a InterfaceBluRay, não pode ser, sim- plesmente, uma classe abstrata? Há, infelizmente, um sério problema com o uso de uma classe básica abstrata para expressar uma propriedade genérica. Uma classe só pode estender uma única classe. Suponha que a classe Bluray já estende outra diferente, digamos, Midia. Ela, então, não poderá estender uma segunda classe, mas cada uma pode implementar quantas interfaces quiser. Outras linguagens de programação, especialmente o C++, permitem que uma classe tenha mais de uma superclasse. Esse recurso é chamado de herança múltipla. Os projetistas do Java optaram por não dar suporte à herança múltipla, porque ela torna a linguagem muito complexa (como no C++). Em vez disso, as interfacessuportam a maioria dos benefícios da herança múltipla e, ao mesmo tempo, evitam as complexidades e ineficiências. A seguir, mostramos um quadro comparativo para tornar mais fácil a com- preensão das diferenças e similaridades entre classes abstratas e interfaces. 3 COMPARANDO INTERFACES e classes Abstratas U N ID A D E 4 142 Característica Interface Classe abstrata Herança múltipla Uma classe pode implementar diversas interfaces. Uma classe pode herdar, somente, uma classe. Implementa- ção padrão Uma interface não pode conter qualquer tipo de código, muito menos código padrão. Uma classe abstrata pode for- necer código completo, código padrão ou ter apenas a declara- ção de seu esqueleto para ser, posteriormente, sobrescrita. Constantes Suporta, somente, constantes do tipo estática. Pode conter constantes está- ticas e de instância. Componentes de terceiros Uma implementação de uma interface pode ser incluída a qualquer classe de terceiros. Uma classe de terceiros precisa ser reescrita para estender somente a partir da classe abstrata. Homogenei- dade Se todas as diversas implemen- tações compartilham a assinatu- ra do método, então, a interface funciona melhor. Se as várias implementações são todas do tipo e comparti- lham um comportamento e um status comuns, então, a classe abstrata funciona melhor. Manutenção Se o código do seu cliente conver- sa somente em termos de uma interface, você pode, facilmente, alterar a implementação concreta usando um método factory. Idêntico. Velocidade Lento, requer trabalho extra para encontrar o método cor- respondente na classe atual. Rápido. Clareza Todas as declarações de cons- tantes, em uma interface, são, presumidamente, públicas ou estáticas. Você pode colocar código compartilhado em uma classe abstrata. Você pode usar códi- go para computar o valor inicial de suas constantes e variáveis de instância ou estáticas. Funcionalida- des adicionais Se você incluir um novo método em uma interface, você precisa ajustar todas as implementações dela. Se você incluir um novo método em uma classe abstrata, você tem a opção de fornecer uma implementação padrão para ele. Quadro 1 - Comparativo entre classe abstrata e interface / Fonte: os autores. U N IC E S U M A R 143 CONSIDERAÇÕES FINAIS Chegamos ao final de mais uma unidade e, nesta, abordamos as duas estruturas de importância na orientação a objetos, às classes abstratas e às interfaces. Estas estruturas são utilizadas para garantir uma estrutura coesa e íntegra no tocante ao desenvolvimento, agilizando ações corretivas ou de customização. Em relação à classe abstrata, nota-se que ela pode conter tanto métodos abstratos quanto não abstratos, mas, se ao menos um deles for marcado com o estereótipo abstract, a classe, também, deverá ser marcada como abstrata. Não esqueça que a subclasse concreta (não abstrata) de uma classe abstrata terá de fornecer implementações de todos os métodos abstratos da superclasse, mas que uma classe abstrata não tem de implementar os métodos abstratos de sua super- classe. Ainda, neste tópico, abordamos que uma classe abstrata nunca poderá ser instanciada, pois a sua única missão de vida é ser estendida. Além disso, abordamos a implementação de interfaces. Aqui, você aprendeu que as interfaces podem estender outra (até mesmo várias) e que qualquer classe que implementar uma interface terá de implementar os métodos de todas as ou- tras existentes na árvore de herança da interface que estiver sendo implementada. Sendo assim, temos que as classes abstratas podem conter atributos, desde que não sejam static e final, e elas podem conter métodos não abstratos implementados. Tais classes abstratas são semelhantes às interfaces, exceto aquelas elas fornecem uma implementação parcial, deixando as subclasses completarem a execução. As interfaces funcionam como um contrato de desenvolvimento cujos métodos não podem ser implementados na interface. Ao longo da unidade, trabalhamos com pequenos estudos de casos que visavam a nortear os seus estudos. Sendo assim, sugere-se que, a partir destes, criem-se novas soluções, pois, para um problema, podem surgir diversas soluções, desde que elas cheguem ao mesmo resultado. 144 na prática 1. Dado o código a seguir, assinale com V (verdadeiro) ou F (falso) as declarações que seguem referentes ao código a seguir: 1. abstract class Automovel { 2. abstract short metodo1 () ; 3. short metodo2() { return (short) 420; } 4. } 5. 6. abstract class MiniCarro extends Automovel { 7. // codigo faltando? 8. short metodo1() { return (short) 42; } 9. } ( ) O código será compilado sem alterações. ( ) A classe MiniCarro criará uma declaração abstract do metodo2() ou implemen- tará esse método para permitir que o código seja compilado. ( ) É válido, mas não necessário, que a classe MiniCarro crie uma declaração abstrata do método metodo2() ou o implemente para permitir que o có- digo seja compilado. ( ) Já que a linha 8 existe, a classe Automovel deve declarar o método metodo1(), de alguma maneira. ( ) Se a linha 6 fosse substituída por "MiniCarro extends Automovel { ", o código seria compilado. ( ) Se a classe Automovel não fosse abstrata, e o método metodo1() da linha 2 fosse implementado, o código não seria compilado. Assinale a alternativa correta: a) V - F - F - F - F - F. b) F - V - V - F - V - F. c) V - V - F - F - F - F. d) V - F - V - V - F - V. e) F - F - F - F - F - V. 145 na prática 2. Um dos recursos providos pela programação orientada a objetos, é a interface, devi- do à sua força de "gerenciamento", ou seja, as interfaces são entidades que definem os métodos que as classes que a implementam a interface devem implementar obrigatoriamente, ou seja, um contrato. Sabemos que não se tratam de classes em si, mas um modelo para suas implementações (classes que virão a configurar os seus métodos). Com base nestas informações, assinale com V (verdadeiro) ou F (falso) as afirmações a seguir. ( ) Toda classe que possui um método abstrato é, obrigatoriamente, uma classe abstrata. ( ) Uma classe abstrata pode ter métodos não abstratos (não marcados com a palavra-chave abstract). ( ) Para evitar erros de compilação, todo método de uma interface necessita ser marcado com a palavra-chave abstract. ( ) Tanto classes abstratas quanto interfaces necessitam ter um método ou uma variável. ( ) Uma classe não abstrata que herda de uma classe abstrata deve, obrigatoria- mente, implementar todos os seus métodos herdados. Assinale a alternativa correta: a) V - F - F - F - F. b) V - V - F - F - F. c) F - V - F - F - F. d) F - V - V - F - F. e) F - F - F - F - V. 3. O uso de Interfaces é amplamente utilizado pelos desenvolvedores e programado- res, pois permite a criação de códigos que especificam quais métodos uma classe deve implementar, sem definir como esses métodos serão tratados, ou seja, uma interface funciona como um contrato de especificações de métodos. Diante do con- texto citado, avalie as afirmações a seguir sobre o uso de interfaces em Java. I - Uma interface pode ser herdada. II - Uma classe concreta pode implementar uma interface. III - Uma interface pode conter métodos e atributos de classe. IV - Uma classe abstrata pode implementar uma interface. V - Uma interface pode ser implementada por outra interface. 146 na prática É correto o que se afirma em: a) I, apenas. b) I e II, apenas. c) II e IV, apenas. d) II, IV e V, apenas. e) I, II, III e IV, apenas. 4. Analise a modelagem a seguir, codifique as classes e crie uma instância de objeto. 5. Analise a modelagem a seguir, codifique as classes e crie uma instância de objeto. Colaborador - dataAdmisao : String - salario : String - cargo : String + cadastro() : void Pessoa <<abstract>> + <<abstract>> cadastro() : void - nome : String - dataNascimento : String Livro - autor :String + cadastro() : void + listar() : void Controle <<interface>> + cadastrar() : void + listar() : void 147 aprimore-se INTERFACES Java também oferece outra estrutura, denominada interface, com sintaxe similar a de classes, mas contendo apenas a especificação da funcionalidade que uma classe deve conter, sem determinar como essa funcionalidade deve ser implementada. Uma interface Java é uma classe abstrata para a qual todos os métodos são implici- tamente abstract e public, e todos os atributos são implicitamente static e final. Em outros termos, uma interface Java implementa uma “classe abstrata pura”. A sintaxe para a declaração de uma interface é similar àquela para a definição de classes, po- rém seu corpo define apenas assinaturas de métodos e constantes. A diferença entre uma classe abstrata e uma interface Java é que a interface obrigatoriamente não tem um “corpo” associado. Para que uma classe seja abstrata basta que ela seja assim declarada, mas a classe pode incluir atributos de objetos e definição de métodos, públicos ou não. Na interface, apenas métodos públicos po- dem ser declarados — mas não definidos. Da mesma forma, não é possível definir atributos — apenas constantes públicas. Enquanto uma classe abstrata é “estendida” (palavra chave extends) por classes derivadas, uma interface Java é “implementada” (palavra chave implements) por ou- tras classes. Uma interface estabelece uma espécie de contrato que é obedecido por 148 aprimore-se uma classe. Quando uma classe implementa uma interface, garante-se que todas as funcionalidades especificadas pela interface serão oferecidas pela classe. Outro uso de interfaces Java é para a definição de constantes que devem ser compartilhadas por diversas classes. Neste caso, a recomendação é implementar interfaces sem métodos, pois as classes que implementarem tais interfaces não precisam tipica- mente redefinir nenhum método. interface Coins { int PENNY = 1, NICKEL = 5, DIME = 10, QUARTER = 25, DOLAR = 100; } Class SodaMachine implements Coins { int price = 3*QUARTER; //... } Fonte: Ricarte (2001, p. 29-30). 149 eu recomendo! Java – Como Programar Autor: Paulo Deitel e Harvey Deitel Editora: Pearson Sinopse: milhões de alunos e profissionais aprenderam progra- mação e desenvolvimento de software com os livros Deitel®. Java – Como Programar, 10ª edição, fornece uma introdução clara, simples, envolvente e divertida à programação Java, com ênfase inicial em objetos. Os destaques incluem: rica cobertura dos fundamentos com exemplos reais; apresentação com ênfase inicial em classes e objetos; uso com Java™ SE 7, Java™ SE 8 ou ambos; Java™ SE 8 abordado em seções modu- lares opcionais; lambdas, fluxos e interfaces funcionais usando método padrão e estático do Java SE 8; Swing e GUI do JavaFX: elementos gráficos e multimídia; conjunto de exercícios “Fazendo a Diferença”; tratamento de exceções integrado; arquivos, fluxos e serialização de objetos; concorrência para melhor desempenho com multiprocessamento. O livro também tem o conteúdo principal para cursos introdutórios; outros tópicos: recursão, pesquisa, classificação, coleções genéri- cas, estruturas de dados, multithreading, banco de dados (JDBC ™ e JPA). livro Com o início do paradigma de desenvolvimento OO, alguns termos se tornaram corriqueiros no mundo de programadores e analistas de sistema. Classes, heran- ça e polimorfismo são alguns deles. http://www.devmedia.com.br/interfaces-x-classes-abstratas/13337 conecte-se 5 ESTUDO DE CASO: ANÁLISE CLÍNICA PLANO DE ESTUDO A seguir, apresentam-se as aulas que você estudará nesta unidade: • Estudo de caso • Passo a passo para a elaboração do diagrama de classes • Implementação do software. OBJETIVOS DE APRENDIZAGEM • Apresentar o fluxo de funcionamento para exemplificar o desenvolvimento de um sistema a partir da modelagem do sistema • Realizar um passo a passo da elaboração do diagrama de classes na visão do analista de software • Rever a modelagem do diagrama de classe na visão do desenvolvedor e do analista. PROFESSORES Me. Márcia Cristina Dadalto Pascutti Esp. Janaina Aparecida de Freitas Me. Rafael Alves Florindo Esp. Victor de Marqui Pedroso INTRODUÇÃO Olá, caro(a) aluno(a)! Chegamos à última unidade do livro de Programação III. Aqui, abordaremos o estudo de caso de um laboratório de análises clínicas, desde a concepção da modelagem até a implementação na linguagem Java. No primeiro tópico, apresentaremos a você o documento de requi- sitos de um laboratório de análises clínicas. Neste documento, o cliente listou todos os desejos de funcionalidades e as especificou. Além dessas especificações, ele detalhou o fluxo de como deve funcionar o sistema. Esse processo tem um grau de relevância alto, pois, caso não compreen- da o que é solicitado, o restante da modelagem ficará precária. Conse- quentemente, quando chegar à implementação, é possível que ocorram problemas sérios, que podem afetar o sistema como um todo e, por isto, deve-se gastar tempo nesta etapa para que, depois, não seja necessário tempo redobrado para a correção e customização. No segundo tópico, daremos continuidade à modelagem; agora, partire- mos da análise de requisitos e realizaremos um passo a passo para a elaboração do diagrama de classes, no tocante à visão do analista/engenheiro de software. No terceiro tópico, com toda essa modelagem em mãos, daremos iní- cio à nossa implementação do software, contudo será necessário rever a modelagem, de forma que possamos colocar, na visão do desenvolvedor, o diagrama de classe, que possuirá alguns atributos e algumas operações diferentes de modelagem, na visão do analista/engenheiro. Após esta análise, poderemos dar início, de fato, à codificação. Esta será realizada em Java, utilizando todos os conhecimentos da linguagem em si, como a implementação dos pilares da orientação a objetos (encapsulamento, herança, polimorfismo e abstração). Os dados serão armazenados em arrays dinâmicos, e o sistema conterá um menu de acesso às operações da clínica. U N ID A D E 5 152 1 ESTUDO DE CASO Neste estudo de caso, mostraremos um novo documento de requisitos, agora, de um laboratório de análises clínicas. Com base nesse documento, elaboraremos o diagrama de classes, mas, desta vez, faremos tudo passo a passo. Antes de começarmos a ler o documento de requisitos, gostaríamos que você imaginasse que foi ao médico, por exemplo, a um clínico geral. Para te dar um diagnóstico preciso e correto, esse médico pediu que você fizesse uma série de exames, por exemplo: hemograma, glicemia, creatinina, triglicerídeos e urocultu- ra. Ele anotou esta lista em um pedido de exames, e você, de posse deste pedido, foi ao laboratório de análises clínicas São João, a fim de fazer os exames. É neste momento que começa o nosso documento de requisitos. Documento de requisitos – Laboratório de análises clínicas O laboratório de análises clínicas São João deseja informatizar as suas atividades, pois, hoje, não há controle do histórico de exames de cada paciente e gasta-se mui- to tempo com atividades que poderiam ser feitas por um sistema informatizado. Hoje, o laboratório funciona da seguinte forma: U N IC E S U M A R 153 ■ O paciente (você) chega ao laboratório com o pedido dos exames (preen- chido pelo médico, o clínico geral que você acabou de consultar). ■ Se for a primeira vez que o paciente fará exames, é preenchida uma ficha de cadastro com os seguintes dados: nome, endereço, cidade, UF, CEP, telefone, data de nascimento, RG e CPF. ■ A recepcionista (a moça que te atendeu quando você chegou ao labora- tório) preenche uma ficha com o nome do paciente, do convênio e dos exames que o paciente fará, além da data e do horário de realização de cada um. No final da ficha, é anotada a data em que os exames estarão prontos. A primeiravia dessa ficha é entregue ao paciente, ou seja, a você. ■ O resultado do exame é colocado no envelope para posterior retirada pelo paciente. O laboratório deseja que o novo sistema possa fornecer informações rápidas, preci- sas e seguras a fim de melhorar as suas atividades administrativas e o atendimento aos seus pacientes. Dessa forma, você permanecerá bem menos tempo no labo- ratório, pois os processos estarão automatizados. Para tanto, o novo sistema deve: ■ Permitir o cadastro dos pacientes do laboratório, com todos os dados preenchidos na ficha. Este cadastro será realizado pelas recepcionistas. ■ Permitir o cadastro dos exames que o laboratório pode realizar. Cada exa- me pertence a um grupo, por exemplo, o exame hemograma pertence ao grupo sangue. Além disso, para cada exame, é preciso saber o seu código, a descrição, o valor e os procedimentos para a sua realização. Por exemplo, para o hemograma, o paciente deve estar em jejum. Esse cadastro será realizado pelos bioquímicos. ■ Permitir o cadastro dos pedidos de exames dos pacientes. É necessário saber os nomes do paciente, do médico que está solicitando os exames e do convênio que o paciente utilizará para esse pedido e, também, o valor dos exames, a data e o horário da realização deles e dos seus resultados. Atenção: cada exame pode ser realizado em datas e horários diferentes. Lembre-se de que o médico pode solicitar mais de um exame em cada pedido (no seu caso, solicitou cinco). Esse cadastro será realizado pelas recepcionistas. ■ Emitir a ficha do paciente, a qual contém todos os dados cadastrados. Este relatório será solicitado e recebido tanto pelas recepcionistas quanto pelo departamento administrativo do laboratório. U N ID A D E 5 154 Levantamento e Análise de Requisitos Uma das primeiras fases de um processo de desenvolvimento de software consiste no Levantamento de Requisitos. As outras etapas, sugeridas por muitos autores, são: Análise de Requisitos, Projeto, que se constitui na principal fase da modelagem, Codificação, Tes- tes e Implantação. Dependendo do método/processo adotado, essas etapas ganham, por vezes, nomenclaturas diferentes. As etapas de levantamento e análise de requisitos tra- balham com o domínio do problema e tentam determinar “o que” o software deve fazer, e se é realmente possível desenvolver o software solicitado. Na etapa de levantamento de requisitos, o engenheiro de software busca compreender as necessidades do usuário e o que ele deseja que o sistema a ser desenvolvido realize. Isso é feito, sobretudo, por meio de entrevistas, nas quais o engenheiro tenta compreender como funciona, atualmente, o processo a ser informatizado, e quais serviços o cliente precisa que o software forneça. Fonte: Guedes (2011, p. 22). conceituando A grande questão é: como saber se as necessidades dos usuários foram realmente bem compreendidas? (Gilleanes T. A. Guedes) pensando juntos ■ Emitir relatório com todos os exames que o laboratório realiza, com o código, a descrição, os procedimentos e o valor de cada um, agrupados por grupo de exame. Devem ser impressos, nesse relatório, o código e a descri- ção do grupo. O relatório será solicitado e recebido pelas recepcionistas. ■ Emitir o pedido do exame em três vias, com todos os dados do pedido do exame. O relatório será emitido pela recepcionista, e a primeira via será entregue ao paciente (comprovante da entrega do exame), a segunda via, ao departamento de faturamento (para a cobrança dos exames dos con- vênios), e a terceira via, aos bioquímicos (para a realização dos exames). ■ Emitir relatório com os resultados dos exames por pedido, com o nome do paciente, a data e o horário do exame (da sua realização), o nome do médico que solicitou o procedimento, o nome do convênio e o resultado de cada exame realizado, caso tenha sido mais de um. O relatório será solicitado pela recepcionista e entregue ao paciente (não é necessário que a recepcionista fique com esse relatório). U N IC E S U M A R 155 2 PASSO A PASSO PARA A ELABORAÇÃO DO DIAGRAMA de Classes 1. Lembre-se que os relatórios não serão a classe por si só, mas, para emitir cada relatório, utilizaremos diversas classes. Uma dica: se o diagrama pos- sui um caso de uso de cadastro, certamente, precisará de uma classe para armazenar os dados que serão cadastrados neste caso de uso. Seguindo este raciocínio, teríamos as seguintes classes: a) Paciente. b) Exame. c) Pedido de exame. d) Resultado de exame. e) Médicos. f) Convênios. g) Cidades. h) UFs. i) Grupos de exames. 2. Agora, para cada uma das classes listadas, relacione os possíveis atri- butos de cada uma delas. A maioria desses atributos já aparece descrita no documento de requisitos. Nunca se esqueça de voltar ao documento sempre que tiver dúvidas. a) Paciente: código, nome, endereço, CEP, cidade, UF, telefone, data de nascimento, RG e CPF. U N ID A D E 5 156 b) Exame: código, descrição, valor, procedimentos e grupo ao qual pertence o exame. c) Pedido de exame: código, nome do paciente, do médico e do convênio, no- mes dos exames que serão realizados, data e hora da realização de cada um, data e hora em que cada exame ficará pronto, bem como o valor de cada. d) Resultado de exame: descrição do resultado (para cada exame do pedido, o resultado deverá ser cadastrado). e) Médicos: CRM, nome (como o documento de requisitos não menciona nada sobre os dados dos médicos, colocaremos somente os atributos que interessam para o pedido de exame). f) Convênios: código, nome (como o documento de requisitos não mencio- na nada sobre os dados dos convênios, colocaremos somente os atributos que interessam para o pedido de exame). g) Cidades: código, nome, DDD (o documento de requisitos não mencio- nou nada a respeito, mas esses atributos devem constar em qualquer classe de cidades). h) UFs: sigla, nome. i) Grupos de exames: código, descrição. j) Desenhar, no diagrama de classes, as classes relacionadas com os seus respectivos atributos. Faremos o desenho do diagrama utilizando a ferra- menta Astah, no mesmo arquivo em que desenhamos o diagrama de casos de uso. Assim, ficaremos com os dois diagramas em um único arquivo. 3. Para cada classe desenhada no diagrama, estabeleça o seu relacionamento com as demais. Lembre-se dos tipos de relacionamentos que estudamos: associação (unária e binária), generalização/especialização, agregação. Releia, também, a explicação sobre classes associativas. 4. Depois disso, estabeleça a multiplicidade de cada relacionamento, lembrando de eliminar os atributos que podem ser obtidos por meio do relacionamento. 5. Primeiro, desenhe o seu diagrama de classes e, depois, compare-o com o nosso, apresentado a seguir: U N IC E S U M A R 157 Pedido Exame - codigo : int Médico - CRM : int - nome : int Convênio - codigo : int - nome : int Cidade - codigo : int - nome : int - DDD : int UF - sigla : int - nome : int Grupo Exame - codigo : int - descricao : int Paciente - codigo : int - nome : int - endereco : int - CEP : int - telefone : int - data_nascimento : int - RG : int - CPF : int Exame-Pedido Exame - data_realizacao_exame : int - hora_realizacao_exame : int - data_pronto : int - hora_pronto : int - resultado : int - valor : int Exame - codigo : int - descricao: int - valor : int - procedimentos : int 1..* 1..* 1..* 1..* 0..* 0..* 0..* 0..* Exame-Pedido Exame Figura 1 - Diagrama de classes do laboratório de análises clínicas / Fonte: os autores. Seguem alguns esclarecimentos sobre o diagrama representado na Figura 1: 1. Em um pedido de exame, pode estar relacionado, somente, um paciente, um médico e um convênio (no diagrama, não aparece a multiplicidade 1, por ser o valor default de um relacionamento). 2. Um pedido de exame pode estar relacionado a um ou a vários exames (no caso desse documento derequisitos, a cinco deles). 3. Note que os atributos data_realizacao_exame, hora_realizacao_exame, data_pronto, hora_pronto, resultado e valor estão armazenados na classe associativa, que foi originada do relacionamento muitos para muitos entre pedido de exame e exame. Isto se deve ao fato de que, para cada exame, es- U N ID A D E 5 158 ses atributos podem ser diferentes. Por exemplo: se o atributo data_pronto tivesse sido armazenado na classe pedido_exame, seria possível cadastrar, somente, uma data em que os exames ficariam prontos. Todavia, na rea- lidade, não é isto o que acontece, ou seja, em uma lista de exames que o paciente precisa realizar, pode-se ter exames que ficam prontos em dois dias e os que ficam prontos em cinco. 4. Veja que não foi criada uma classe resultado_exame, pois, como é, so- mente, uma descrição, decidiu-se armazená-la na classe associativa Exa- me-Pedido Exame. 5. Note, também, que, na classe Pedido Exame, não aparece o nome do pa- ciente, assim como relacionamos no item 2 deste passo a passo. Isto por- que o nome será obtido por meio do relacionamento de Pedido Exame com paciente. Não desenhamos o atributo-chave de paciente na classe Pedido Exame, mas ele está lá, ou seja, por meio dele é que buscaremos o nome do paciente na classe Paciente, quando precisarmos. 6. Em vez de termos utilizado o recurso da classe associativa, poderíamos ter usado o relacionamento de agregação. Veja como ficaria (serão mostradas, somente, algumas classes; as demais não foram mostradas, pois ficariam iguais ao diagrama já apresentado): Figura 2 - Diagrama de classes associativas / Fonte: os autores. Pedido Exame - codigo : int Grupo Exame - codigo : int - descricao : int Exame-Pedido Exame - data_realizacao_exame : int - hora_realizacao_exame : int - data_pronto : int - hora_pronto : int - resultado : int - valor : int Exame - codigo : int - descricao: int - valor : int - procedimentos : int 1..* 1..* 1..* 0..* 0..* 0..* U N IC E S U M A R 159 3 IMPLEMENTAÇÃO DO SOFTWARE Agora que já temos uma modelagem preliminar do diagrama de classe do la- boratório de análises clínicas São João, daremos início ao desenvolvimento da implementação na linguagem Java. Primeiro, criaremos um projeto no NetBeans chamado AnalisesCli- nicas. Inicialmente, desmarque o campo Criar Classe Principal. Figura 3 - Criando o projeto AnalisesClinicas / Fonte: os autores. U N ID A D E 5 160 Em seguida, criaremos um paco- te com o nome de desenvol- vimento, com o objetivo de separação de códigos. Após a criação do projeto Anali- sesClinicas e do pacote desen- volvimento, daremos início ao de- senvolvimento das nossas classes. A primeira classe a ser criada no sistema será a Paciente, contudo analisaremos as classes que completam o cadastro do paciente e as remodelaremos na visão do desenvolvedor, como na figura a seguir. Figura 5 - Classe para o cadastro do paciente / Fonte: os autores. Note que, nas classes Paciente, Cidade e Uf, todos os seus respectivos atributos possuem a visibilidade private, e os tipos dos dados estão como inteiro. Primeiramente, alteraremos os tipos dos atributos de acordo com a necessidade do atributo. Na sequência, serão implementados todos os métodos modificadores (setters e getters) de todas as classes para garantir o encapsulamento dos atributos. Figura 4 - Criando o pacote / Fonte: os autores. Cidade - codigo : int - nome : int - DDD : int UF - sigla : int - nome : int Paciente - codigo : int - nome : int - endereco : int - CEP : int - telefone : int - data_nascimento : int - RG : int - CPF : int 1..* 0..* U N IC E S U M A R 161 Além desses métodos, deveremos criar outros de acordo com a necessidade do estudo de caso, sendo eles: Na classe Paciente, cidade e UF: ■ Para garantir “Permitir o cadastro dos pacientes do laboratório, com todos os dados preenchidos na ficha de cadastro." Deveremos criar um método que armazene os seus referidos campos. ■ Para garantir “Emitir a ficha do paciente, a qual con- tém todos os dados cadastrados. Esse relatório será solicitado e recebido tanto pelas recep- cionistas quanto pelo departamento administra- tivo do laboratório.”, deveremos criar um método que liste todos os referidos campos. ■ Lista Paciente, que será utilizado quando solicitar um exame. Note, ainda, que a classe Paciente possui um relacionamento (1..*) com a classe cidade. Esse relacionamento pode ser lido e interpretado da seguinte forma: Um paciente habita em uma cidade e, em uma cidade, habitam diversos pacientes. A classe Cidade, possui relacionamento (0..*) com a classe UF, neste caso, criaremos um novo atributo, com o nome de Uf, na classe Cidade. Uma cidade possui um UF e, em uma UF, podem ter zero ou mais cidades. Note que as classes Paciente e Cidade não possuem um atributo que expli- cita a relação entre as classes, sendo assim, para que seja possível realizar a mul- tiplicidade entre as classes, criaremos um novo atributo na classe Paciente, com o nome de cidade, e na classe Cidade, com o nome de UF. Como esses atributos receberão a ligação com as outras classes, estas, por sua vez, serão do tipo da sua respectiva classe, ou seja, o campo cidade, da classe Paciente, ficará como cidade: Cidade, e o atributo UF, da classe UF, será colocado: uf: UF. Confira a figura a seguir, com todas as referidas alterações. U N ID A D E 5 162 Figura 6 - Alterações das classes: Paciente, Cidade, UF / Fonte: os autores. Agora que já remodelamos uma parte do diagrama de classe, codificaremos as classes. Para isso, no pacote de desenvolvimento, crie as classes Java para Paciente, Cidade e UF, de forma que fique com a seguinte programação: Na classe Paciente: package desenvolvimento; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; public class Paciente { private int codigo; private String nome; private String endereco; private String CEP; private String telefone; private Date dataNascimento; private String RG; Cidade - codigo : int - DDD : String - UF : UF + cadastrar() : void + imprimirCidade() : void + setters() : void + getters() : void UF - sigla : String - nome : String + cadastrar() : void + imprimirUf() : void Paciente - codico : int - nome : int - endereco : int - CEP : int - telefone : int - data_nascimento : int - RG : int - CPF : int + cadastrar() : void + listarPaciente() : void + imprimirPaciente() : void + setters() : void + getters() : void 0..* 1..* 1 1 U N IC E S U M A R 163 private String CPF; private Cidade cidade = new Cidade(); public int getCodigo(){ return codigo; } public void setCodigo(int codigo) { this.codigo = codigo; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getCEP() { return CEP; } public void setCEP(String CEP) { this.CEP = CEP; } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public Date getDataNascimento() { return dataNascimento; } U N ID A D E 5 164 public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; } public String getRG() { return RG; } public void setRG(String RG) { this.RG = RG; } public String getCPF() { return CPF; } public void setCPF(String CPF) { this.CPF = CPF; } public Cidade getCidade() { return cidade; } public void setCidade(Cidade cidade) { this.cidade = cidade; } public void cadastrar() throws ParseException{ Scanner tec = new Scanner(System.in); System.out.println("Informe os dados do Paciente"); System.out.print("Nome: "); this.setNome(tec.nextLine()); System.out.print("Endereço: "); this.setEndereco(tec.nextLine()); System.out.print("CEP: "); this.setCEP(tec.nextLine()); System.out.print("Telefone: "); this.setTelefone(tec.nextLine()); System.out.print("Data de Nascimento Ex: [02/12/1982]:"); String dataRecebida = tec.nextLine(); U N IC E S U M A R 165 SimpleDateFormat df = new SimpleDateFormat("dd/ MM/yyyy"); Date dt = df.parse(dataRecebida); this.setDataNascimento(dt); System.out.print("RG: "); this.setRG(tec.nextLine()); System.out.print("CPF: "); this.setCPF(tec.nextLine());cidade.cadastra(); } public void imprimirPaciente(){ SimpleDateFormat formataData= new SimpleDate- Format("dd/MM/yyyy"); String stringData = formataData.format(this.ge- tDataNascimento()); System.out.println("Paciente: "+this.getNome()); System.out.println("Endereço: "+this.getEndere- co()); System.out.println("CEP: "+this.getCEP()); System.out.println("Telefone: "+this.getTelefo- ne()); System.out.println("Data de Nascimento: "+string- Data); System.out.println("RG: "+this.getRG()); System.out.println("CPF: "+this.getCPF()); cidade.imprimirCidade(); } public void listarPaciente(){ System.out.print(" | " +this.getCPF()); System.out.print(" | " +this.getNome()); } } U N ID A D E 5 166 Explicando o código, temos que, na classe Paciente, foi necessário realizar a im- portação de quatro bibliotecas que serão utilizadas para o tratamento de exceção, conversão e formatação de datas e, também, de entrada dos campos, sendo elas: import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; respectivamente. Para garantir a relação do Paciente com a cidade, foi necessário declarar o campo cidade do tipo Cidade - private Cidade cidade = new Ci- dade();. Neste campo, será instanciado um objeto da classe Cidade, que, no caso, armazenará os dados da cidade do Paciente. E, por fim, foram implementados os mé- todos cadastrar(), imprimirPaciente(), listarPaciente() e, para garantir o encapsulamento, foram implementados os métodos modificadores. Continuando na classe Paciente, note que, no método cadastrar(), estamos trabalhando com o tipo Date para o atributo dataNascimento. Neste caso, primeiramente, fazemos a leitura de uma string e armazenamos em uma variável “dataRecebida” String dataRecebida = tec.nextLine();. Posteriormente, formatamos a dataRecebida por intermédio da classe Sim- pleDataFormat SimpleDateFormat df = new SimpleDate- Format(“dd/MM/yyyy”); e, por fim, convertemos a string em dataRe- cebida pelo Date dt = df.parse(dataRecebida). Na classe Cidade: package desenvolvimento; import java.util.Scanner; public class Cidade { private String nome, ddd; private Uf uf = new Uf(); public String getDdd() { return ddd; } public void setDdd(String ddd) { this.ddd = ddd; } U N IC E S U M A R 167 public Uf getUf() { return uf; } public void setUf(Uf uf) { this.uf = uf; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public void cadastrar(){ Scanner tec = new Scanner(System.in); System.out.print("Cidade:"); this.setNome(tec.nextLine()); System.out.print("DDD: "); this.setDdd(tec.nextLine()); uf.cadastra(); } public void imprimirCidade(){ System.out.println("Cidade: "+this.getNome()); System.out.println("DDD: "+this.getDdd()); uf.imprimirUf(); } } Explicando o código, temos que, na classe Cidade, foi necessário realizar a im- portação de, apenas, uma biblioteca, que será utilizada para a entrada dos campos: import java.util.Scanner. U N ID A D E 5 168 Para garantir a relação da cidade com o UF, foi necessário declarar o campo uf do tipo Uf - private Uf uf = new Uf();. Neste campo, será instanciado um objeto da classe UF, que, no caso, armazenará os dados do estado. E, por fim, foram implementados os métodos cadastrar(), imprimir- Cidade(), listarCidade() e, para garantir o encapsulamento, foram implementados os métodos modificadores. Na classe UF: package desenvolvimento; import java.util.Scanner; public class Uf { private String nome; private String sigla; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getSigla() { return sigla; } public void setSigla(String sigla) { this.sigla = sigla; } public void cadastrar(){ Scanner tec = new Scanner(System.in); U N IC E S U M A R 169 System.out.print("Estado: "); this.setNome(tec.nextLine()); System.out.print("Sigla: "); this.setSigla(tec.nextLine()); } public void imprimirUf(){ System.out.println("Estado: "+this.getNome()); System.out.println("Sigla: "+this.getSigla()); } } Agora, analisaremos a parte da modelagem que trata do pedido de exame. Confira o recorte a seguir: Figura 7 - Recorte do pedido de exame / Fonte: os autores. Note que a classe Paciente foi alterada, agora, alteraremos as classes Médico, Con- vênio e Pedido Exame. Na classe Médico, alteraremos o tipo do atributo nome e incluiremos os métodos para cadastro e impressão, além dos métodos modifica- dores. O mesmo ocorrerá na classe Convênio e na classe PedidoExame. Confira a modelagem atualizada. Pedido Exame - codigo : int Médico - CRM : int - nome : int Convênio - codigo : int - nome : int Paciente - codigo : int - nome : int - endereco : int - CEP : int - telefone : int - data_nascimento : int - RG : int - CPF : int 1..* 1..* 0..* 0..* 0..* U N ID A D E 5 170 Figura 8 - Alterações das classes: Médico, Convênio, Pedido de Exame / Fonte: os autores. Agora que já remodelamos uma parte do diagrama de classe, codificaremos as classes. Para isso, no pacote de desenvolvimento, crie as classes Java para Medico, Convenio e PedidoExame, de forma que fique com a seguinte programação. A classe Convênio: package desenvolvimento; import java.util.Scanner; public class Convenio { private int codigo; private String nome; public int getCodigo() { return codigo; } public void setCodigo(int codigo) { Paciente - codigo : int - nome : String - dataNascimento : Date - telefone : String - RG : String - CPF : String - endereco : String - cidade : Cidade - CEP : String + cadastrar() : void + listarPaciente() : void + imprimirPaciente() : void + setters() : void + getters() : void PedidoExame - codigo : int - paciente : Paciente - medico : Medico - convenio : Convenio + cadastrar() : void + imprimirPedido() : void + setters() : void + getters() : void Médico - CRM : int - nome : String + cadastrar() : void + imprimirMedico() : void + listarMedico() : void + setters() : void + getters() : void Médico - codigo : int - nome : String + cadastrar() : void + imprimirConvenio() : void + listarConvenio() : void + setters() : void + getters() : void 1 1 1 0..* 0..* 0..* U N IC E S U M A R 171 this.codigo = codigo; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public void cadastrar(){ Scanner cad = new Scanner(System.in); System.out.print("Convênio: "); this.setNome(cad.nextLine()); } public void imprimirConvenio(){System.out.println("Convênio: "+this.getNo- me()); } } Explicando o código, temos que, na classe Convenio, foi necessário realizar a importação de apenas uma biblioteca, que será utilizada para a entrada dos campos: import java.util.Scanner. A classe possui os métodos de cadastro e impressão de convênios. A classe Médico: package desenvolvimento; import java.util.Scanner; public class Medico { private int CRM; private String nome; public int getCRM() { return CRM; } public void setCRM(int CRM) { U N ID A D E 5 172 this.CRM = CRM; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public void cadastrar(){ Scanner cad = new Scanner(System.in); System.out.print("Nome: "); this.setNome(cad.nextLine()); System.out.print("CRM: "); this.setCRM(cad.nextInt()); } public void imprimirMedico(){ System.out.println("Médico: "+this.getNome()); System.out.println("CRM: "+this.getCRM()); } } Explicando o código, temos que, na classe Medico, foi necessário realizar a importação de apenas uma biblioteca, que será utilizada para a entrada dos campos: import java. util.Scanner. A classe possui os métodos de cadastro e impressão de convênios. A classe PedidoExame: package desenvolvimento; import static desenvolvimento.Programa.c; import static desenvolvimento.Programa.m; import static desenvolvimento.Programa.p; import static desenvolvimento.Programa.contC; import static desenvolvimento.Programa.contM; import static desenvolvimento.Programa.contP; import java.util.Scanner; U N IC E S U M A R 173 public class PedidoExame { private int codigo; private Paciente paciente = new Paciente(); private Medico medico = new Medico(); private Convenio convenio = new Convenio(); public int getCodigo() { return codigo; } public void setCodigo(int codigo) { this.codigo = codigo; } public Paciente getPaciente() { return paciente; } public void setPaciente(Paciente paciente) { this.paciente = paciente; } public Medico getMedico() { return medico; } public void setMedico(Medico medico) { this.medico = medico; } public Convenio getConvenio() { return convenio; } public void setConvenio(Convenio convenio) { this.convenio = convenio; } public void cadastrar(){ Scanner cad = new Scanner(System.in); int codPaciente, codMedico, codConvenio; System.out.println("----------------------"); System.out.println("Relação de Pacientes"); System.out.println("Código | CPF | Nome"); U N ID A D E 5 174 for(int i = 0; i < contP; i++){ System.out.print(" "+i); p[i].listarPaciente(); System.out.println(); } System.out.println("Escolha um Paciente"); codPaciente = cad.nextInt(); this.setPaciente(p[codPaciente]); cad.nextLine(); System.out.println("--------------------"); System.out.println("Relação de Médicos"); System.out.println("Código | CRM | Nome"); for(int i = 0; i < contM; i++){ System.out.print(" "+i); m[i].listarMedico(); System.out.println(); } System.out.println("Escolha um Médico"); codMedico = cad.nextInt(); this.setMedico(m[codMedico]); cad.nextLine(); System.out.println("-------------------------"); System.out.println("Relação de Convênio"); System.out.println("Código | Nome"); for(int i = 0; i < contC; i++){ System.out.print(" "+i); c[i].imprimirConvenio(); System.out.println(); } System.out.println("Escolha um convênio"); codConvenio = cad.nextInt(); this.setConvenio(c[codConvenio]); cad.nextLine(); } U N IC E S U M A R 175 public void imprimirPedidoExame(){ System.out.println("Médico: "+this.getMedi- co().getNome()); System.out.println("Convênio: "+this.getConve- nio().getNome()); System.out.println("Paciente: "+this.getPa- ciente().getNome()); } } Como a classe PedidoExame() possui os relacionamentos com as classes Convenio, Medico e Paciente, classes estas já construídas em seu material, faz-se necessário importar os registros de cada classe para abrir o pedido de exame. Os arrays estão armazenadas na classe Programa, que será a nossa classe principal do sistema, a qual, até o presente momento não desenvolvemos (Ver a classe Programa). Confira, a seguir, a importação dos arrays. import static desenvolvimento.Programa.c; import static desenvolvimento.Programa.m; import static desenvolvimento.Programa.p; Além de importar os arrays, é necessário importar a última posição de cada um, o que indicará quantos registros temos em cada array. import static desenvolvimento.Programa.contC; import static desenvolvimento.Programa.contM; import static desenvolvimento.Programa.contP; Continuando a explicação da nossa classe, veja que declaramos os objetos pa- ciente, médico e convênio para que possamos garantir os relacionamentos dessa classe com as demais. private Paciente paciente = new Paciente(); private Medico medico = new Medico(); private Convenio convenio = new Convenio(); U N ID A D E 5 176 Na sequência, foram desenvolvidos os métodos modificadores, que garantem o encapsulamento dos atributos. O método cadastrar é o cerne dessa classe, é nele que os relacionamentos acontecem. Veja que, de início, declaramos três variáveis do tipo inteiro (int codPaciente, codMedico, codConvenio;), variáveis estas que permitirão ao usuário informar o registro e, posteriormente, armazenar nos atri- butos de relacionamento da classe. O primeiro bloco de códigos do método cadastrar lista e todos os pacientes estão armazenados no array p[]por meio do método listarPaciente(). Após listar todos os pacientes, o usuário digitará a posição do array onde se en- contra o paciente e, na sequência, armazenará o objeto com a posição do paciente selecionado. System.out.println(“Relação de Pacientes”); System.out.println(“Código | CPF | Nome”); for(int i = 0; i < contP; i++){ System.out.print(“ “+i); p[i].listarPaciente(); System.out.println(); } System.out.println(“Escolha um Paciente”); codPaciente = cad.nextInt(); this.setPaciente(p[codPaciente]); cad.nextLine(); No segundo e no terceiro bloco deste método, ocorrem a listagem, a seleção e o armazenamento do objeto, além da sua respectiva posição selecionada das classes médico e convenio, respectivamente. Após esta parte inicial, será chamada a classe associativa, que armazenará os exames do pedido do paciente. Agora, analisaremos a parte da modelagem que trata do grupo do exame e do exame. Confira o recorte a seguir: U N IC E S U M A R 177 Figura 9 - Recorte do grupo de exame / Fonte: os autores. Na classe Exame, alteraremos o tipo dos atributos e incluiremos os métodos para cadastro, listagem e impressão, além dos métodos modificadores. O mesmo ocor- rerá na classe GrupoConvenio. Confira a modelagem atualizada: Figura 10 - Alterações das classes Exame e GrupoExame / Fonte: os autores. Agora, codificaremos as classes, já que remodelamos uma parte do diagrama delas. Para isso, no pacote de desenvolvimento, crie as classes Java para Exame e GrupoExame, de forma que fiquem com a seguinte programação: A classe GrupoExame package desenvolvimento; import java.util.Scanner; public class GrupoExame { Grupo Exame - codigo : int - descricao : int Exame - codigo : int - descricao: int - valor : int - procedimentos : int 1..* Exame - codigo : int - descricao : String - valor : double - procedimentos : String - grupoExame : GrupoExame + cadastrar() : void + imprimirExame() : void + listarExame() : void + setters() : void + getters() : void GrupoExame - codigo : int - descricao : String + cadastrar() : void + imprimirGrupoExame() : void + setters() : void + getters() : void 1..* 1 U N ID A D E 5 178 private int codigo; private String Descricao; public int getCodigo() { return codigo; } public void setCodigo(int codigo){ this.codigo = codigo; } public String getDescricao() { return Descricao; } public void setDescricao(String Descricao) { this.Descricao = Descricao; } public void cadastrar(){ Scanner cad = new Scanner (System.in); System.out.print("Código do Grupo: "); this.setCodigo(cad.nextInt()); cad.nextLine(); System.out.print("Grupo do Exame: "); this.setDescricao(cad.nextLine()); } public void imprimirGrupoExame(){ System.out.println("Codigo: "+this.getCodigo()); System.out.println("Grupo do Exame: "+this.getDescricao()); System.out.println("--------------------"); } public void listarGrupoExame(){ System.out.print(" | "+this.getDescricao()); } } A classe GrupoExame dispensa explicação, uma vez que já é possível o seu entendimento. Implementaremos a classe Exame. U N IC E S U M A R 179 package desenvolvimento; import static desenvolvimento.Programa.contGe; import static desenvolvimento.Programa.ge; import java.util.Scanner; public class Exame { private int codigo; private String Descricao; private double valor; private String procedimentos; private GrupoExame grupoExame = new GrupoExame(); public int getCodigo() { return codigo; } public void setCodigo(int codigo) { this.codigo = codigo; } public String getDescricao() { return Descricao; } public void setDescricao(String Descricao) { this.Descricao = Descricao; } public double getValor() { return valor; } public void setValor(double valor) { this.valor = valor; } public String getProcedimentos() { return procedimentos; } public void setProcedimentos(String procedimentos) { this.procedimentos = procedimentos; } U N ID A D E 5 180 public GrupoExame getGrupoExame() { return grupoExame; } public void setGrupoExame(GrupoExame grupoExame) { this.grupoExame = grupoExame; } public void cadastrar(){ Scanner cad = new Scanner(System.in); int codGrupoExame; System.out.println("----------------------"); System.out.println("Relação dos Grupos"); System.out.println("Código | Grupos"); for(int i = 0; i < contGe; i++){ System.out.print(" "+i); ge[i].listarGrupoExame(); System.out.println(); } System.out.println("Vincule a um grupo"); codGrupoExame = cad.nextInt(); this.setGrupoExame(ge[codGrupoExame]); cad.nextLine(); System.out.println("Código: "); this.setCodigo(cad.nextInt()); cad.nextLine(); System.out.println("Exame"); this.setDescricao(cad.nextLine()); System.out.println("Valor"); this.setValor(cad.nextDouble()); cad.nextLine(); System.out.println("Procedimentos"); U N IC E S U M A R 181 this.setProcedimentos(cad.nextLine()); } public void imprimirExame(){ System.out.println("Código.........: "+this. getCodigo()); System.out.println("Exame..........: "+this. getDescricao()); System.out.println("Grupo do Exame.: "+this. getGrupoExame().getDescricao()); System.out.println("Valor..........: "+this. getValor()); System.out.println("Procedimentos..: "+this. getProcedimentos()); System.out.println("-------------------"); } } Como a classe Exame() possui o relacionamento com a classe GrupoExame, faz-se necessário importar os registros de cada classe para possibilitar o relacio- namento do exame em seu respectivo grupo. O array está armazenado na classe Programa, que será a nossa classe principal do sistema (ver a classe Programa). Confira, a seguir, a importação do array. import static desenvolvimento.Programa.ge; Além de importar o array, é necessário importar a última posição dele, que in- dicará quantos registros temos. import static desenvolvimento.Programa.contGe; Continuando a explicação sobre a nossa classe, vejam que foi declarada a classe GrupoExame() para garantir o relacionamento desta com a de Exame. U N ID A D E 5 182 private GrupoExame grupoExame = new GrupoExame(); Na sequência, foram desenvolvidos os métodos modificadores, que garantem o encapsulamento dos atributos. O método cadastrar é o cerne dessa classe, é nele que os relacionamentos acontecem. Veja que, de início, declaramos uma variável do tipo inteiro (int codGrupoExame;), variável essa que permitirá ao usuário informar o registro e, posteriormente, armazená-lo no atributo de relacionamento da classe. A primeira parte de código do método Cadastrar lista todos os grupos de exames que estão armazenados no array ge[],por meio do método listar- GrupoExame(). Após listar todos os grupos, o usuário digitará a posição do array onde se encontra o grupo e, na sequência, armazena o objeto com a posição do grupo selecionado. System.out.println("Relação dos Grupos"); System.out.println("Código | Grupos"); for(int i = 0; i < contGe; i++){ System.out.print(" "+i); ge[i].listarGrupoExame(); System.out.println(); } System.out.println("Vincule a um grupo"); codGrupoExame = cad.nextInt(); this.setGrupoExame(ge[codGrupoExame]); cad.nextLine(); Confira a modelagem completa do estudo de caso da clínica de exames. U N IC E S U M A R 183 Figura 11 - Modelagem final / Fonte: os autores. ExamePedidoExame - dataRealizacaoExame : Date - horaRealizacaoExame : time - dataPronto : Date - horaPronto : time - resultado : String - ExamePedido : Exame 1 1 0..* 0..* 0..* 1..* 1 ExamePedidoExame PedidoExame - codigo : int - paciente : Paciente - medico : Medico - convenio : Convenio + cadastrar() : void + imprimirPedido() : void + setters() : void + getters() : void Médico - CRM : int - nome : String + cadastrar() : void + imprimirMedico() : void + listarMedico() : void + setters() : void + getters() : void Exame - codigo : int - descricao : String - valor : double - procedimentos : String - grupoExame : GrupoExame + cadastrar() : void + imprimirExame() : void + listarExame() : void + setters() : void + getters() : void GrupoExame - codigo : int - descricao : String + cadastrar() : void + imprimirGrupoExame() : void + setters() : void + getters() : void Convenio - codigo : int - nome : String + cadastrar() : void + imprimirConvenio() : void + listarConvenio() : void + setters() : void + getters() : void Paciente - codigo : int - nome : String - dataNascimento : Date - telefone : String - RG : String - CPF : String - endereco : String - cidade : Cidade - CEP : String + cadastrar() : void + listarPaciente() : void + imprimirPaciente() : void + setters() : void + getters() : void Cidade - codigo : int - DDD : String - UF : UF + cadastrar() : void + imprimirCidade() : void + setters() : void + getters() : void UF - sigla : String - nome : String + cadastrar() : void + imprimirUf() : void 0..* 1 1..* 1 1 U N ID A D E 5 184 Agora, desenvolveremos a classe ExamePedidoExame, classe essa que reunirá as informações dos exames e os seus respectivos dias. O código dela é conhecido, dessa forma, deixamos para você a tarefa de analisar o que está sendo realizado nela. No geral, o usuário escolhe o exame e realiza a entrada com as datas e as horas do exame. package desenvolvimento; import static desenvolvimento.Programa.contEx; import static desenvolvimento.Programa.ex; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; public class ExamePedidoExame { private Date dataRealizacaoExame; private Date dataPronto; private Exame examePedido = new Exame(); private Date horaRealizacaoExame; private Date horaPronto; private String resultado; public Exame getExamePedido() { return examePedido; } public void setExamePedido(Exame examePedido) { this.examePedido = examePedido; } public Date getDataRealizacaoExame() { return dataRealizacaoExame; } public void setDataRealizacaoExame(Date dataReali- zacaoExame) { this.dataRealizacaoExame = dataRealizacaoExame; } U N IC E S U M A R 185 public Date getDataPronto() { return dataPronto; } publicvoid setDataPronto(Date dataPronto) { this.dataPronto = dataPronto; } public Date getHoraRealizacaoExame() { return horaRealizacaoExame; } public void setHoraRealizacaoExame(Date horaReali- zacaoExame) { this.horaRealizacaoExame = horaRealizacaoExame; } public Date getHoraPronto() { return horaPronto; } public void setHoraPronto(Date horaPronto) { this.horaPronto = horaPronto; } public String getResultado() { return resultado; } public void setResultado(String resultado) { this.resultado = resultado; } public void cadastrar() throws ParseException{ Scanner cad = new Scanner(System.in); int codE; System.out.println("--------------------"); System.out.println("Relação de Exames"); System.out.println("Cód | Descrição | Valor"); for(int i = 0; i < contEx; i++){ System.out.print(" "+i); U N ID A D E 5 186 ex[i].listarExame(); System.out.println(); } System.out.println("Escolha um Exame: "); codE = cad.nextInt(); this.setExamePedido(ex[codE]); cad.nextLine(); System.out.print("Data Realização exame Ex: [02/12/1982]:"); String dataRealizarExame = cad.nextLine(); SimpleDateFormat sdf1 = new SimpleDateForma- t("dd/MM/yyyy"); Date dt1 = sdf1.parse(dataRealizarExame); this.setDataRealizacaoExame(dt1); System.out.print("Data exame pronto Ex: [02/12/1982]: "); String dataRetirarExame = cad.nextLine(); SimpleDateFormat sdf2 = new SimpleDateForma- t("dd/MM/yyyy"); Date dt2 = sdf2.parse(dataRetirarExame); this.setDataPronto(dt2); System.out.print("Hora Realização exame Ex: [10:12]: "); String horaRealizarExame = cad.nextLine(); SimpleDateFormat sdf3 = new SimpleDateForma- t("HH:mm"); Date dth1 = sdf3.parse(horaRealizarExame); this.setHoraRealizacaoExame(dth1); System.out.print("Hora exame pronto Ex: [10:12]: "); String horaRetirarExame = cad.nextLine(); U N IC E S U M A R 187 SimpleDateFormat sdf4 = new SimpleDateForma- t("HH:mm"); Date dth2 = sdf4.parse(horaRetirarExame); this.setHoraPronto(dth2); System.out.print("Valor: "+ this.getValor()); } public void imprimir(){ SimpleDateFormat formataData= new SimpleDate- Format("dd/MM/yyyy"); String dataRealizarExame = formataData.forma- t(this.getDataRealizacaoExame()); System.out.println("Data do Exame: "+dataRea- lizarExame); SimpleDateFormat formataHora= new SimpleDate- Format("HH:mm"); String horaRealizarExame = formataHora.forma- t(this.getHoraRealizacaoExame()); System.out.println("Horário do Exame: "+hora- RealizarExame); String dataRetirarExame = formataData.format(- this.getDataPronto()); System.out.println("Data para retirar o Exame: "+dataRetirarExame); String stringData13 = formataHora.format(this. getHoraPronto()); System.out.println("Horário para retirar o Exame: "+stringData13); } } U N ID A D E 5 188 Agora, chegou a hora de colocarmos o nosso có- digo para rodar. No pacote de desenvolvimento, crie uma nova classe Java principal e a salve como Programa. Confira, na Figura 12, como ficou a estrutura de classes do nosso sistema. package desenvolvimento; import java.text.ParseException; import java.util.Scanner; public class Programa { static Paciente p[] = new Paciente[10]; static Medico m[] = new Medico[10]; static Convenio c[] = new Convenio[10]; static PedidoExame pe[] = new PedidoExame[10]; static GrupoExame ge[] = new GrupoExame[10]; static Exame ex[] = new Exame[10]; static ExamePedidoExame pexe[] = new ExamePedidoExame[10]; public static int contPe = 0; public static int contP = 0; public static int contM = 0; public static int contC = 0; public static int contGe = 0; public static int contEx = 0; public static int contPexe = 0; public static void imprimePaciente(){ for(int i = 0; i < contP; i++){ Figura 12 - Estrutura de classes Fonte: os autores. U N IC E S U M A R 189 System.out.print(i); p[i].imprimirPaciente(); System.out.println("----------------------"); } } public static void imprimeConvenio(){ for(int i = 0; i < contC; i++){ c[i].imprimirConvenio(); System.out.println("----------------------"); } } public static void imprimeMedico(){ for(int i = 0; i < contM; i++){ m[i].imprimirMedico(); System.out.println("----------------------"); } } public static void imprimePedidoExame(){ for(int i = 0; i < contPe; i++){ pe[i].imprimirPedidoExame(); System.out.println("----------------------"); for(int j = 0; j < contPexe; j++){ pexe[j].imprimir(); System.out.println ("------------------- ---"); } } } public static void imprimeGrupoExame(){ for(int i = 0; i < contGe; i++){ ge[i].imprimirGrupoExame(); System.out.println("----------------------"); } } public static void imprimeExame(){ U N ID A D E 5 190 for(int i = 0; i < contEx; i++){ ex[i].imprimirExame(); System.out.println ("------------"); } } public static void main(String[] args) throws Par- seException { Scanner tec = new Scanner(System.in); int opcao = 1; while(opcao!=0){ System.out.println("----------------"); System.out.println("SISTEMA ANALISES CLINICAS"); System.out.println("------------------------"); System.out.println("|PACIENTE [1 - Cadas- trar] [2 - Listar]|"); System.out.println("|CONVÊNIO [3 - Cadas- trar] [4 - Listar]|"); System.out.println("|MÉDICO [5 - Cadas- trar] [6 - Listar]|"); System.out.println("|PEDIDO EXAME [7 - CADAS- TRAR] [8 - Listar]|"); System.out.println("|EXAME [9 - CADAS- TRAR] [10 - Listar]|"); System.out.println("|GRUPO EXAME [11 - CADAS- TRAR] [12 - Listar]|"); System.out.println ("-------------------"); System.out.println("| 0 - Sair |"); System.out.print("Escolha uma opção: "); opcao = tec.nextInt(); tec.nextLine(); switch(opcao){ case 1: p[contP] = new Paciente(); p[contP].cadastrar(); U N IC E S U M A R 191 contP++; break; case 2: imprimePaciente(); break; case 3: c[contC] = new Convenio(); c[contC].cadastrar(); contC++; break; case 4: imprimeConvenio(); break; case 5: m[contM] = new Medico(); m[contM].cadastrar(); contM++; break; case 6: imprimeMedico(); break; case 7: pe[contPe] = new PedidoExame(); pe[contPe].cadastrar(); contPe++; int resp = 1; while(resp == 1){ pexe[contPexe] = new ExamePedidoExame(); pexe[contPexe].cadastrar(); contPexe++; System.out.print("Deseja cadastrar outro exame? [1 - Sim][0 - Não]: " ); resp = tec.nextInt(); tec.nextLine(); } U N ID A D E 5 192 break; case 8: imprimePedidoExame(); break; case 9: ex[contEx] = new Exame(); ex[contEx].cadastrar(); contEx++; break; case 10: imprimeExame(); break; case 11: ge[contGe] = new GrupoExame(); ge[contGe].cadastrar(); contGe++; break; case 12: imprimeGrupoExame(); break; default: System.exit(0); } } } } Explicando o código do programa principal, temos a instância de cada classe em seu respectivo objeto, sendo esse do tipo vetor de modificador static. Depois, a declaração dos contadores de cada vetor, também, com modificador static. Em seguida, construímos um menu de opções chamando os métodos de cadastro que instanciam, em cada posição do vetor, uma nova instância da classe. Depois, há a impressão dos dados cadastrados. U N IC E S U M A R 193 CONSIDERAÇÕES FINAIS Chegamos ao final da nossa última unidade do livro. Nesta, apresentamos a você o estudo de caso fictício de uma clínica de exames médicos. Neste estudo, partimos da apresentação do problema, fizemos a análise de requisitos, a modelagem UML do diagrama de classe e, posteriormente, a implementação do estudo de caso na linguagem Java. Porém, como cada analista é único, você pode, depois, remodelar as classes, colocar mais propriedades e comportamentos, bem como abstrair mais informações, ou seja, remodelar a modelagem apresentada. A proposta do sistema era realizar o cadastro e a impressão das informações do sistema. Dessa forma, deixamos algumas lacunas de melhoria a serem imple- mentadas no sistema construído. ■ A primeira lacuna que pode ser melhorada são os tratamentos de exce- ções e validações nos campos informados pelo usuário, por exemplo, CPF, data e hora, entreoutros campos que, no momento, aceitam quaisquer valores por estarem com o tipo String. ■ A segunda lacuna é a implementação de persistência, armazenando os dados em um banco de dados relacional, de forma que possam ser gerados diversos tipos de relatórios e seja facilitada a entrada de valores. ■ A terceira lacuna é deixar a interface do seus sistema mais robusta, com interação homem-máquina mais interessante. Neste caso, pode-se utilizar o recurso de interfaces gráficas com o Swing, saindo do modo de console. ■ A quarta lacuna é trocar os arrays simples pela classe ArrayList, que tra- balha com vetores de forma dinâmica. Esta classe possui diversas funções que facilitam e permitem dinamizar as interações no sistema de forma mais amigável e robusta. Estas são algumas das lacunas que deixamos abertas para que você, aluno(a), possa resgatá-las nas disciplinas de Programação I e Programação II. Isto, com certeza, fará a diferença no mercado de trabalho. 194 na prática 1. Analise a modelagem a seguir. De acordo com ela, implemente as classes e crie uma re- gra de negócios que não permita aos clientes o saque em valor superior ao seu saldo. 2. Em programas orientados ao objeto, normalmente, existem várias classes em vá- rios pacotes e elas interagem entre si. Para utilizar outras classes, tanto as criadas por você quanto as do próprio Java, muitas vezes, é necessário importá-las com o comando import. Considerando estas informações, analise as afirmações a seguir. I - Se a classe Carro está no pacote “raiz”, a classe Moto está no pacote “veiculos” e o pacote “veiculos” está dentro do pacote “raiz”, então, a classe Carro não precisa importar a classe Moto para utilizá-la. II - O comando import não é necessário quando se referencia uma classe do mesmo pacote. III - Não há a necessidade de utilizar import quando a classe consta no pacote “java.lang”. É correto o que se afirma em: a) II, apenas. b) I e II, apenas. c) I e III, apenas. d) II e III, apenas. e) I, II e III. 3. Ao permitirem que os objetos possuam comportamentos, os métodos os deixam muito versáteis e úteis. Um método pode ser chamado por outra classe ou, ainda, chamar outro método dentro da mesma classe onde foi declarado. Considerando as chamadas de métodos que estejam dentro da mesma classe, analise o trecho de código a seguir: ContaCorrente - agencia : int - conta : int - cpf : String - saldo : int + depositar(valor : double) : void + sacar(valor : double) : void + imprimirSaldo() : void 195 na prática public class Principal { public static void main(String[] args) { mostrarDados(25, "João"); } public void mostrarDados(int idade, String nome){ System.out.println("idade:" + idade + "Nome:" + nome;) } } Considerando o código exposto, pode-se afirmar que: a) A saída do sistema será: “Idade: 25 Nome: João”. b) O método mostrarDados não pode ser chamado pelo método main, pois méto- dos estáticos não podem chamar outros métodos. c) O método mostrarDados não pode ser chamado pelo método main, pois méto- dos estáticos só podem chamar métodos estáticos. d) O método mostrarDados não pode ser chamado pelo método main, pois o mé- todo main não pode chamar nenhum outro método. e) O método mostrarDados não pode ser chamado pelo método main, pois méto- dos estáticos só podem chamar métodos que não esperam parâmetros. 4. O desenvolvedor Java de determinada empresa deparou-se com a necessidade de criar uma classe chamada Pessoa para atender às necessidades de negócio da aplicação que ele estava desenvolvendo. Porém, como usava uma biblioteca de classes de terceiros, o desenvolvedor notou que já havia uma classe Pessoa criada. Analisando o estudo de caso exposto, é correto afirmar que, para não haver erros no projeto, o programa deve: a) Criar a classe Pessoa, desde que ela esteja no mesmo pacote da classe Pessoa criada. b) Criar a classe Pessoa, desde que esteja em um pacote diferente da classe Pessoa criada anteriormente. 196 na prática c) Não poderá criar a classe Pessoa, pois o Java não permite a criação de classes com o mesmo nome. d) Criar a classe Pessoa, porém com o P minúsculo, tornando-se pessoa, pois o Java distingue maiúsculas de minúsculas. e) Criar a classe Pessoa normalmente, pois o compilador Java saberá identificar que as classes, apesar de terem o mesmo nome, são diferentes. 5. O trecho de código, a seguir, representa uma classe chamada Pessoa, com as suas definições. Temos, ainda, o método main, realizando a chamada ao método telefonar da classe Pessoa. public static void main(String[] args) { Pessoa pessoa = new Pessoa (); pessoa.telefone = "3377-5654"; pessoa.telefonar (); } public class Pessoa { public String nome; public int idade; public void telefonar () { String telefone = "3322-5599"; System.out.println ("Ligar para" + telefone); } } Considerando o código exposto, caso ele seja executado do jeito que está, o Java lançará algum erro? Se sim, reescreva o código de maneira que possamos chamar o método telefonar a partir do método main, porém de maneira que não lance mais nenhum erro. 197 aprimore-se INTRODUÇÃO À TECNOLOGIA DE OBJETOS Hoje, como a demanda por software novo e mais poderoso está aumentando, cons- truir softwares de maneira rápida, correta e econômica continua a ser um objetivo indefinido. Objetos ou, mais precisamente, as classes de onde os objetos vêm são essencialmente componentes reutilizáveis de software. Há objetos data, objetos data/hora, objetos áudio, objetos vídeo, objetos automóvel, objetos pessoas etc. Quase qualquer substantivo pode ser razoavelmente representado como um ob- jeto de software em termos dos atributos (por exemplo, nome, cor e tamanho) e comportamentos (por exemplo, calcular, mover e comunicar). Grupos de desenvol- vimento de software podem usar uma abordagem modular de projeto e implemen- tação orientados a objetos para que sejam muito mais produtivos do que com as técnicas anteriormente populares como “programação estruturada” — programas orientados a objetos são muitas vezes mais fáceis de entender, corrigir e modificar. O automóvel como um objeto Para ajudar a entender objetos e seus conteúdos, vamos começar com uma analogia simples. Suponha que você queira guiar um carro e fazê-lo andar mais rápido pisan- do no pedal acelerador. O que deve acontecer antes que você possa fazer isso? Bem, antes de poder dirigir um carro, alguém tem de projetá-lo. Um carro tipicamente começa como desenhos de engenharia, semelhantes a plantas que descrevem o projeto de uma casa. Esses desenhos incluem o projeto do pedal do acelerador. O pedal oculta do motorista os complexos mecanismos que realmente fazem o carro ir mais rápido, assim como o pedal de freio “oculta” os mecanismos que diminuem a velocidade do carro e a direção “oculta” os mecanismos que mudam a direção dele. Isso permite que pessoas com pouco ou nenhum conhecimento sobre como moto- res, freios e mecanismos de direção funcionam consigam dirigir um carro facilmen- te. Assim como você não pode cozinhar refeições na planta de uma cozinha, não pode dirigir os desenhos de engenharia de um carro. Antes de poder guiar um carro, ele deve ser construído a partir dos desenhos de engenharia que o descrevem. Um 198 aprimore-se carro pronto tem um pedal de acelerador real para fazê-lo andar mais rápido, mas mesmo isso não é suficiente — o carro não acelerará por conta própria (tomara!), então o motorista deve pressionar o pedal do acelerador. Métodos e classes Vamos usar nosso exemplo do carro para introduzir alguns conceitos fundamentais da programação orientada a objetos. Para realizar uma tarefa em um programa é necessário um método. O método armazena as declarações do programa que, na verdade, executam as tarefas; além disso, ele oculta essas declarações do usuário, assim como o pedal do aceleradorde um carro oculta do motorista os mecanismos para fazer o veículo ir mais rápido. No Java, criamos uma unidade de programa chamada classe para armazenar o conjunto de métodos que executam as tarefas dela. Por exemplo, uma classe que representa uma conta bancária poderia conter um método para fazer depósitos de dinheiro, outro para fazer saques e um terceiro para perguntar qual é o saldo atual. Uma classe é similar em termos do conceito aos desenhos de engenharia de um carro, que armazenam o projeto de um pedal de acelerador, volante etc. Instanciação Assim como alguém tem de fabricar um carro a partir dos desenhos de engenharia antes que possa realmente dirigi-lo, você deve construir um objeto de uma classe antes que um programa possa executar as tarefas que os métodos da classe defi- nem. O processo para fazer isso é chamado instanciação. Um objeto é então referi- do como uma instância da sua classe. Reutilização Assim como os desenhos de engenharia de um carro podem ser reutilizados vá- rias vezes para fabricar muitos carros, você pode reutilizar uma classe muitas vezes 199 aprimore-se para construir vários objetos. A reutilização de classes existentes ao construir novas classes e programas economiza tempo e esforço. Também ajuda a construir siste- mas mais confiáveis e eficientes, porque classes e componentes existentes costu- mam passar por extensos testes, depuração e ajuste de desempenho. Assim como a noção das partes intercambiáveis foi crucial para a Revolução Industrial, classes reutilizáveis são fundamentais para a revolução de software que foi estimulada pela tecnologia de objetos. Mensagens e chamadas de método 1. Ao dirigir um carro, o ato de pressionar o acelerador envia uma mensagem para o veículo realizar uma tarefa — isto é, ir mais rápido. Da mesma forma, você envia mensagens para um objeto. Cada mensagem é implementada como uma chamada de método que informa a um método do objeto a maneira de realizar sua tarefa. Por exemplo, um programa pode chamar o método depósito de um objeto conta bancária para aumentar o saldo da conta. Atributos e variáveis de instância Um carro, além de ter a capacidade de realizar tarefas, também tem atributos, como cor, número de portas, quantidade de gasolina no tanque, velocidade atual e regis- tro das milhas totais dirigidas (isto é, a leitura do odômetro). Assim como suas ca- pacidades, os atributos do carro são representados como parte do seu projeto nos diagramas de engenharia (que, por exemplo, incluem um odômetro e um medidor de combustível). Ao dirigir um carro real, esses atributos são incorporados a ele. Cada carro mantém seus próprios atributos. Cada carro sabe a quantidade de gaso- lina que há no seu tanque, mas desconhece quanto há no tanque de outros carros. Um objeto, da mesma forma, tem atributos que ele incorpora à medida que é usado em um programa. Esses atributos são especificados como parte da classe do objeto. Por exemplo, um objeto conta bancária tem um atributo saldo que repre- senta a quantidade de dinheiro disponível. Cada objeto conta bancária sabe o saldo 200 aprimore-se que ele representa, mas não os saldos de outras contas bancárias. Os atributos são especificados pelas variáveis de instância da classe. Encapsulamento e ocultamento de informações Classes (e seus objetos) encapsulam, isto é, contêm seus atributos e métodos. Os atributos e métodos de uma classe (e de seu objeto) estão intimamente relaciona- dos. Os objetos podem se comunicar entre si, mas eles em geral não sabem como outros objetos são implementados — os detalhes de implementação permanecem ocultos dentro dos próprios objetos. Esse ocultamento de informações, como vere- mos, é crucial à boa engenharia de software. Herança Uma nova classe de objetos pode ser criada convenientemente por meio de herança — ela (chamada subclasse) começa com as características de uma classe existente (chamada superclasse), possivelmente personalizando-as e adicionando aspectos próprios. Fonte: Deitel e Deitel (2017, p. 8-10). 201 eu recomendo! Java – Guia do Programador Atualizado para Java 8 Autor: Peter Jandl Junior Editora: NovaTec Sinopse: desenvolva aplicações usando o Java 8! Explore todas as vantagens da programação orientada a objetos por meio da ele- gante sintaxe Java. Aprenda a usar sobrecarga, herança, classes abstratas, polimorfismo, interfaces, genéricos e expressões lamb- da. Construa aplicações gráficas utilizando componentes Swing, tornando-as mul- titarefa com as threads. Opere dados de qualquer tipo com fluxos de dados e ar- quivos, organizando-os por meio das coleções. Manipule coleções com operações de filtragem, mapeamento e redução. Implemente aplicações comerciais capazes de acessar bancos de dados com a API JDBC ou de se comunicar em rede local e na Internet, por meio dos sockets e datagramas. Java – Guia do Programador abrange o conteúdo essencial para as certificações Oracle Certified Associate e Oracle Certified Professional para Java SE 5, SE 6, SE 7 e SE 8. Este livro contém quase 300 exemplos completos, comentados em detalhe, muitos fragmentos de código prontos para uso, além de diagramas, telas e dezenas de resumos da API Java. Inclui, também, mais de 120 exercícios de revisão. Todo o material de apoio encontra-se disponível no site da Novatec Editora. Principais tópicos abordados: sintaxe java orientação a objetos, sobrecarga e sobreposição, herança e polimorfismo, classes abstratas interfaces, expressões lambda, referências para métodos genéricos, componentes swing, coleções threads, arquivos e streams JDBC sockets e datagramas. livro Aula sobre interface gráfica em Java, com o professor Yuri Lacerda. É abordado, nesta aula: por que uma interface gráfica? Plug-ins nas IDEs, estudo de caso de cadastro de contato, formulário em interface gráfica, swing: JFrame, JLabel, JText- Field, JMenu, Action Listener, programando no Eclipse. https://www.youtube.com/watch?v=p9mZHeWVsAg conecte-se 202 conclusão geral conclusão geral 202 conclusão geral conclusão geral Neste livro, procuramos mostrar a importância da disciplina engenharia de software e como ela, aliada ao desenvolvimento de software, pode ser aplicada durante o de- senvolvimento de um sistema. A fim de possibilitar o seu entendimento, na Unidade 1, foram estudados os conceitos de software e a sua engenharia. Na Unidade 2, abordamos os recursos que garantem a integridade e o controle de acesso aos atributos e métodos, o encapsulamento. Por meios dos modificado- res de acesso, verificamos que é possível dar permissão de acesso a classes que os utilizam, ou seja, estudamos os modificadores abstract, public, private e protected. Na Unidade 3, abordamos o conceito de herança: nele, estudamos, na prática, como aplicar a herança entre classes e tirar proveito desse recurso por meio do polimorfismo, que é a capacidade de especializar um método herdado do pai. Além da especialização, percebemos que, com a herança, reduzimos a quantidade de có- digos, contudo aumentamos a complexidade da aplicação. Na Unidade 4, abordamos os recursos que funcionam como modelo de desen- volvimento de outros de contrato: as classes abstratas e interfaces, respectivamen- te. Na classe abstrata, temos um modelo de classes que pode ser utilizado e adap- tado às necessidades do desenvolvedor. Já as interfaces funcionam como contrato, em que todos os métodos devem ser implementados. Para finalizar, trabalhamos, na Unidade 5, com a implementação do estudo de caso. Primeiramente, realizamos um estudo fazendo algumas alterações nele, o que possibilitou o desenvolvimento de um MVP (Minimum Viable Product, ou seja, Míni- mo Produto Viável). Esperamos ter alcançado o objetivo inicial, que era o de mostrar a importância da engenharia de software. Desejamos que você seja muito feliz profissionalmente, utilizando os conceitos apresentados aqui e, se pudermos,de alguma forma, ajudá- -lo(a), estamos à sua disposição. referências 203 BOOCH, G.; RUMBAUGH, J.; JACOBSON, I. UML: Guia do Usuário. 2. ed. São Paulo: Campus, 2006. CLARO, D. B.; SOBRAL, J. B. M. Programação em Java. Florianópolis: Copyleft Pearson Educa- tion, 2008. DEITEL, H.; DEITEL, P. Java – Como Programar. 10. ed. São Paulo: Prentice Hall Brasil, 2017. GUEDES, G. T. A. UML 2: guia prático. São Paulo: Novatec, 2007. GUEDES, G. T. A. UML 2: uma abordagem prática. 2. ed. São Paulo: Novatec, 2011. LIMA, A. S. UML 2.0: do requisito à solução. São Paulo: Érica, 2009. MELO, A. C. Desenvolvendo aplicações com UML 2.0: do conceitual à implementação. Rio de Janeiro: Brasport, 2004. RICARTE, I. L. M. Departamento de Engenharia de Computação e Automação Industrial. Fa- culdade de Engenharia Elétrica e de Computação. Programação Orientada a Objetos: Uma Abordagem com Java. Campinas: Unicamp, 2001. Aulas. 118p. SOMMERVILLE, I. Engenharia de Software. 9. ed. São Paulo: Pearson Prentice Hall, 2011. REFERÊNCIA ON-LINE 1 Em: https:/blog.caelum.com.br/revisitando-a-orientacao-a-objetos-encapsulamento-no-ja- va/. Acesso em: 29 out. 2019. gabarito 204 UNIDADE 1 1. 2. 3. D 4. Funcionario + Matricula : Integer + CpfCnpj : String + NomeFunc : String + DtAdmissao : Date + IncluirNovoFunc() : void + BuscaFunc() : void + ExcluirFunc() : void Dependente - IdDep : int - NomeDep : String + DtNascimento : Date + IdFunc : Integer + IncluirNovoDep() : void + ExcluirDep() : void 0..* Ônibus - placa : char - cor : char - nrportas : int - tipo_automovel : int - quilometragem : long - renavam : long - valor_locacao : double + conAuto() : char Modelo - descricao : string + consultaMod() : void Marca - descricao : string + consultaMarca() : void Locação - dt_locacao : date - hora_locacao : string - dt_devolucao : date - hora_devolucao : string - quilometragem : long - devolvido : int + consultaCli() : void Cliente - cpf_cli : long - nome_cli : string - end_cli : string - tel_cli : string - email_cli : string + consultaCli() : void 0..* 1..* 1..* 1..* compõe tem realiza possui Produto - nome : String - unidadeCompra : int -qntPrevistoMes : double - precoEstimado : double + incluir(p1 : Produto) : void ListaCompra - mes : int + incluir(mes : int) : void + calcularTotal(mes : int) : void ItemCompra - qtdEfetivaCompra : int + incluir(i1 : ItemCompra) : void 0..* 1..* gabarito 205 5. Os três modos de visibilidade são: o pú- blico, o protegido e o privado. • O símbolo mais (+) indica visibilidade pública, ou seja, significa que o atribu- to ou o método pode ser utilizado por objetos de qualquer classe. • O símbolo sustenido (#) indica que a visibilidade é protegida, ou seja, deter- mina que, apenas, objetos da classe possuidora do atributo ou do método ou de suas subclasses podem acessá-lo. • O símbolo menos (-) indica que a visi- bilidade é privada, ou seja, somente, os objetos da classe possuidora do atribu- to ou método poderão utilizá-lo. UNIDADE 2 1. C. 2. B. 3. D. 4. Classe: casa. Atributos: janela, portas e quartos. Método: inserir(), listar() e excluir(). 5. Encapsulamento. O controle de acesso aos dados de um objeto envolve uma das principais ca- racterísticas da programação orientada a objetos: o encapsulamento. Ele é im- plementado pela restrição de acesso aos atributos. Se você não quiser que um atri- buto seja, facilmente, modificado, pode torná-lo privado e permitir a sua modifi- cação apenas por meio de um método público. Com esta abordagem, você pode validar os dados antes de atribuí-los. UNIDADE 3 1. package grupo; public class Grupo { public String imprimeDescricao() { return "Grupo"; } public static void main(String[] args) { System.out.println(new Grupo(). imprimeDescricao()); System.out.println(new SubGrupo(). imprimeDescricao()); System.out.println(new Produto(). imprimeDescricao()); } } ------------------------------ package grupo; public class SubGrupo extends Grupo { @Override public String imprimeDescricao() { return "Sub-Grupo"; } } ------------------------------ package grupo; public class Produto extends SubGrupo { @Override public String imprimeDescricao() { return "Produto"; } } 2. package Principal; public class Materiais { protected String assunto; protected String titulo; gabarito 206 public String getAssunto() { return assunto; } public void setAssunto(String assunto) { this.assunto = assunto; } public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } public void mostrarGeral(){ System.out.println("Assunto:"+ this.getAssunto()); System.out.println("Título: "+this.getTitulo()); } } ------------------------------ package Principal; public class Livros extends Materiais{ private String editora, isbn, autor; private int edicao; public String getEditora() { return editora; } public void setEditora(String editora) { this.editora = editora; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getAutor() { return autor; } public void setAutor(String autor) { this.autor = autor; } public int getEdicao() { return edicao; } public void setEdicao(int edicao) { this.edicao = edicao; } @Override public void mostrarGeral(){ super.mostrarGeral(); System.out.println("Au- tor:"+this.getAutor()); System.out.println("ISBN:"+this. getIsbn()); System.out.println("Edição:"+- this.getEdicao()); System.out.println("Editora:"+- this.getEditora()); } } ------------------------------ package Principal; public class Revista extends Materiais{ private String colecao, editora; gabarito 207 public String getColecao() { return colecao; } public void setColecao(String colecao) { this.colecao = colecao; } public String getEditora() { return editora; } public void setEditora(String editora) { this.editora = editora; } @Override public void mostrarGeral(){ super.mostrarGeral(); System.out.println("Coleção:"+- this.getColecao()); System.out.println("Editora:"+- this.getEditora()); } } 3. package Principal; public class Principal { public static void main(String[] args) { Livros l1 = new Livros(); System.out.println("-- Dados do Livro --"); l1.setAssunto("Modelagem UME e Programação Orientada a Objetos em Java"); l1.setAutor("Rafael Alves Flo- rindo"); l1.setEdicao(1); l1.setEditora("Unicesumar"); l1.setIsbn("2831283912047"); l1.setTitulo("Programação III"); l1.mostrarGeral(); System.out.println("-- Dados da Revista --"); Revista r1 = new Revista(); r1.setAssunto("Modelagem UME e Programação Orientada a Objetos em Java"); r1.setTitulo("Programação III"); r1.setColecao("Programação"); r1.setEditora("Unicesumar"); r1.mostrarGeral(); } } 4. public class Ouvidoria { private String nome, email, assunto; public String getAssunto() { return assunto; } public void setAssunto(String assunto) { this.assunto = assunto; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEmail() { gabarito 208 return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "Ouvidoria{" + "nome=" + nome + ", email=" + email + ", as- sunto=" + assunto + '}'; } public Ouvidoria(String nome, String email, String assunto) { this.nome = nome; this.email = email; this.assunto = assunto; ------------------------------ import java.util.Scanner; public class Principal { public static void main(String[] args) { String nome, email, assunto; Scanner tec = new Scanner(System. in); System.out.println("Informe o seu nome: "); nome = tec.nextLine(); System.out.println("Informeo seu email: "); email = tec.nextLine(); System.out.println("Informe o seu Assunto: "); assunto = tec.nextLine(); Ouvidoria ouvidoria = new Ouvido- ria(nome, email, assunto); System.out.println(ouvidoria. toString()); } } 5. import java.util.Scanner; public class Pagamento { private double valor; private String titulo, propretario; public double getValor() { return valor; } public void setValor(double valor) { this.valor = valor; } public String getTitulo() { return titulo; } public void setTitulo(String ti- tulo) { this.titulo = titulo; } public String getPropretario() { return propretario; } public void setPropretario(S- tring propretario) { this.propretario = propretario; } public void inserir(){ Scanner tec = new Scanner(System. in); System.out.println("Digite o Proprietario"); this.setPropretario(tec.nextLi- ne()); System.out.println("Digite o Ti- tulo"); gabarito 209 this.setTitulo(tec.nextLine()); System.out.println("Digite o va- lor"); this.setValor(Double.parseDoub- le(tec.nextLine())); } public void imprimir(){ System.out.println("Titulo: "+getTitulo()); System.out.println("Proprietá- rio: "+getPropretario()); System.out.println("Valor: "+ge- tValor()); S y s t e m . o u t . p r i n t ln("----------------------"); } } ------------------------ public class Principal { static Pagamento p[] = new Paga- mento[4]; public static int ultimo = 0; public static void imprimeP(){ for(int i = 0; i < ultimo; i++){ p[i].imprimir(); System.out.println ("--------- -------------"); } } public static void main(String[] args) { int i; for (i = 0; i < 4; i++){ p[i] = new Pagamento(); p[i].inserir(); ultimo++; } imprimeP(); } } UNIDADE 4 1. A 2. B 3. C 4. Classe Pessoa package livroabstrato; abstract public class Pessoa { private String nome, dataNascimento; public Pessoa() { } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getDataNascimento() { return dataNascimento; } public void setDataNascimento(S- tring dataNascimento) { this.dataNascimento = dataNasci- mento; } public abstract void cadastro(); } Classe Colaborador package livroabstrato; public class Colaborador extends Pessoa { @Override public void cadastro() { this.setNome("Rafael"); this.setDataNascimento("02/12/1982"); gabarito 210 } public void listar(){ System.out.println("Nome: " + this.getNome()); System.out.println("Data de Nas- cimento: " + this.getDataNascimen- to()); } } classe Principal package livroabstrato; public class Principal { public static void main(String[] args) { Colaborador colab1 = new Colabo- rador(); colab1.cadastro(); colab1.listar(); } } 5. Interface Controle package interfacecontrolelivro; public interface Controle { public void cadastrar(); public void listar(); } Classe Livro package interfacecontrolelivro; public class Livro implements Controle { private String autor; public String getAutor() { return autor; } public void setAutor(String autor){ this.autor = autor; } @Override public void cadastrar() { this.setAutor("Rafael Alves Flo- rindo"); } @Override public void listar() { System.out.println("Autor: " + this.getAutor()); } } Classe Principal package interfacecontrolelivro; public class Principal { public static void main(String[] args) { Livro livro = new Livro(); livro.cadastrar(); livro.listar(); } } UNIDADE 5 1. package banco; public class ContaCorrente { private String agencia; private String conta; private String cpf; private double saldo=0; public String getAgencia() { gabarito 211 return agencia; } public void setAgencia(String agencia) { this.agencia = agencia; } public String getConta() { return conta; } public void setConta(String conta) { this.conta = conta; } public String getCpf() { return cpf; } public void setCpf(String cpf) { this.cpf = cpf; } public double getSaldo() { return saldo; } public void setSaldo(double saldo) { this.saldo = saldo; } public void depositar(double valor){ this.setSaldo(this.getSal- do() + valor); System.out.println(valor + " Valor Depositado com sucesso"); } public void sacar(double valor){ if (this.getSaldo() < valor){ System.out.println("Impos- sível Sacar, saldo insuficiente"); }else{ this.setSaldo(this.getSal- do() - valor); System.out.println(valor + " Valor resgatado com sucesso"); } } public void imprimirSaldo(){ System.out.print ln("------ ---------------------------"); System.out.println("Agen- cia: " + this.getAgencia()); System.out.println("Conta: " + this.getConta()); System.out.println("CPF: " + this.getCpf()); System.out.println("Saldo: " + this.getSaldo()); System.out.print ln("------ ---------------------------"); } } package banco; public class Movimento { public static void main(String[] args) { ContaCorrente conta1 = new ContaCorrente(); conta1.setAgencia("Centro"); conta1.setConta("12345"); conta1.setCpf("123456789"); conta1.imprimirSaldo(); conta1.depositar(500.00); conta1.imprimirSaldo(); conta1.sacar(200.00); conta1.imprimirSaldo(); conta1.sacar(400.00); conta1.imprimirSaldo(); } } gabarito 212 2) D. 3) C. 4) B. 5) Sim, ocorre um problema. O método main está tentando atribuir um va- lor de telefone à variável telefone, que é local (pertencente ao méto- do “telefonar”, portanto, só pode ser visualizada e acessada de dentro dele). Para solucionar o problema, uma das alternativas é tornar a va- riável “telefonar” uma variável de instância, deixando-a no escopo da classe. A seguir, é demonstrado o resultado da correção. public static void main(String[] args) { Pessoa pessoa = new Pessoa (); pessoa.telefone = "3377-5654"; pessoa.telefonar (); } public class Pessoa { public String nome; public int idade; public String telefone = "3322-5599"; public void telefonar() { System.out.println ("Ligar para" + telefone); } } anotações anotações anotações anotações MODELAGEM DE SISTEMAS MODIFICADORES JAVA e ENCAPSULAMENTO HERANÇA E POLIMORFISMO CLASSES ABSTRATAS e Interfaces ESTUDO DE CASO: ANÁLISE CLÍNICA conclusão geral h.j3faof3fxm9o _GoBack