100% acharam este documento útil (3 votos)
221 visualizações180 páginas

1006 Programacao Orientada A Objetos Unopar

Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
100% acharam este documento útil (3 votos)
221 visualizações180 páginas

1006 Programacao Orientada A Objetos Unopar

Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
Você está na página 1/ 180

Programação

orientada a
objetos
Programação orientada a
objetos

Adriano Sepe
Roque Maitino Neto
© 2017 por Editora e Distribuidora Educacional S.A.
Todos os direitos reservados. Nenhuma parte desta publicação poderá ser reproduzida ou transmitida de qualquer
modo ou por qualquer outro meio, eletrônico ou mecânico, incluindo fotocópia, gravação ou qualquer outro tipo
de sistema de armazenamento e transmissão de informação, sem prévia autorização, por escrito, da Editora e
Distribuidora Educacional S.A.

Presidente
Rodrigo Galindo

Vice-Presidente Acadêmico de Graduação


Mário Ghio Júnior

Conselho Acadêmico
Alberto S. Santana
Ana Lucia Jankovic Barduchi
Camila Cardoso Rotella
Cristiane Lisandra Danna
Danielly Nunes Andrade Noé
Emanuel Santana
Grasiele Aparecida Lourenço
Lidiane Cristina Vivaldini Olo
Paulo Heraldo Costa do Valle
Thatiane Cristina dos Santos de Carvalho Ribeiro

Revisão Técnica
Ruy Flávio de Oliveira

Editorial
Adilson Braga Fontes
André Augusto de Andrade Ramos
Cristiane Lisandra Danna
Diogo Ribeiro Garcia
Emanuel Santana
Erick Silva Griep
Lidiane Cristina Vivaldini Olo

Dados Internacionais de Catalogação na Publicação (CIP)

Sepe, Adriano
S479p Programação orientada a objetos / Adriano Sepe, Roque
Maitino Neto. – Londrina : Editora e Distribuidora Educacional
S.A., 2017.
176 p.

ISBN 978-85-8482-915-6

1. Programação orientada a objeto. 2. Análise de


sistemas. I. Maitino Neto, Roque. II. Título.
CDD 005.1

2017
Editora e Distribuidora Educacional S.A.
Avenida Paris, 675 – Parque Residencial João Piza
CEP: 86041-100 — Londrina — PR
e-mail: editora.educacional@kroton.com.br
Homepage: http://www.kroton.com.br/
Sumário
Unidade 1 | Problemas no desenvolvimento de software 7

Seção 1 - Arte do desenvolvimento 11


1.1 | Visão geral 11
1.2 | Introdução ao desenvolvimento 12
1.3 | Complexidades relacionadas 13
1.4 | Tipo de dados 14
1.5 | Tipo de dados complexos 17

Seção 2 - Limitações em evidência 27


2.1 | Paradigma estruturado 27
2.2 | Limitações associadas à linguagem 39

Seção 3 - Propondo organização às linguagens 47


3.1 | Propondo organização às linguagens 47

Unidade 2 | Introdução à programação orientada a objetos 57

Seção 1 - Modelos 61
1.1 | Conceito 61
1.2 | Exemplificando 62

Seção 2 - O paradigma da orientação a objetos 69


2.1 | Conceito 69
2.2 | Fundamentos da orientação a objeto 70

Unidade 3 | Classes e objetos 79

Seção 1 - Aprendendo através da prática 83


1.1 | Definições 83
1.2 | Introduzindo uma dinâmica 84
1.3 | Projeto estruturado 85
1.4 | Olhando para objetos 100

Seção 2 - Base conceitual 105


2.1 | Classes 105
2.2 | Atributos 106
2.3 | Objetos 108
2.4 | Métodos 111
2.5 | Modificadores de visibilidade 113
2.6 | Flexibilizando métodos 116
2.7 | Métodos construtores 120
2.8 | Membros da classe 123

Seção 3 - Codificando orientado a objetos 129


3.1 | Resolvendo a dinâmica 129
3.2 | Classe produto 130
3.3 | Classe tela 132
3.4 | Classe estoque 136
3.5 | Classe program 138

Unidade 4 | Herança e polimorfismo 151

Seção 1 - Herança 155


1.1 | Conceito de herança 155
1.2 | Implementação em Java 156
1.3 | Delegação ou composição 161

Seção 2 - Polimorfismo 165


2.1 | Utilização do polimorfismo 165
2.2 | Acesso a métodos das superclasses 168
Apresentação
Este material didático possui a função de apresentar aos
estudantes de engenharia conceitos relacionados à programação
orientada a objetos.

Discutiremos como melhorar a qualidade do código que


criamos, utilizando a técnica mencionada de programação
orientada a objetos. Discutiremos, também, algumas dificuldades
que surgem quando não adotamos técnicas, ou até mesmo quando
são utilizados mecanismos que não dispõem de importantes
vantagens existentes na orientação a objetos, como exemplo,
organização, escopo e segurança. Vamos imergir em uma visão
técnica e atual a respeito de como as linguagens podem facilitar a
vida dos desenvolvedores, ou então, dificultá-la.

Vamos, a partir desse ponto de entendimento, discursar e


principalmente, evidenciar melhorias que tornam a etapa de
codificação mais simples. Só então partiremos para conceitos e
práticas relacionadas à programação orientada a objetos, trazendo
na medida do possível elementos reais que justificarão o empenho
sobre tais conceitos. Ao finalizar o estudo desse material, é
esperado que o aluno consiga visualizar as limitações existentes
no desenvolvimento utilizando uma técnica mais elementar, ou as
consequências de não utilizar técnica alguma. Veremos também
como a qualidade do seu código pode ser potencializada adotando
elementos básicos de organização extraídos dos conceitos da
programação orientada a objetos. Para demonstração de tais
conceitos utilizaremos a linguagem C#, o que servirá como uma
introdução à programação, mas sempre reforçando a importância
dos conceitos, e não da linguagem.

Não obstante o material não demonstrará a construção de


uma aplicação funcional, em vista da priorização dos conceitos
elementares. Esperamos que o leitor possa compreender a
importância da técnica, e possa também procurar aplicá-la no
desenvolvimento de software.
Tendo em vista que apresentaremos uma técnica, essa por
sua vez poderá ser utilizada na construção de aplicativos mais
complexos. Portanto, devemos compreender que quanto maior a
determinação na aplicação dos conceitos, maior será a capacidade
de enxergar as vantagens relacionadas.

Avaliando a técnica de desenvolvimento orientado a objetos


podemos afirmar que o seu uso trouxe vantagens significativas
para a indústria tecnológica, e consequentemente um profissional
com domínio efetivo terá condições de se diferenciar no mercado.

Através da Unidade 1, o aluno visualizará algumas possíveis


limitações no desenvolvimento, e como poderá torná-lo mais
claro observando um código existente e um código proposto. Já
a Unidade 2 abordará conceitos introdutórios, relativos a como
observamos os problemas e visualizamos as soluções, e como isso
se transfere para a programação.

Concluímos então através das unidades 3 e 4, em que


efetivamente o aluno poderá perceber como todos os conceitos
definidos nas unidades anteriores estão relacionados e podem ser
aplicados, por meio de uma linguagem. Nesse ponto, o enfoque
continua sendo a técnica, mas com uma pegada prática, pois
os conceitos são demonstrados através de um ambiente de
desenvolvimento integrado (IDE).

Através dessa estrutura de aprendizagem esperamos garantir


ao aluno um início sólido e seguro, criando uma base capaz de
ajudá-lo em outros futuros desafios, relacionados à programação
e desenvolvimento de soluções computacionais.

Bons estudos!
Unidade 1

Problemas no
desenvolvimento de software

Adriano Sepe

Objetivos de aprendizagem
• Entender problemas existentes no processo de
desenvolvimento de software.

• Enxergar limitações existentes em técnicas que


antecederam a técnica de orientação a objetos.

• Avaliar a evolução através de um problema modelo.

• Discutir como utilizar elementos e visões simples de


organização e poderemos partir de um nível para
outro.

• Visualizar a evolução e onde devemos e podemos


chegar.

Seção 1 | Arte do desenvolvimento


Será apresentada uma visão sobre desenvolvimento de software e
também algumas limitações subjacentes. Exploraremos alguns conceitos
técnicos que são primários para a dimensão dos conceitos fundamentais
desta obra, bem como desta unidade.

Seção 2 | Limitações em evidência


Limitações no desenvolvimento estão relacionadas às limitações
individuais ou da tecnologia, vamos enquadrar algumas limitações da
tecnologia e como podem ser um limitador.
Seção 3 | Propondo organização às linguagens
A partir das limitações apresentadas, vamos considerar algumas ações
que poderiam tornar o desenvolvimento no mínimo mais simples.
Introdução à unidade
Nesta unidade, vamos avaliar uma evolução na forma como
organizamos o nosso código, problemas relacionados ao
desenvolvimento. Através de um problema modelo abordaremos
uma série de restruturações, e na medida do possível apontaremos
as vantagens. Existem vários conceitos envolvidos nos códigos
apresentados, até porque foram utilizadas técnicas que não
necessariamente são de conhecimento da maioria, principalmente
se avaliarmos o perfil do leitor. Nesse caso é esperado que o leitor
possa extrair e racionalizar aquilo que é conceitual, e que está
sendo sintetizado pelo autor.
Para essa unidade os códigos foram desenvolvidos utilizando
a linguagem C. Utilize os comentários de código para a devida
compreensão. Se houver a necessidade, o leitor poderá procurar
os seguintes conceitos relacionados à linguagem: ponteiro,
endereçamento indireto de memória, funções, passagem de
parâmetros por valor e por referência.
Algumas instruções não serão discutidas em detalhes, isso para
não perdermos o foco principal, então, caso fique alguma dúvida,
como já mencionado, procure outra fonte de conhecimento
com foco na linguagem C. Vamos apontar vários conteúdos
correlacionados, aproveitem!

U1 - Problemas no desenvolvimento de software 9


10 U1 - Problemas no desenvolvimento de software
Seção 1

Arte do desenvolvimento
Introdução à seção

Vamos avaliar algumas características técnicas, que serão


necessárias para mergulharmos, em detalhes, em como podemos
melhorar a forma com que organizamos o nosso código, como
podemos adequar a expectativa com a realidade, buscando a
excelência através de conceitos.

1.1 Visão geral


O desenvolvimento de software, sem dúvida, é uma arte
que exige muito dos seus artistas. Isso porque a cada dia as
aplicações que utilizamos, e principalmente as aplicações que
desejamos utilizar, exigem cada vez mais a capacidade cognitiva
dos envolvidos na elaboração e construção. As aplicações
estão mudando a forma como enxergamos o mundo, desde
uma simples corrida para mantermos a saúde até uma viagem
internacional podem estar associadas ao uso de um ou mais
aplicativos. Dentro dessa perspectiva devemos entender que, cada
vez mais as aplicações serão complexas e dependentes de ótimas
técnicas de desenvolvimento, teste, avaliação, distribuição, e até
de comercialização (MEDEIROS, 2004).

Sempre que olhamos para uma solução computacional


devemos enxergar que ela nada mais é que um conjunto de milhões
de instruções de máquina, que quando executadas corretamente
atingem o seu propósito. A construção de uma pequena instrução
não demora mais que alguns segundos, mas garantir que essas
instruções no final das contas sejam executadas uma após a outra,
considerando uma sequência lógica, considerando uma série de
entradas de dados e por fim gerando uma série de saída de dados.
Isso realmente é complexo. Atualmente ainda temos alguns fatores
que preocupam qualquer desenvolvedor experiente, entre estes

U1 - Problemas no desenvolvimento de software 11


podemos destacar o fator chamado Big Data, que podemos associar
ao volume de dados. Existem outros, por exemplo, usabilidade,
portabilidade, performance, estabilidade, manutenabilidade, e
para ficarmos por aqui, custo, isso é importantíssimo (MEDEIROS,
2004).

1.2 Introdução ao desenvolvimento


A construção de uma solução computacional envolve várias
etapas, sendo que cada uma delas exige conhecimentos e
ferramentas muitas vezes distintas. Quando iniciamos os estudos
sobre desenvolvimento de software encontramos vários conceitos
complexos e abstratos, difíceis de serem compreendidos, mas se
mantivermos o foco poderemos usufruir as vantagens relacionadas
às técnicas. Vantagens como produtividade, melhor qualidade nos
artefatos desenvolvidos, robustez, eficácia, eficiência e outros
atributos que podem ser um ponto decisivo em um mercado
extremamente competitivo, como o de tecnologia de informação
(SOMMERVILLE, 2007).

Para desenvolvermos um software, devemos explorar a nossa


capacidade de raciocinar de forma lógica, lembre-se sempre de
que nos colocarmos na condição de comandantes de algum
dispositivo computacional, esse, sim, respeitará as instruções
que devem ser racionais e vão estabelecer uma lógica para
possivelmente resolver um problema. Isso já é algo rotineiro, visto
que quando encontramos um problema buscamos estabelecer
uma sequência para resolvê-lo. Por isso vários cursos de graduação
introduzem a programação de computador através de alguma
disciplina relacionada à lógica, às vezes, sem a utilização de alguma
linguagem, apenas através de esboços de raciocínios lógicos.

Alguns alunos, quando iniciam suas trilhas no processo de


aprendizagem, para serem profissionalmente desenvolvedores
de soluções computacionais, até pelo campo de visão associado
à maturidade, assumem que ao dominarem uma linguagem por
completo, então estarão garantidos profissionalmente. Essa visão
tende a não ser exata, pois com a evolução das tecnologias, o
domínio efetivo sobre todos os elementos envolvidos passou a ser
o maior desafio.

12 U1 - Problemas no desenvolvimento de software


Vamos avaliar algumas proposições.

Quando nos deparamos com lógicas mais complexas, em


um ambiente que em seu entorno dispõe de um alto grau de
complexidade, então a linguagem será o menor dos problemas.
Vamos transformar essa afirmação em um exemplo mais real.
Se optarmos por utilizar uma linguagem como Java, antes de
escrevermos um simples “hello world”, vamos nos deparar com a
seguinte questão: “Qual é a plataforma que se pretende utilizar?”
O aplicativo será no estilo terminal de comandos (Estilo DOS /
Terminal Bash), ou então desktop (interface gráfica), ou então uma
aplicação Web? Pode, ainda, ser um serviço que roda em segundo
plano no sistema operacional. Sem alongar-se sobre quais os
pacotes que temos em Java, esse não é o propósito deste livro,
muito menos desta seção.

Existem excelentes profissionais ganhando muito dinheiro


em vários estilos de aplicativos que possamos imaginar, existem
profissionais sendo valorizados em suas equipes e empresas,
trabalhando com tecnologias ditas obsoletas e até aquelas que
foram descontinuadas.

1.3 Complexidades relacionadas


À medida que nos debruçamos em como podemos instruir
as máquinas para fazerem tarefas, possivelmente vamos
encontrar limitações que são inerentes a este processo. Uma
grande dificuldade que encontramos está no fato que programas
computacionais são criados para resolver problemas complexos.
Essa junção de complexidade de instruir a máquina, associada
à complexidade relativa à própria tarefa que se deseja resolver,
cria um ambiente insustentável, visto as limitações que nós seres
humanos possuímos.

Devemos perceber que além da complexidade natural das


tarefas que comumente resolvemos através da computação,
também existem complexidades relacionadas à quantidade de
instruções que vão sendo incorporadas aos programas a cada
versão, isso porque é natural que novas necessidades sejam
atendidas através de novas versões. Também devemos destacar

U1 - Problemas no desenvolvimento de software 13


que existem mudanças do nosso dia a dia que vão afetar também
os softwares, como maiores exigências de segurança, processos
e procedimentos baseados em leis vigentes, modo com que as
pessoas interagem com os computadores. Veja, antes estávamos
limitados a uma mesa com um computador, teclado e mouse,
hoje estamos livres com nossos dispositivos móveis. Existem
mudanças relacionadas à própria evolução das tecnologias, que
normalmente trazem vantagens para os usuários, mas que geram
demandas de novas técnicas, ferramentas e principalmente a boa
vontade de se estudar e aprender (SOMMERVILLE, 2007).

1.4 Tipo de dados


Utilizamos o computador basicamente como um equipamento
que, mediante a entrada de dados, realiza processamento e gera
um resultado. Dentro dessa linha de pensamento compreendemos
que o computador em si tem uma série de limitações, mas possui
uma vantagem significativa, que é a sua capacidade de processar,
e processar muito rápido. Agora no escopo da programação, cada
dado – seja de entrada ou de saída – deverá ser representado
na forma que a máquina consiga entendê-lo. Para isso devemos
conseguir distinguir entre números inteiros, números flutuantes,
cadeias de caracteres, cadeias de bytes ou então valores
verdades. Essa necessidade de conseguirmos discriminar para o
computador o tipo de um determinado dado existe exatamente
para que o processamento seja facilitado, para que a vantagem
seja evidenciada (SCHILDT, 1996).

Ao iniciarmos o estudo de uma linguagem de programação, por


exemplo a linguagem C, vamos entender que para manipularmos
um determinado dado necessariamente deveremos categorizá-lo
em um dos tipos disponíveis, ou então através de um tipo criado
por nós mesmos. Isso abordaremos mais à frente.

Para melhor entendermos vamos apresentar os tipos primários,


mais conhecidos como primitivos, para a linguagem C, mas
devemos extrair exatamente o conceito, não somente os tipos. Até
porque isso pode variar em parte ou totalmente quando avaliamos
uma outra linguagem.

14 U1 - Problemas no desenvolvimento de software


Na linguagem C existem cinco tipos básicos de dados: caractere,
inteiro, ponto flutuante, ponto flutuante de precisão dupla e sem
valor, respectivamente utilizamos as seguintes palavras reservadas
(char, int, float, double e void). Cada tipo numérico determina uma
faixa de valores bem como uma quantidade de bytes necessários
para seu armazenamento. Já um caractere deverá ocupar
possivelmente um byte. Essa variação na quantidade de bytes pode
variar de acordo com a arquitetura do processador, características
do compilador e até questões de padronização (SCHILDT,
1996). Essa variação pode inicialmente criar uma confusão, mas
demonstra uma vantagem efetiva da programação, a abstração.
Quando identificamos a necessidade de trabalharmos com um
dado numérico inteiro já conseguimos identificar o tipo de dado
necessário, ainda relativo a faixa de valor, deveremos utilizar alguns
modificadores de tipo, isso será abordado logo mais, mas essa
independência entre o tipo e precisão do dado para o computador
e, ainda, o efetivo armazenamento na memória volátil, trazem
vantagens significativas ao desenvolvimento e ao desenvolvedor.

Uma segunda característica quando estudamos tipos de dados


em uma linguagem flexível com C são os modificadores. Através
destes conseguimos alterar as faixas de valores aplicados a cada
tipo, isso não se aplica ao tipo sem valor (void) (SCHILDT, 1996). Os
seguintes modificadores são suportados:

• signed

• unsigned

• long

• short

Como observado, cada um dos modificadores vai alterar a


precisão de um tipo para atender a uma determinada necessidade,
então inicialmente todos os tipos numéricos por padrão suportam
números positivos e negativos, se identificamos a necessidade
apenas de números positivos podemos utilizar o modificador
unsigned. Já o modificador long pode alterar um tipo duplicando
a sua capacidade, como exemplo podemos considerar o long int
(SCHILDT, 1996).

U1 - Problemas no desenvolvimento de software 15


Para demonstração do uso dos modificadores de tipo, vamos
construir um algoritmo simples que apresentará ao usuário uma
faixa de números inteiros positivos, e com o uso do modificador
unsigned alteraremos a apresentada.

void main() {

// Inicializa a variável a com 1

unsigned char a = 1;

// Enquanto a variável a for positiva o laço repetirá

while (a > 0) {

// Apresentando o valor contido na variável, um


por linha

printf("%d\n", a);

// Incrementa a variável a em 1

a++;

Fonte: elaborado pelo autor.

A execução desse código apresentará na tela a faixa de valores


do 1 ao 255, agora, se alteramos o tipo da variável “a” removendo
o modificador unsigned, então a faixa de valores apresentada será
do 1 ao 127. Esse comportamento justifica-se por duas razões,
primeiro que a faixa de valores de uma variável char varia de
-128 a 127, agora ao modificarmos esse tipo colocando-o como
unsigned, então a sua faixa passará a ser de 0 a 255. A segunda
razão está no fato que a variável “a” ao atingir o maior valor possível
para sua precisão, que podem ser 127 ou 255, dependendo do uso
ou não do modificador, então na próxima tentativa de incrementá-

16 U1 - Problemas no desenvolvimento de software


la ocorrerá um conceito chamado overflow. Na linguagem C, ao
ultrapassarmos o limite positivo de uma variável, caracterizando
um overflow, essa variável passa a representar o menor valor
suportado pela sua precisão.

Para saber mais


Se ficou alguma dúvida a respeito dos tipos de dados em C e o
funcionamento dos modificadores, sugerimos a leitura do artigo
Mais sobre tipos: conversão implícita e explícita, disponível em:
<http://www.inf.ufpr.br/cursos/ci067/Docs/NotasAula/notas-21_
Mais_sobre_tipos_convers.html>. Acesso em: 22 maio 2017.

1.5 Tipo de dados complexos


Assim como já posicionado, todo e qualquer dado deve ser
categorizado. Isso porque a forma como o computador armazena
um dado alfanumérico difere de um dado numérico com ponto
flutuante. Também devemos compreender que o ciclo básico
de um computador começa com a entrada de dados, passa
pelo processamento e, por fim, chega à geração dos resultados.
O muito famoso “entrada/processamento/saída”. Os dados de
entrada necessariamente são armazenados em variáveis, que
posteriormente serão levadas e processadas dentro da CPU.
Existem situações em que variáveis primitivas não atendem a
alguns tipos de algoritmos, visto pela complexidade da lógica
empregada ou dos dados manipulados. Nessa linha de pensamento
um importante recurso do qual dispomos é o que chamamos de
tipos complexos, que basicamente nos permitem criar novos tipos
de dados a partir desses tipos primários. Esses tipos não são só
agrupamentos de tipos primários, são na verdade agrupamentos
de tipos primários conceitualmente relacionados (SCHILDT,
1996). Com o objetivo de demonstrarmos conceitos e técnicas,
utilizadas na abordagem para resolução de um problema através
do computador, então introduziremos uma situação-problema.
Vamos imaginar a demanda gerada por um município fictício,
que deseja efetuar uma pesquisa na sua população e obter alguns
indicadores. Vamos aos detalhes dessa demanda:

U1 - Problemas no desenvolvimento de software 17


É necessário a construção de um aplicativo, capaz de receber
os dados coletados em uma pesquisa efetuada no município
Brasil Moderno (fictício). Essa pesquisa coletou dados relativos às
características físicas de 50 habitantes. De cada habitante foram
coletados os seguintes dados: sexo, altura, idade e cor dos olhos
(A – azuis, V – verdes, ou C – castanhos). Após, devidamente
alimentado ele deverá ser capaz de gerar as seguintes informações:

a) A média de idade das pessoas com olhos castanhos e


altura superior a 1,60m.

b) A maior idade entre os habitantes.

c) A quantidade de indivíduos do sexo feminino cuja idade


esteja entre 20 e 45 anos (inclusive), ou que tenham olhos verdes
e altura inferior a 1,70m.

d) O percentual de homens.

Para construirmos uma aplicação que atenda aos requisitos


listados acima, devemos primeiramente compreender quais são os
dados que trabalharemos, e como eles podem ser representados
através da programação.

Vejamos que, para cada habitante será necessário dispor uma


estrutura de dados capaz de armazenar sexo, altura, idade e cor
dos olhos.

Para esse tipo de característica de algoritmo, felizmente


algumas linguagens como C ANSI, Pascal, C# e outras, suportam a
definição de novos tipos para representar dados mais complexos,
como no nosso exemplo, em que para cada habitante quatro
dados primitivos serão coletados. A representação de dados
complexos, através da programação, consiste na definição de
um novo tipo, baseado em tipos mais elementares ou primitivos.
Conceitualmente chamamos de tipo de dado complexo.

Não seria razoável a possibilidade de criarmos o nosso próprio


tipo capaz de armazenar os dados necessários: sexo, altura, idade
e cor dos olhos. Seguindo essa linha de raciocínio não seria
fantástico então se pudéssemos criar apenas uma variável do tipo

18 U1 - Problemas no desenvolvimento de software


vetor, em que cada elemento (índice) fosse um habitante. Isso
tornará o nosso algoritmo mais legível e mais fácil de programar.

Para demonstração da solução vamos utilizar a linguagem C,


mas devemos entender que os conceitos devem ser abstraídos,
entrada, processamento e saída:

// Definindo uma constante por questões do compilador


utilizado

#define _CRT_SECURE_NO_WARNINGS

// Define uma constante para controlar o número de "habitantes"


questionados

#define QTD_PESQ 2

#include <stdio.h>

// Aqui definimos o nosso tipo complexo, que em C é


apresentado através

// de uma struct, veja que cada membro dela representa um


dado de um habitante,

// que conceitualmente podemos compreender como um


agrupador de variáveis de tipos

// primários

struct {

char sexo;

float altura;

char cor_olhos;

int idade;

} typedef Habitante;

U1 - Problemas no desenvolvimento de software 19


int main() {

// Declarando um array (vetor) do nosso

// tipo complexo Aluno

Habitante populacao_pesquisa[QTD_PESQ];

// Objetivo 1 - A média de idade das pessoas com olhos


castanhos e altura superior a 1,60m;

// Variável para somar as idades de pessoas com olhos


castanhos e altura superior a 1,60m

int sm_idade_cast160 = 0;

// Variável para contar o número de pessoas com olhos


castanhos e altura superior a 1,60m

int ct_idade_cast160 = 0;

// Objetivo 2 - A maior idade entre os habitantes;

// Variável de controle para identificar a maior idade

int maior_idade = 0;

// Objetivo 3 - A quantidade de indivíduos do sexo feminino


cuja idade esteja entre 20 e

// 45 anos (inclusive), ou que tenham olhos verdes e altura


inferior a 1,70m;

// Variável de controle para contar o número de indivíduos


de acordo com o Objetivo 3

int ct_fem_20_45 = 0;

// Objetivo 4 - O percentual de homens.

// Variável de controle para contar o número de indivíduos


de acordo com o Objetivo 4

20 U1 - Problemas no desenvolvimento de software


int ct_homens = 0;

// Interação com o usuário (Saída padrão Monitor)

printf("Iniciando Aplicacao, por favor informe os dados.\n");

// Entrada de Dados: Repetirá pelo número de habitantes

for (int i = 0; i < QTD_PESQ; i++) {

// Interação com o usuário (Saída padrão Monitor)

printf("Habitante N. %d >>>\n", i + 1);

// Interação com o usuário (Saída padrão Monitor


& Entrada Padrão Teclado)

printf("Sexo ([M]asculino, [F]eminino): ");

scanf_s(" %c", &populacao_pesquisa[i].sexo);

// Interação com o usuário (Saída padrão Monitor


& Entrada Padrão Teclado)

printf("Altura (cm): ");

scanf("%f", &populacao_pesquisa[i].altura);

// Interação com o usuário (Saída padrão Monitor


& Entrada Padrão Teclado)

printf("Cor dos Olhos ([A]zuis, [V]erdes, [C]


astanhos): ");

scanf_s(" %c", &populacao_pesquisa[i].cor_olhos,


1);

U1 - Problemas no desenvolvimento de software 21


// Interação com o usuário (Saída padrão Monitor
& Entrada Padrão Teclado)

printf("Idade: ");

scanf("%d", &populacao_pesquisa[i].idade);

// Processamento: Repetirá pelo número de habitantes

for (int i = 0; i < QTD_PESQ; i++) {

// Objetivo 1 - A média de idade das pessoas com


olhos

// castanhos e altura superior a 1,60m;

if (populacao_pesquisa[i].cor_olhos == 'C'

&& populacao_pesquisa[i].altura > 160) {

sm_idade_cast160 += populacao_
pesquisa[i].idade;

ct_idade_cast160++;

// Objetivo 2 - A maior idade entre os habitantes;

if (populacao_pesquisa[i].idade > maior_idade) {

maior_idade = populacao_pesquisa[i].
idade;

// Objetivo 3 - A quantidade de indivíduos do sexo


feminino cuja idade esteja entre 20 e

22 U1 - Problemas no desenvolvimento de software


// 45 anos (inclusive), ou que tenham olhos verdes
e altura inferior a 1,70m;

if (populacao_pesquisa[i].sexo == 'F'

&& (populacao_pesquisa[i].idade >= 20

&& populacao_pesquisa[i].idade <= 45)

|| (populacao_pesquisa[i].cor_olhos == 'V'

&& populacao_pesquisa[i].altura < 170)) {

ct_fem_20_45++;

// Objetivo 4 - O percentual de homens.

if (populacao_pesquisa[i].sexo == 'M') {

ct_homens++;

// Interação com o usuário (Saída padrão Monitor)

printf("Gerando Resultados >>>>\n");

if (ct_idade_cast160 == 0) {

printf("\t-Objetivo 1: 0\n");

} else {

printf("\t-Objetivo 1: %.2f\n",

(sm_idade_cast160 / (float)ct_idade_cast160));

U1 - Problemas no desenvolvimento de software 23


printf("\t-Objetivo 2: %d\n", maior_idade);

printf("\t-Objetivo 3: %d\n", ct_fem_20_45);

printf("\t-Objetivo 4: %.1f\n", (ct_homens / (float)QTD_


PESQ) * 100);

return 0;

}
Fonte: elaborado pelo autor.

É comum que as linguagens de programação possuam


recursos para criar tipos de dados complexos, com isso podemos
agrupar os dados que são correlacionados, que conceitualmente
não deveriam ficar separados.

O código apresentado como solução da nossa situação-


problema foi, nesse caso, escrito em C, mas pode ser facilmente
convertido para outras linguagens que dispõem do recurso de
criação de tipos de dados complexos, visto que basicamente
utilizamos operações de entrada (scanf e scanf_s), operações
de saída (printf) e controle de fluxo (if, if-else e for), além das
declarações de variáveis primitivas, bem como a nossa estrutura de
dados unidimensional homogênea, mais conhecida como vetor.

Para saber mais


Para saber mais a respeito da linguagem C, um material introdutório
produzido a partir de um curso on-line chamado Curso de C,
promovido pela UFMG, pode ser uma ótima investida. Disponível
em: <http://web.inf.ufpr.br/menotti/ci056-2015-2-1/source/
introducao_c_renatocm_deeufmg.pdf>. Acesso em: 22 maio 2017.

24 U1 - Problemas no desenvolvimento de software


Questão para reflexão
Até que ponto uma linguagem pode auxiliar ou dificultar o
desenvolvimento de um algoritmo, podendo chegar a determinar
a qualidade do que se produz?

Atividades de aprendizagem
1. Os tipos de dados complexos são assim chamados porque:
a) Dificultam o desenvolvimento.
b) Devem ser utilizados em algoritmos complexos.
c) Permitem representar dados não atendidos pelos tipos primitivos.
d) Estão disponíveis apenas em linguagens de baixo nível.
e) Geram restrições relacionadas à plataforma de execução.

2. A representação de um dado na programação está diretamente


relacionada à utilização de variável, então é natural que, ao projetarmos
um algoritmo a quantidade e os tipos das variáveis estarão relacionados
a alguma lógica. Qual é a justificativa para a declaração de uma variável
e a definição do seu tipo?

U1 - Problemas no desenvolvimento de software 25


26 U1 - Problemas no desenvolvimento de software
Seção 2

Limitações em evidência
Introdução à seção

Avaliaremos a técnica de programação procedural, bem como


algumas limitações que encontramos em linguagens que suportam
essa técnica. Uma visão objetiva sobre as limitações e como elas
podem trazer desvantagens e deficiências ao código desenvolvido.

2.1 Paradigma estruturado


À medida que desenvolvemos aplicativos maiores, isso em
complexidade e também volume de instruções, naturalmente
procuraremos técnicas para tentarmos organizar essas instruções
bem como evitar a repetição (CAMARGO, 2009).

O paradigma estruturado pode também ser chamado de


imperativo. Através dele um problema será resolvido utilizando
estruturas sequenciais, condicionais e de repetição. Também
dispõe de técnicas para quebrar um problema complexo em
partes menores e mais simples, permitindo assim a reutilização
(ASCENCIO; CAMPOS, 2007).

Vamos hipoteticamente estabelecer características de um


algoritmo, que na sua primeira versão foi concebido com algumas
dezenas de linhas, feitas pelo desenvolvedor João. Este que
acabara de ser contratado, pois demonstrou conhecimentos
adequados proporcionais aos seus dois semestres de programação
aprendidos na faculdade. Passados seis meses o mesmo algoritmo
fora alterado centenas de vezes para atender a algumas demandas
trazidas pelos usuários desta aplicação. Além de várias mudanças
que descaracterizaram o algoritmo inicial, no total somam-se 2000
linhas de código. Agora já não tão confiante sobre os detalhes e
funcionamento do algoritmo, João procura meios para tornar o seu
código mais simples. Um desenvolvedor mais experiente, chamado

U1 - Problemas no desenvolvimento de software 27


Pedro, sugere que o código seja desenvolvido em pequenos
blocos, que na medida da necessidade serão executados.

Após aplicada essa técnica, João chegou ao resultado de 70


pequenos blocos de códigos com na média de 25 linhas cada
um. Também percebeu que uma organização lógica do algoritmo
potencializou a sua capacidade de entender o código, dentre os
vários contextos que o algoritmo dispõe, vista a complexidade dos
fluxos de execução, entenda como if, if-else e outros. Além disso,
João removeu aproximadamente 250 linhas, isso porque algumas
linhas se repetiam, e após a organização em blocos lógicos,
conseguiu enxergar a possibilidade de executar o mesmo código
mais de uma vez (SCHILDT, 1996).

Essa situação hipotética possivelmente será reconhecida por


muitos quando iniciam a sua jornada no campo de desenvolvimento.
Dentro de linhas gerais, ao iniciarmos o desenvolvimento
seguimos à risca, em programação convencional, as instruções
são executadas uma a uma, então focamos a construção de
algoritmos seguindo isso como princípio, a cada problema
construímos nosso código para satisfazer a necessidade, e assim
vamos encadeando essas instruções de forma elementar, uma
coisa por vez, uma atrás de outra, ou então uma sequente à outra.
Veja, essa visão é natural, se formos executar uma receita de bolo,
vamos nos deparar com instruções uma após a outra. Se fizermos
uma viagem, elaboraremos um itinerário com alguma lógica,
possivelmente o menor caminho. Uma simples ida ao mercado
despertará em nós a vontade de fazermos o menor trajeto quando
estivermos atrás dos itens desejados, ou até encontramos o caixa
com menor fila. Isso podemos compreender como “estratégia”.
Nossa natureza nos leva a fazermos uma coisa por vez e de forma
sequencial. Todos agimos dessa forma, não nos vemos de outra
forma. Se pensarmos em afirmações como, para fazermos um
arroz antes colocamos a panela no fogão para depois despejar o
arroz, isso pode soar como algo esquisito, pois são ações naturais
e cotidianas.

Estamos trazendo você, leitor, para a seguinte dedução, nós


nos organizamos e agimos de forma lógica, sempre executando
uma tarefa depois de outra, a fim de obtermos um resultado

28 U1 - Problemas no desenvolvimento de software


satisfatório. Assim agimos, e quando vamos instruir a máquina
nada mais natural fazermos da mesma forma, colocando uma
ação após a outra, até obtermos o resultado satisfatório.

Essa característica, por mais natural que seja, pode trazer


limitações, isso porque na medida que vamos construindo
soluções mais complexas, o número de linhas de código aumenta
significativamente. Podemos lembrar a essência do que fizemos,
mas não os detalhes. Assumindo isso, várias são as linguagens que
dispõem de recursos para conseguirmos fracionar nosso algoritmo.
Também vale observar que várias são as técnicas que dispomos
para tal feito, isso porque não fracionamos ao acaso, adotamos
regras estabelecidas pela técnica e flexibilidades dispostas pela
linguagem.

Depois de toda a argumentação vamos a algumas afirmações. A


linguagem C dispõe de recursos que permitem que um algoritmo
seja dividido, criando elementos de modularização. Esse recurso
visa atender às características da programação procedural, que,
como falado, estabelece a máxima da divisão do código em blocos
de códigos menores, para facilitar o entendimento, a leitura, a
reutilização e outras vantagens (SCHILDT, 1996).

Para estabelecermos um paralelo entre a teoria e a prática,


vamos exercitar através da adaptação do código desenvolvido
na seção Tipos de Dados Complexos, e então demonstrarmos
algumas possíveis vantagens. Esse processo que executaremos
podemos chamá-lo de refatoração de código. Isso porque a partir
de um código existente vamos alterá-lo, mas sem mudarmos a
lógica.

void executa_leitura(Habitante populacao_pesquisa[]) {

// Entrada de Dados: Repetirá pelo número de habitantes

for (int i = 0; i < QTD_PESQ; i++) {

// Interação com o usuário (Saída padrão Monitor)

printf("Habitante N. %d >>>\n", i + 1);

U1 - Problemas no desenvolvimento de software 29


// Interação com o usuário (Saída padrão Monitor
& Entrada Padrão Teclado)

printf("Sexo ([M]asculino, [F]eminino): ");

scanf_s(" %c", &populacao_pesquisa[i].sexo);

// Interação com o usuário (Saída padrão Monitor


& Entrada Padrão Teclado)

printf("Altura (cm): ");

scanf("%f", &populacao_pesquisa[i].altura);

// Interação com o usuário (Saída padrão Monitor


& Entrada Padrão Teclado)

printf("Cor dos Olhos ([A]zuis, [V]erdes, [C]


astanhos): ");

scanf_s(" %c", &populacao_pesquisa[i].cor_olhos,


1);

// Interação com o usuário (Saída padrão Monitor


& Entrada Padrão Teclado)

printf("Idade: ");

scanf("%d", &populacao_pesquisa[i].idade);

Fonte: elaborado pelo autor.

Podemos organizar a entrada de dados em um bloco de código


separado do principal, veja que cada bloco deve necessariamente
receber um nome, neste caso, chamamos de executa_leitura.

30 U1 - Problemas no desenvolvimento de software


A modularização de blocos de código de leitura gerou uma
grande flexibilidade, pois se houver a necessidade de coletarmos
mais de uma vez a entrada de dados, devemos apenas acioná-lo
quantas vezes necessário. Mas, vale destacar, não é o caso desse
algoritmo, pois a entrada de dados ocorre apenas uma vez durante
a execução do programa.

A separação é algo razoável a ser feito por estratégia de


modularização, ações que se constituem em mais de uma linha
de código e conceitualmente conseguimos segmentá-la do todo
(SCHILDT, 1996). Vamos logo mais observar uma outra grande
vantagem, a princípio observe mais um bloco de código que
extraímos do bloco principal:

void executa_processamento(Habitante populacao_pesquisa[],

int* sm_idade_cast160, int* ct_idade_cast160, int* maior_


idade,

int* ct_fem_20_45, int* ct_homens) {

// Processamento: Repetirá pelo número de habitantes

for (int i = 0; i < QTD_PESQ; i++) {

// Objetivo 1 - A média de idade das pessoas com


olhos castanhos e altura superior a 1,60m;

if (populacao_pesquisa[i].cor_olhos == 'C'

&& populacao_pesquisa[i].altura > 160) {

*sm_idade_cast160 += populacao_
pesquisa[i].idade;

(*ct_idade_cast160)++;

// Objetivo 2 - A maior idade entre os habitantes;

U1 - Problemas no desenvolvimento de software 31


if (populacao_pesquisa[i].idade > *maior_idade) {

*maior_idade = populacao_pesquisa[i].
idade;

// Objetivo 3 - A quantidade de indivíduos do sexo


feminino cuja idade esteja entre 20 e

// 45 anos (inclusive), ou que tenham olhos verdes


e altura inferior a 1,70m;

if (atende_objetivo3(&populacao_pesquisa[i])) {

(*ct_fem_20_45)++;

// Objetivo 4 - O percentual de homens.

if (populacao_pesquisa[i].sexo == 'M') {

(*ct_homens)++;

Fonte: elaborado pelo autor.

Seguindo os mesmos princípios que nos guiaram para a


criação do bloco de código executa_leitura, vamos separar a
lógica de processamento em um bloco único chamado executa_
processamento. Veja que a separação do código exigiu uma
técnica até antes não utilizada, ela se chama endereçamento de
memória através de ponteiro, um conceito que não será explanado
aqui, mas a síntese a se extrair está no fato que um novo bloco
foi criado agrupando as instruções que estão semanticamente

32 U1 - Problemas no desenvolvimento de software


relacionadas, para os devidos esclarecimentos da técnica, por favor
considere a seção “Para saber mais” definida logo mais. Dentro
dessa medida podemos extrair muitos outros blocos, respeitando
a relação conceitual das instruções. Vamos a mais um caso, agora
procurando além da modularização extrair uma lógica que expõe
um grau de complexidade maior:

int atende_objetivo3(Habitante* hab) {

if (hab->sexo == 'F'

&& (hab->idade >= 20 && hab->idade <= 45)

|| (hab->cor_olhos == 'V' && hab->altura < 170)) {

return 1;

return 0;

Fonte: elaborado pelo autor.

Conforme o bloco de código atende_objetivo3, podemos


perceber que a separação, desmembramento e principalmente a
organização podem chegar a níveis muito satisfatórios. Neste caso
utilizamos um recurso relativo à própria modularização, o retorno
de dados. Existem blocos que agrupam instruções conceitualmente
relacionadas, também existem blocos, que além de agruparem
as mesmas razões, devem retornar um valor, como analogia
poderíamos dizer que são miniprogramas, isso porque têm a entrada
de dados através de parâmetros, executam um processamento e
por fim geram uma ou mais saídas, que seriam o retorno. Vejamos
o bloco atende_objetivo3 que tem a sua entrada de dados através
do parâmetro hab, do tipo ponteiro de Habitante, ao executar a sua
lógica, suas instruções, ele gerará a saída de apenas um valor, ou
esse valor indicará que o habitante atende ao objetivo 3, no caso do

U1 - Problemas no desenvolvimento de software 33


valor 1, senão gerará o valor 0 indicando que não atende. Não se
prenda aos nomes técnicos, se prenda ao conceito, avalie a lógica
não o código, subtraia a essência não os detalhes.

Devemos compreender o valor da organização, e em programação


a modularização é uma das razões de alcançarmos aplicações com
alto grau de complexidade e qualidades cada vez mais evidentes. É
fato que existem outras razões, muitas não serão abordadas nessa
obra, mas aquelas que aqui estão com certeza valem o apelo para
serem estudadas e compreendidas (CAMARGO, 2009).

Vejamos como ficaram os blocos que tiveram códigos extraídos.


Dentro do processo de refatoração em nossa segunda ação,
separamos o bloco executa_processamento e posteriormente
extraímos a lógica de avaliação do objetivo 3 através do bloco
atende_objetivo3. Na sequência poderemos observar como o
bloco de código executa_processamento ficou mais simples,
visto que toda complexidade de análise do objetivo 3 estava
escondida em outro bloco, entenda como encapsulado, oculto
ou até escondido.

void executa_processamento(Habitante populacao_pesquisa[],

int* sm_idade_cast160, int* ct_idade_cast160, int* maior_


idade,

int* ct_fem_20_45, int* ct_homens) {

// Processamento: Repetirá pelo número de habitantes

for (int i = 0; i < QTD_PESQ; i++) {

// Objetivo 1 - A média de idade das pessoas com


olhos castanhos e altura superior a 1,60m;

if (populacao_pesquisa[i].cor_olhos == 'C'

&& populacao_pesquisa[i].altura > 160) {

*sm_idade_cast160 += populacao_
pesquisa[i].idade;

34 U1 - Problemas no desenvolvimento de software


(*ct_idade_cast160)++;

// Objetivo 2 - A maior idade entre os habitantes;

if (populacao_pesquisa[i].idade > *maior_idade) {

*maior_idade = populacao_pesquisa[i].
idade;

// Objetivo 3 - A quantidade de indivíduos do sexo


feminino cuja idade esteja entre 20 e

// 45 anos (inclusive), ou que tenham olhos verdes


e altura inferior a 1,70m;

if (atende_objetivo3(&populacao_pesquisa[i])) {

(*ct_fem_20_45)++;

// Objetivo 4 - O percentual de homens.

if (populacao_pesquisa[i].sexo == 'M') {

(*ct_homens)++;

Fonte: elaborado pelo autor.

U1 - Problemas no desenvolvimento de software 35


Faça a leitura mental da estrutura condicional utilizada para limitar
o incremento da variável ct_fem_20_45, veja que nesse ponto
ocultamos a complexidade da avaliação, em vias gerais isso ajudará
futuramente quando aqui nesse ponto retornarmos, isso porque
entenderemos como é a análise dos objetivos 1, 2 e 5. Já o objetivo
3, não, pois ele foi extraído. Como ideal deveríamos encapsular
cada um desses casos, por mais simples que sejam, pois dentro do
contexto em que se encontram (executa_processamento), suas
lógicas dificultarão o entendimento do todo.

Também alteramos o bloco principal extraindo os blocos de


códigos de entrada e processamento, poderíamos ainda separar em
outros blocos, mas para entendimento o suficiente foi alcançado,
veja como ficou o bloco principal:

int main() {

// Declarando um array (vetor) do nosso

// tipo complexo Aluno

Habitante populacao_pesquisa[QTD_PESQ];

// Objetivo 1 - A média de idade das pessoas com olhos


castanhos e altura superior a 1,60m;

// Variável para somar as idades de pessoas com olhos


castanhos e algura superior a 1,60m

int sm_idade_cast160 = 0;

// Variávei para contar o número de pessoas com olhos


castanhos e algura superior a 1,60m

int ct_idade_cast160 = 0;

// Objetivo 2 - A maior idade entre os habitantes;

36 U1 - Problemas no desenvolvimento de software


// Variável de controle para identificar a maior idade

int maior_idade = 0;

// Objetivo 3 - A quantidade de indivíduos do sexo feminino


cuja idade esteja entre 20 e

// 45 anos (inclusive), ou que tenham olhos verdes e altura


inferior a 1,70m;

// Variável de controle para contar o número de indivíduos


de acordo com o Objetivo 3

int ct_fem_20_45 = 0;

// Objetivo 4 - O percentual de homens.

// Variável de controle para contar o número de indivíduos


de acordo com o Objetivo 4

int ct_homens = 0;

// Interação com o usuário (Saída padrão Monitor)

printf("Iniciando Aplicacao, por favor informe os dados.\n");

executa_leitura(populacao_pesquisa);

executa_processamento(populacao_pesquisa,

&sm_idade_cast160, &ct_idade_cast160, &maior_


idade,

&ct_fem_20_45, &ct_homens);

// Interação com o usuário (Saída padrão Monitor)

printf("Gerando Resultados >>>>\n");

U1 - Problemas no desenvolvimento de software 37


if (ct_idade_cast160 == 0) {

printf("\t-Objetivo 1: 0\n");

} else {

printf("\t-Objetivo 1: %.2f\n",

(sm_idade_cast160 / (float)ct_idade_cast160));

printf("\t-Objetivo 2: %d\n", maior_idade);

printf("\t-Objetivo 3: %d\n", ct_fem_20_45);

printf("\t-Objetivo 4: %.1f\n", (ct_homens / (float)QTD_PESQ)


* 100);

return 0;

Fonte: elaborado pelo autor.

Conseguimos reduzi-lo de 86 linhas para 44, isso analisado


através do editor de fontes. Lógico que a principal vantagem não
é o número de linhas, isso porque o número total de linhas partiu
de 107 para 135, mas a organização, essa sim foi a nossa principal
vantagem.

Linguagens, que permitem a organização do código em blocos,


possibilitam o desenvolvimento de uma aplicação pautado em
organização e reutilização. Em nossa refatoração, nosso exemplo
não nos permitiu observar a reutilização, mas a organização sim.
Vale destacar que a reutilização também está em evidência em nossa
aplicação, mas não através de algo que construímos, mas através de
algo que está disponível na própria tecnologia utilizada. Observe a
rotina printf, que inicialmente marcamos como um mecanismo de
interação com o usuário. Perceba quantas vezes ela é chamada,
sendo que seu propósito sempre é o mesmo, variando quanto ao
conteúdo a ser apresentado na saída-padrão, o monitor.

38 U1 - Problemas no desenvolvimento de software


Essa facilidade em reutilizarmos uma rotina quantas vezes forem
necessárias, sim, é uma excelente vantagem, conseguimos alcançar
um parâmetro de eficiência, pois evitamos a reescrita de uma
mesma sequência de instruções. Menos recurso será necessário
para executar uma mesma ação, isso não na ótica da computação,
mas relativo ao código desenvolvido, que para ser escrito depende
de um recurso escasso: horas de trabalho de um desenvolvedor.

2.2 Limitações associadas à linguagem


Através desse simples exemplo, vamos destacar alguns pontos
que podemos enquadrar como frágeis, isso quando a ordem
é organização. Vejamos a nossa estrutura de dados Habitante:
através dela conseguimos agrupar vários dados que pertencem a
um único habitante, isso no contexto da pesquisa. Assim como
foi denotado, o agrupamento sem sombra de dúvidas facilitou
muito a programação, um segundo ponto foi a possibilidade
de modularizar a lógica por trás de cada objetivo, inicialmente
extraímos apenas a lógica do objetivo 3, mas como já observado,
poderíamos efetuar isso para todos os outros objetivos. Para facilitar
a visualização vamos, a seguir, analisar uma parte do projeto, com
a nossa estrutura de dado, bem como os quatro objetivos em
separado, exatamente para evidenciar um ponto fundamental do
nosso aprendizado, vejamos o código:

// Avalia se o habitante atende ao objetivo 1

int atende_objetivo1(Habitante* hab) {

if (hab->cor_olhos == 'C' && hab->altura > 160) {

return 1;

return 0;

U1 - Problemas no desenvolvimento de software 39


// Avalia se o habitante atende ao objetivo 2

int atende_objetivo2(Habitante* hab, int* maior_idade) {

if (hab->idade > *maior_idade) {

return 1;

return 0;

// Avalia se o habitante atende ao objetivo 3

int atende_objetivo3(Habitante* hab) {

if (hab->sexo == 'F'

&& (hab->idade >= 20 && hab->idade <= 45)

|| (hab->cor_olhos == 'V' && hab->altura < 170)) {

return 1;

return 0;

// Avalia se o habitante atende ao objetivo 4

int atende_objetivo4(Habitante* hab) {

return hab->sexo == 'M' ? 1 : 0;

}
Fonte: elaborado pelo autor.

40 U1 - Problemas no desenvolvimento de software


Isso por consequência influenciou a mudança do bloco
executa_processamento, vejamos como ele foi redesenhado:

void executa_processamento(Habitante populacao_pesquisa[],

int* sm_idade_cast160, int* ct_idade_cast160, int* maior_


idade,

int* ct_fem_20_45, int* ct_homens) {

// Processamento: Repetirá pelo número de habitantes

for (int i = 0; i < QTD_PESQ; i++) {

// Objetivo 1 - A média de idade das pessoas com


olhos castanhos e altura superior a 1,60m;

if (atende_objetivo1(&populacao_pesquisa[i])) {

*sm_idade_cast160 += populacao_
pesquisa[i].idade;

(*ct_idade_cast160)++;

// Objetivo 2 - A maior idade entre os habitantes;

if (atende_objetivo2(&populacao_pesquisa[i],
maior_idade)) {

*maior_idade = populacao_pesquisa[i].
idade;

// Objetivo 3 - A quantidade de indivíduos do sexo


feminino cuja idade esteja entre 20 e

// 45 anos (inclusive), ou que tenham olhos verdes

U1 - Problemas no desenvolvimento de software 41


e altura inferior a 1,70m;

if (atende_objetivo3(&populacao_pesquisa[i])) {

(*ct_fem_20_45)++;

// Objetivo 4 - O percentual de homens.

if (atende_objetivo4(&populacao_pesquisa[i])) {

(*ct_homens)++;

Fonte: elaborado pelo autor.

Agora vamos efetuar algumas fatorações que tornarão o código


ainda mais legível, e a partir disso vamos poder avaliar se é possível
alcançarmos um novo nível na qualidade de nosso código. Vamos
a mais algumas mudanças:

int eh_masc(Habitante* hab) {

return hab->sexo == 'M';

int eh_fem(Habitante* hab) {

return hab->sexo == 'F';

42 U1 - Problemas no desenvolvimento de software


int na_faixa(Habitante* hab, int inicio, int fim) {

return hab->idade >= inicio && hab->idade <= fim;

Fonte: elaborado pelo autor.

Essas alterações têm por objetivo extrair lógicas relacionadas a


características pertinentes a um habitante: seu sexo e faixa etária.
Isso são asserções que fazemos naturalmente, sem nos atentarmos
para isso. Já na perspectiva da máquina devemos ser explícitos.
Após aplicarmos esse desmembramento levando em consideração
características relacionadas ao habitante, vejamos como o bloco
relativo ao objetivo 3, como foi alterado para usufruir desses novos
elementos desenvolvidos:

int atende_objetivo3(Habitante* hab) {

if (eh_fem(hab)

&& (na_faixa(hab, 20, 45)||(hab->cor_olhos == 'V'


&& hab->altura < 170))) {

return 1;

return 0;

Fonte: elaborado pelo autor.

Retorne na implementação original e compare com a versão


apresentada acima. Perceba que a leitura e entendimento do
código ficou mais fácil, vamos dizer mais ágil e com certeza mais
seguro, visto que nos ajudará a compreender quais as condições
para um habitante atender ao objetivo 3, evitando falhas na
interpretação e por fim futuras mudanças incorretas.

U1 - Problemas no desenvolvimento de software 43


Vamos ao choque de realidade, essa seção não foi pensada
para enaltecer a programação procedural, visando organização e
legibilidade de código. Não, isso fizemos anteriormente, vamos
agora colocar o seguinte pensamento: puxa como seria legal
se pudéssemos conceber uma caixinha onde além de dados
pudéssemos também acrescentar lógicas relacionadas, isso em
uma estrutura unificada e com alguns facilitadores, algo que
pudesse reunir tudo que semanticamente estão relacionados.
Imagine essa caixinha sendo chamada Habitante, dentro dela
poderíamos colocar todos as características que desejamos
armazenar, sexo, altura, cor dos olhos e idade, além disso
poderíamos colocar comportamentos naturais a essa habitante,
por exemplo, se ele pertence ao sexo masculino ou feminino, e
até se ele faz parte de uma faixa etária.

Imagino que você já tenha percebido a limitação da linguagem,


assim eu espero, vejamos que a linguagem exemplo C não
dispõe de um mecanismo que liga os dados do habitante com
comportamentos que são naturais a ele, existe uma limitação
técnica e conceitual, até porque as coisas não foram pensadas
dessa forma.

Para saber mais


Para saber mais a respeito de acesso indireto de memória, utilize
o artigo Ponteiro em C: Definição. Disponível em: <https://www.
embarcados.com.br/ponteiro-em-c-definicao>. Acesso em: 22
maio 2017.

Questão para reflexão


Uma linguagem que dispõe de elementos para modularização
ou recursos que facilitaram o desenvolvimento devem ser
obrigatoriamente melhor observadas pela indústria de tecnologia?

Atividades de aprendizagem
1. “Software tornou-se profundamente incorporado em praticamente
todos os aspectos de nossas vidas e, consequentemente, o número de
pessoas interessadas nos recursos e nas funções oferecidas por uma

44 U1 - Problemas no desenvolvimento de software


determinada aplicação tem crescido significativamente” (PRESSMAN,
2011, p. 38).
Para atendermos a essa demanda cada vez mais crescente de softwares,
técnicas evoluem, baseadas em observações e experiências de
profissionais. Qual é a limitação apontada no texto relativo à programação
estruturada?

2. A estruturação de um código traz algumas vantagens significativas,


citadas e visualizadas através da refatoração efetuada. Assinale a
alternativa incorreta relativa à modularização:
a) Possibilidade de reutilização de código.
b) Complexidade nas técnicas utilizadas.
c) Aumento de legibilidade.
d) Associa-se necessariamente a técnicas mais simples de programação.
e) Potencializa a organização código.

U1 - Problemas no desenvolvimento de software 45


46 U1 - Problemas no desenvolvimento de software
Seção 3

Propondo organização às linguagens


Introdução à seção

Através de todo o conteúdo até aqui abordado, buscamos expor


limitações e algumas características técnicas, necessárias para o
entendimento do todo. Agora, vamos definir como seria o mundo
ideal, alguns recursos que poderiam fazer parte da linguagem, e
como isso poderia nos ajudar.

3.1 Propondo organização às linguagens


No último instante da seção anterior avaliamos a possibilidade
de criarmos algo unificado, em que dados e comportamentos
pudessem estar juntos, ligados, conceitualmente e através da
linguagem, de forma explícita. Vamos imaginar aquela caixinha
de que falamos como algo que possa ser codificado, então para
estabelecer os limites dela, onde essa estrutura começa e onde
termina, podemos utilizar quaisquer caracteres, por exemplo, uma
linha “escrita” utilizando o caractere asterisco, vejamos como ficaria:

************************

struct {

char sexo;

float altura;

char cor_olhos;

int idade;

} typedef Habitante;

U1 - Problemas no desenvolvimento de software 47


int eh_masc(Habitante* hab) {

return hab->sexo == 'M';

int eh_fem(Habitante* hab) {

return hab->sexo == 'F';

int na_faixa(Habitante* hab, int inicio, int fim) {

return hab->idade >= inicio && hab->idade <= fim;

************************

Fonte: elaborado pelo autor.

Vamos agora imaginar que tudo que estivesse dentro dessa


caixinha pertenceria a um Habitante, olhando isso como uma
estrutura única, vamos nomeá-la, delimitada pelos caracteres
de início e fim, lembre-se: utilizamos vários asteriscos. Como
assumimos que dentro da caixinha tudo pertence à Habitante, e
principalmente tudo está junto, unificado. Então aproveitaremos
para remover os parâmetros de cada bloco de código referente ao
habitante, porque agora eles pertencem a um habitante. Assim os
blocos podem acessar os atributos sem qualquer sintaxe partícula.
Vamos entender que como tudo está dentro de uma caixinha,
todos fazem parte de um todo, sem restrições de acesso.

Habitante ************************

char sexo;

float altura;

48 U1 - Problemas no desenvolvimento de software


char cor_olhos;

int idade;

int eh_masc() {

return sexo == 'M';

int eh_fem() {

return sexo == 'F';

int na_faixa(int inicio, int fim) {

return idade >= inicio && idade <= fim;

************************
Fonte: elaborado pelo autor.

Ainda, para melhorar a legibilidade, vamos delimitar a nossa


caixinha utilizando os delimitadores de bloco-chave. Além disso,
vamos dar um nome para esse novo bloco, ele se chamará
Habitante, será que ficará legal, vamos agir:

Habitante {

char sexo;

float altura;

U1 - Problemas no desenvolvimento de software 49


char cor_olhos;

int idade;

int eh_masc() {

return sexo == 'M';

int eh_fem() {

return sexo == 'F';

int na_faixa(int inicio, int fim) {

return idade >= inicio && idade <= fim;

Fonte: elaborado pelo autor.

Realmente ficou legível. Que tal patentearmos isso? Brincadeiras


à parte, essa evolução que foi exposta nesta última seção não
apresentou nada revolucionário, na verdade seu objetivo é abrir
o seu horizonte e entender que partimos de um ponto histórico,
utilizando uma técnica eficiente, mas que pode ser melhorada.
Além disso, abordamos aqui a possibilidade de evoluir ainda
mais. Sabendo o autor que a evolução já aconteceu, abramos o
caminho para uma técnica mais rebuscada em que princípios de
organização, segurança e facilidades estão presentes, do começo
ao fim.

50 U1 - Problemas no desenvolvimento de software


Acreditamos que agora podemos mergulhar em conceitos que
fundamentarão e nos nortearão para explorarmos os conceitos de
uma programação orientada a objetos. Lembre-se: a linguagem é
o menor dos detalhes, os conceitos são distintos, mas singulares
independentemente da linguagem utilizada.

Para saber mais


Uma visão sobre a evolução das linguagens pode ajudá-lo a
compreender a dinâmica que existe, e até enxergar a importância
dos conhecimentos que são alheios à linguagem, até porque
elas evoluem. Disponível em: <https://www.extremetech.com/
computing/91572-the-evolution-of-computer-languages-
infographic>. Acesso em: 22 maio 2017.

Questão para reflexão


Quais são as principais razões que promovem evoluções nas
linguagens de programação?

Atividades de aprendizagem
1. Qual termo abaixo melhor define o objetivo da linguagem proposta?
a) Recursividade.
b) Organização.
c) Separar.
d) Unificar.
e) Otimizar.

2. Conforme o autor Pressman (2011, p. 48) bem pontuou, “software


é um elemento-chave para soluções computacionais, com evolução
nesses últimos 50 anos de uma ferramenta especializada em análise de
informações e resolução de problemas para uma indústria propriamente
dita. Mesmo após tal evolução problemas são comuns no desenvolvimento
de software de qualidade, considerando prazo e orçamento".
Baseado nesta unidade, apresente algumas limitações que podemos ter
e como evoluir.

U1 - Problemas no desenvolvimento de software 51


Fique ligado
Nesta unidade, estudamos:
• Introdução ao desenvolvimento.
• Complexidades no desenvolvimento.
• Tipos de dados.
• Tipos de dados complexos.
• Programação procedural.

Para concluir o estudo da unidade


Caro aluno, esta unidade fica por aqui!
Através dela, você pôde observar características intrínsecas
ao desenvolvimento, problemas e limitações. Nosso objetivo
central foi estimular a necessidade da utilização e aprimoramento
das técnicas de desenvolvimento. Demonstramos como essa
evolução pode ser natural, e mesmo sem uma experiência prévia,
é fundamental compreendermos a evolução na forma com que
os softwares estão sendo construídos. Na sequência faremos uma
imersão na técnica de programação orientada a objetos, mas fica
aqui a ressalva, uma técnica possivelmente influenciará, mas a
mudança parte do indivíduo.

Atividades de aprendizagem da unidade


1. Uma prefeitura de uma cidade fez uma pesquisa com a sua população,
coletando dados sobre o salário, idade e número de filhos. O objetivo
dessa pesquisa é gerar as seguintes informações:
• A média de salário da população.
• A média do número de filhos.
• Maior salário.
• Percentual de pessoas com salário superior a R$ 1.000,00.
Avalie os dados e informações manipuladas através dessa pesquisa e
assinale a alternativa correta, relativo à devida identificação dos dados,
informações e proprietário delas:
a) Habitante: salario, idade e número de filhos - População: média
salarial, média de número de filhos, maior salário e percentual de salários
superior a R$ 1000,00.

52 U1 - Problemas no desenvolvimento de software


b) Habitante: salário, idade e número de filhos – População: média
salarial, maior salário e percentual de salários superior a R$ 1000,00.
c) Habitante: salário, idade e número de filhos – População: média
salarial, média de número de filhos, maior salário e percentual de salários
superior a R$ 1000,00.
d) Habitante: salário, idade e número de filhos – População: média
salarial, média de número de filhos e percentual de salários superior a
R$ 1000,00.
e) Habitante: média salarial, idade e número de filhos – População: média
de número de filhos, maior salário e percentual de salários superior a R$
1000,00

2. A possibilidade de definirmos tipos de dados na programação,


principalmente para atender aos dados que tendem a algum grau de
complexidade na sua representação, pode facilitar muito o desenvolvimento
de algoritmos com manipulação direta sobre esses dados.
A possibilidade de consolidar em uma estrutura única os dados e os
algoritmos de manipulação podem trazer algumas vantagens. Assinale a
alternativa que não apresenta uma vantagem em potencial:
a) Performance.
b) Escopo.
c) Semântica.
d) Organização.
e) Legibilidade.

3. Como Pressman (2011) devidamente pontuou, todos queremos


construir softwares que facilitem o trabalho, evitando pontos negativos
latentes nas tentativas malsucedidas. Então para termos o êxito, precisamos
de disciplina no projeto e na construção do software.
O apoio para esses desafios varia desde técnicas, ferramentas, boas práticas
e outros. Assinale a alternativa que corresponde ao ponto decisivo quando
analisamos a qualidade do código, que foi abordado nesta unidade:
a) Uso da tecnologia certa.
b) Utilização de técnicas devidamente homologadas por órgãos
governamentais.
c) Criação de padrões regionais.
d) Organização como ponto fundamental.
e) Utilização de linguagens de alto nível.

4. “Há cinco tipos básicos de dados em C: caractere, inteiro, ponto


flutuante, ponto flutuante de precisão dupla e sem valor (char, int, float,
double e void, respectivamente). Todos os outros tipos de dados em C são

U1 - Problemas no desenvolvimento de software 53


baseados em um desses tipos. O tamanho e a faixa desses tipos de dados
variam de acordo com o tipo de processador e com a implementação do
compilador C” (SCHILDT, 1996, p. 16).
A ideia por trás da existência dos tipos de dados está baseada nas
características de gerenciamento de memória, mas para o desenvolvedor
os tipos auxiliarão em qual aspecto?
a) Performance.
b) Abstração.
c) Segurança.
d) Estabilidade.
e) Durabilidade.

5. “Um paradigma de programação está intimamente relacionado à forma


de pensar do programador e como ele busca a solução para os problemas.
É o paradigma que permite ou proíbe a utilização de algumas técnicas de
programação. Ele é capaz, ainda, de mostrar como o programador analisou
e abstraiu o problema a resolver” (ASCENCIO; CAMPOS, 2007, p. 12).
Uma linguagem deve ao menos dar suporte a um paradigma, mas isso não
garante que a técnica associada ao paradigma será utilizada. Assinale a
alternativa que verbaliza corretamente essa assertiva:
a) Mesmo a linguagem suportando o paradigma, ainda assim o desenvolver
pode não utilizar.
b) Alguns paradigmas são complexos ao ponto de não serem aconselhados.
c) Códigos sem paradigmas tem melhor performance.
d) As linguagens obrigam a utilização do paradigma suportado.
e) A não utilização de um paradigma tende a ser uma máxima, assim as
linguagens têm retirado o suporte aos paradigmas.

54 U1 - Problemas no desenvolvimento de software


Referências
ASCENCIO, Ana F. G.; CAMPOS, Edilene A. V. Fundamentos da programação de
computadores: Algoritmos, Pascal, C/C++ e Java. 2. ed. São Paulo: Pearson, 2007.
CAMARGO, Heloísa de C. Programação Estruturada. Disponível em: <http://www2.
dc.ufscar.br/~heloisa/PLP2009/ProgEstr1.pdf>. Acesso em: 15 mar. 2017.
LEE, Richard. C.; TEPFENHART, William M. UML e C++. São Paulo: MAKRON Books,
2001.
MEDEIROS, E. Desenvolvendo software com UML 2.0: definitivo. São Paulo:
Pearson Makron Books, 2004.
PRESSMAN, Roger S. Engenharia de software: uma abordagem profissional. 7. ed.
Porto Alegre: AMGH, 2011.
SCHILDT, H. C.: Completo e total. 3. ed. São Paulo: Makron Books, 1996.
SOMMERVILLE, I. Engenharia de software. 8. ed. São Paulo: Pearson Addison-
Wesley, 2007.
TANEBAUM, Andrew S. Organização estruturada de computadores. 5. ed. São
Paulo: Pearson Prentice Hall, 2007.
THE CHROMIUM PROJECTS. Release process. Disponível em: <https://www.
chromium.org/developers/tech-talk-videos/release-process>. Acesso em: 15 mar.
2017.

U1 - Problemas no desenvolvimento de software 55


Unidade 2

Introdução à programação
orientada a objetos

Roque Maitino Neto

Objetivos de aprendizagem
O objetivo desta unidade é proporcionar a você a
compreensão do paradigma de orientação a objetos,
passando pela conceituação e caracterização de modelos
e pela abordagem de abstração, herança, polimorfismo e
encapsulamento, itens fundamentais em nosso contexto
e cujo entendimento permitirá, no futuro, a construção
de programas bem modularizados e com o máximo de
aproveitamento de código.

Seção 1 | Modelos
Nesta seção será apresentado o conceito de modelo e as características
que o tornam a base para o estudo futuro de classes, métodos e objetos.
Por meio de exemplo baseado no cotidiano, será explicada a separação
entre dados e operações, características típicas de um modelo. Por
fim, a seção aborda as diferentes descrições de um mesmo modelo,
dependendo do contexto em que ele está inserido.

Seção 2 | O paradigma da orientação a objetos


Nesta seção serão abordados os conceitos básicos do paradigma
de orientação a objetos e se basearão no estudo das representações de
objetos, as quais chamamos de modelos. A seção inclui a conceituação de
orientação a objeto e a descrição de seus pilares, quais sejam: abstração,
herança, polimorfismo e encapsulamento.
Introdução à unidade
Caro aluno! Seja bem-vindo à segunda unidade do nosso livro
didático!

Você já imaginou se pudéssemos representar coisas da vida


real – tal qual elas são – em um programa de computador? Já
imaginou se conseguíssemos reproduzir suas informações e
comportamentos na resolução de um problema computacional?
Pois com a orientação a objeto nós podemos e esta unidade
começa a nos dizer como.

Depois de estudarmos os fundamentos do desenvolvimento de


um programa e problemas que dele podem derivar, mudaremos
nosso foco agora para a base da programação orientada a
objetos. Esta unidade está dividida em duas seções: a primeira
trata dos modelos, abordados como representações de objetos e
processos do nosso cotidiano que poderão, no futuro, fazer parte
dos problemas computacionais que seremos chamados a resolver
por meio de uma linguagem orientada a objetos. Nesta seção são
oferecidos exemplos de como os modelos devem ser estruturados
e como dados e operações (ou comportamentos) são agregados
para a perfeita caracterização de um modelo.

A segunda seção aborda o paradigma da orientação a objetos


propriamente dito, tratando de seu conceito e das características
que distinguem este paradigma do procedural. Desejamos que
esta seção seja capaz de esclarecer temas importantes sobre
orientação a objetos e estimule você a se aprofundar mais no tema.

Boa leitura!

U2 - Introdução à programação orientada a objetos 59


60 U2 - Introdução à programação orientada a objetos
Seção 1

Modelos
Introdução à seção

A compreensão do paradigma da orientação a objetos inicia-


se pela compreensão do conceito e aplicação de modelos. Eles
são a representação simplificada de um objeto e incluem, em sua
definição, dados e comportamentos deste objeto. Além disso,
são a base para a criação de classes, item fundamental em um
programa orientado a objeto.

1.1 Conceito
O programa de computador é o meio pelo qual um computador
executa cálculos e funções. Ele é escrito com o uso de comandos,
operadores, variáveis e alguns outros recursos que, dispostos
corretamente, formam uma linguagem de programação. O que de
melhor um computador faz é receber, processar e disponibilizar
nomes, valores, fórmulas e textos que, de forma genérica,
chamamos de dados. Tudo isso, é claro, por meio de um programa.

O que nos interessa, por ora, são justamente os dados.


Santos (2003) nos ensina que o paradigma de programação
orientada a objetos considera que os dados a serem processados
e os mecanismos de processamento destes dados devem ser
considerados em conjunto. Esta relação entre os dados e as
operações neles aplicadas logo será mais bem detalhada.

Quando representamos elementos reais de forma simplificada


e padronizada, estamos criando um modelo para esses
elementos. "Modelos são representações simplificadas de objetos,
pessoas, itens, tarefas, processos, conceitos, ideias etc., usados
comumente por pessoas no seu dia a dia, independente do uso de
computadores" (SANTOS, 2003, p. 2).

U2 - Introdução à programação orientada a objetos 61


Questão para reflexão
Seria adequado, então, associarmos modelos a elementos que vemos
no mundo real?

1.2 Exemplificando
Os modelos geralmente agregam dados e as operações que se
aplicam a eles. Para entendermos melhor esse conceito, devemos
passar pelo exemplo que segue, adaptado de Santos (2003).

O restaurante POOeNaMesa, em seu procedimento diário,


permite que o próprio cliente se sirva da refeição que deseja. O
peso da comida e demais itens consumidos são então anotados
no que chamaremos de comanda, cujo desenho pode ser visto na
Figura 2.1. Quando o cliente pede a conta, o gerente calcula o valor
devido por meio do que foi anotado na comanda e a apresenta ao
cliente. O processo é representado na Figura 2.1.

Figura 2.1 | processo do restaurante POOeNaMesa

Gerente
O consumo
Cliente se confirma e
é anotado na
serve informa a
comanda
conta

Fonte: elaborada pelo autor.

Pronto, temos aqui um modelo do restaurante. De forma


simplificada, ele é capaz de agregar as informações necessárias
para a contabilização dos pedidos (peso da refeição, tipo e
quantidade de refrigerantes solicitados e a sobremesa, por
exemplo) e as operações ou procedimentos associados a essas
informações, tais como encerramento do pedido, apresentação
da conta para o cliente e inclusão de um novo item, entre outras.

62 U2 - Introdução à programação orientada a objetos


Figura 2.2 | Comanda do restaurante POOeNaMesa

Mesa número ___


Refeição (Kg)
Sobremesa
Refrigerante (2l)
Refrigerante (0,6l)
Refrigerante (lata)
Cerveja

Fonte: adaptada de Santos (2003).

Alternativamente, modelos que só contenham dados ou só


contenham operações também podem ser criados. Se, por exemplo,
criarmos um modelo que só contenha funções matemáticas e de
processamentos de dados que não necessitem de armazenamento,
teremos criado uma biblioteca de operações. Dependendo da sua
aplicação, esse expediente pode ser bastante útil.

A mesma forma de utilização de um modelo pode ser


considerada para submodelos. Usando o POOeNaMesa como
referência, imagine uma comanda específica para conter os
dados da sobremesa ou das bebidas consumidas. Esta comanda
específica conterá características gerais da comanda principal,
tal como o nome do restaurante, mas tratará especificamente
do armazenamento dos dados da sobremesa consumida, por
exemplo.

A Figura 2.3 mostra um exemplo da comanda específica da


sobremesa.

U2 - Introdução à programação orientada a objetos 63


Figura 2.3 | Comanda da sobremesa

Comanda da sobremesa
Mesa número ___
Possui outra
comanda?

Bolo (pedaço)
Torta (pedaço)
Brigadeiro (unidade)
Gelatina (unidade)

Fonte: elaborada pelo autor.

Dependendo do contexto em que um modelo está inserido, é


comum que ele assuma certas particularidades. Tomemos como
exemplo a representação das informações de uma pessoa. Se
estivermos tratando de um modelo em que uma pessoa é um eleitor,
alguns dados serão relevantes e outros não. Observe os exemplos.

Pessoa considerada como eleitor: neste caso, é necessário


conhecermos seu nome, endereço, número de inscrição, zona de
votação e seção. A operação de alteração de domicílio eleitoral
poderia ser aplicada neste modelo.

Pessoa considerada como aluno: para a composição do


modelo de aluno é necessário que se tenha o nome, número de
matrícula, nota da primeira prova, nota da segunda prova, faltas e
nota final, entre outros dados. Operações como consultar nota e
solicitar revisão de ausências são plausíveis neste contexto.

Pessoa considerada como motorista: neste caso, as


informações de nome do condutor, número da CNH, histórico
de multas e data da revalidação devem compor o modelo, assim
como as operações de consulta multas e solicita revalidação.

Lembre-se, a inclusão ou não de dados ou operações no modelo


dependem fortemente do contexto. Não faria sentido colocarmos,
nos respectivos modelos, o dado de salário do condutor do veículo
ou a operação de consulta nota para a representação de um eleitor.
Embora ambos possam ser necessários em alguma circunstância

64 U2 - Introdução à programação orientada a objetos


de representação de uma pessoa, no contexto em que foram
incluídos não teriam aplicação prática alguma e contrariariam o
princípio de simplificação ao qual todo modelo deve estar sujeito.

Antes de terminarmos nossa abordagem sobre modelos,


vale a pena tratamos da capacidade de serem reutilizados. Para
representar, por exemplo, diferentes alunos (cada um com
dados específicos e únicos, como o número de matrícula), não
precisamos necessariamente de um modelo para cada aluno.
Ao contrário, cada um deles deve ser representado pelo mesmo
modelo, respeitadas as especificidades de cada um.

Para saber mais


Mais exemplos de modelos você pode encontrar a partir da página
6 de: SANTOS, Rafael. Introdução à programação orientada a
objetos usando Java. Rio de Janeiro: Campus, 2003.

Mais um exemplo de modelo: Lâmpada de LED (Light Emitting


Diode)

Dado básico: estado de ligada ou desligada.

Operações: ligar, desligar, alterar cor1, mostrar estado2.

A mudança de estado se dá pelo ato de ligar ou desligar a


lâmpada. A Figura 2.4 mostra o modelo “Lâmpada”.

Figura 2.4 | O modelo “Lâmpada”, com seus dados e operações


Lâmpada
- Estado da lâmpada
- Acende
- Apaga
- Altera cor
- Mostra estado
Fonte: adaptada de Santos (2003, p. 7).

1
Em alguns modelos, isso é possível pelo acionamento e desligamento da lâmpada
via interruptor em menos de um segundo.
2
Operação desnecessária na vida real, mas necessária durante a modelagem.

U2 - Introdução à programação orientada a objetos 65


Esta representação coloca o nome do modelo (sem acento) na
primeira divisão do retângulo. Logo em seguida é descrito o único
dado (haverá mais que um, na maioria dos casos) do modelo. Na
terceira divisão da representação são exibidos os comportamentos
ou funções do modelo Lâmpada.

O conceito de modelo e as características apresentadas serão


bastante úteis a você na ocasião em que classes e objetos forem
abordados, na próxima unidade. Um pouco de prática, antes de
avançarmos para a segunda seção.

Atividades de aprendizagem
1.
A programação orientada a objetos separa claramente a
noção de o que é feito de como é feito. O que é descrito
como um conjunto de métodos e suas semânticas
associadas. O como de um objeto é definido pela sua
classe, que define a implementação dos métodos que o
objeto suporta. (ARNOLD; GOSLING; HOLMES, 2007, p.
62)

Assinale a alternativa que contém a expressão que melhor define “modelo”


em nosso contexto.
a) Abreviação.
b) Demonstração.
c) Simplificação.
d) Apresentação.
e) Representação.

2.
Programas processam dados: valores em uma conta
bancária, caracteres entradas por um teclado, pontos
em uma imagem, valores numéricos para cálculos. O
paradigma de programação orientada a objetos considera
que os dados a serem processados e os mecanismos de
processamento destes dados devem ser considerados em
conjunto. (SANTOS, 2003, p. 1)

66 U2 - Introdução à programação orientada a objetos


Entendidos como base para a criação de objetos no paradigma OO,
os modelos apresentam algumas características próprias. Assinale a
alternativa que contém apenas indicações de afirmações verdadeiras
relacionadas a tais características.
I) Modelos são capazes de agregar informações e operações sobre certos
objetos da vida real.
II) Um modelo genérico pode gerar modelos mais específicos,
dependendo do contexto em que é analisado.
III) Não é possível a criação de modelos que contenham apenas operações
e que não contenham dados do modelo, já que isso descaracterizaria o
modelo.
a) Apenas as afirmações I e II são verdadeiras.
b) Apenas as afirmações II e III são verdadeiras.
c) Apenas as afirmações I e III são verdadeiras.
d) Apenas a afirmação I é verdadeira.
e) Apenas a afirmação II é verdadeira.

U2 - Introdução à programação orientada a objetos 67


68 U2 - Introdução à programação orientada a objetos
Seção 2

O paradigma da orientação a objetos


Introdução à seção

O criador da expressão Programação Orientada a Objetos


(POO) foi Alan Kay, o mesmo que criou a linguagem Smalltalk. No
entanto, mesmo antes do termo ter sido criado, ideias conceituais
sobre Orientação a Objetos já estavam sendo aplicadas na
linguagem de programação Simula 67 (DOUGLAS, 2015). Na
sequência conceituaremos programação orientada a objetos e,
em seguida, serão apresentados seus quatro pilares.

2.1 Conceito
Programação Orientada a Objetos é um paradigma de
programação de computadores onde se usam classes e objetos,
criados a partir de modelos usados para representar e processar
dados (SANTOS, 2003, p. 4).

Questão para reflexão


Será que a simples ação de se programar um computador usando
classes, objetos e os recursos que deles advêm significa usar, em
sua plenitude, o paradigma de orientação a objetos?

Douglas (2015) defende que, ao criar um programa que


“executa sequencialmente os métodos” de um objeto ou “atualiza
seus atributos”, você estará utilizando conceitos da programação
procedural, não do paradigma OO. O entendimento de métodos
e atributos ficará completo quando estudarmos classes e objetos,
na próxima unidade. Antes de chegarmos até lá, entenda que as
funções da linguagem procedural são os métodos e os dados são
aqui chamados de atributos.

U2 - Introdução à programação orientada a objetos 69


2.2 Fundamentos da orientação a objeto

Machado (2015) entende que o paradigma da orientação a


objeto é fundamentado por quatro características:

Abstração: a abstração está relacionada à definição precisa


de um objeto. Esta definição inclui sua identificação (nome), suas
características (ou propriedades) e o conjunto de ações que ele
desempenha. Tomemos como exemplo um cachorro: o objeto
cachorro deve ser único e não poderá ser repetido. Neste ponto,
o objeto já tem identidade definida. Sua caracterização se dá pela
cor do pelo, peso, raça e por aí vai. Por fim, as ações que ele é
capaz de desempenhar incluem latir, farejar, pular etc. Pronto!
Conseguimos abstrair o objeto cachorro e o temos perfeitamente
definido.

Herança: por meio desta característica do paradigma OO, um


objeto filho herdará características e comportamentos do objeto
pai. Quando estiver criando classes, você vai perceber que essa
possibilidade permite reaproveitamento de código e torna o
trabalho mais racional e otimizado. A ideia da herança é mostrada
na Figura 2.5.

Figura 2.5 | Herança na orientação a objetos

Funcionário Nome, cargo, data de admissão

Nome, cargo, data de admissão,


Gerente quantidade de funcionários que
gerencia, senha de acesso
Fonte: elaborada pelo autor.

Podemos ilustrar a herança com os dados do modelo


“Funcionário”. Eles devem identificá-lo, por exemplo, com nome,
cargo e data de admissão, entre outros. No entanto, existe uma
categoria especial de funcionário chamado “Gerente” que, além
desses dados, deverá também conter a quantidade de funcionários
que gerencia e a senha de acesso ao sistema da empresa.

70 U2 - Introdução à programação orientada a objetos


O modelo “Gerente” herda características do modelo
“Funcionário”, especificamente os dados Nome, Cargo e Data de
Admissão. Esta é a ideia central da herança: aproveitar características
e comportamentos gerais em modelos mais específicos. Esta
particularidade nos permite entender que um objeto mais abaixo
é um caso específico do objeto acima, ou seja, ele possui as
características gerais do objeto pai, acrescidas de mais algumas
especificidades que o diferem do seu ancestral.

Polimorfismo: antes de entendermos este conceito, melhor


entendermos a composição de seu nome. O termo “poli” significa
muitos, vários. Já “morfismo” remete a formas. Então temos
que polimorfismo significa “muitas formas” e é exatamente esta
definição que caracteriza com exatidão este pilar da orientação a
objetos.

Já sabemos que um objeto filho herda características e ações


de seu objeto pai, situado hierarquicamente acima do primeiro.
Contudo, em certos casos, precisaremos definir ações do objeto
de outra forma. Assim, polimorfismo consiste em dar outra forma
à alguma ação herdada do objeto pai.

Imagine um objeto chamado eletrodoméstico. Uma das suas


ações consiste em ligar. No entanto, os objetos forno de micro-
ondas e televisão – que são especializações de eletrodomésticos
– são ligados de formas diferentes. Por isso, para cada um dos
objetos filho, a ação ligar será descrita de modo diferente.

Encapsulamento: neste nosso contexto, o termo


encapsulamento está relacionado à proteção ou ocultação dos
dados do objeto. Para entender esta característica do paradigma
OO, pense em uma câmera fotográfica automática. Santos (2003)
ensina que, quando você clica o botão para tirar a foto, o processo
de seleção de velocidade e de abertura apropriada do obturador
é iniciado. Para quem está operando a câmera, os detalhes
relacionados velocidade, tipo de filme e ajuste à iluminação do
ambiente não tem relevância alguma. O que importa, de fato, é
que a foto seja tirada.

Ainda usando a analogia da máquina fotográfica, a ocultação


do comportamento e dos dados relativos ao processo permite

U2 - Introdução à programação orientada a objetos 71


que o usuário se preocupe apenas em tirar a foto e o impede de
modificar dados e comportamentos da câmera. Assim será quando
você estiver escrevendo classes e utilizando objetos.

Para saber mais


Em pouco mais de nove minutos, Danilo Filitto sintetiza conceitos
básicos do paradigma de orientação a objetos, com clareza
e objetividade. Veja o vídeo, acessando o link: Disponível em:
<https://www.youtube.com/watch?v=vhvmZfxZhPw. Acesso em:
28 abr. 2017.

Não deixe de fazer as atividades propostas, bons estudos e até


a próxima seção!

Atividades de aprendizagem

1.
Em Programação Orientada a Objetos, os dados
pertencentes aos modelos são representados por tipos
de dados nativos, ou seja, que são característicos da
linguagem de programação. Dados também podem ser
representados por modelos já existentes na linguagem ou
por outros modelos criados pelo programador. (SANTOS,
2003, p. 4)

Analise as afirmações referentes as quatro características fundamentais


do paradigma de orientação a objetos.
I) Polimorfismo significa “muitas formas” e relaciona-se com a capacidade
de um objeto em assumir forma que oculte e proteja seus dados do
acesso de outros objetos.
II) A abstração está relacionada à definição de um objeto, incluindo sua
identificação, características e ações.
III) A característica da Herança nos permite entender que um objeto mais
abaixo na hierarquia é um caso genérico do objeto mais acima.
IV) É por meio do encapsulamento que um objeto consegue transmitir
suas características e seu conjunto de ações a outro objeto.
Assinale a alternativa que contém apenas indicações de afirmações
verdadeiras.
a) Apenas as afirmações II e III são verdadeiras.

72 U2 - Introdução à programação orientada a objetos


b) Apenas as afirmações I, II e IV são verdadeiras.
c) Apenas a afirmação II é verdadeira.
d) Apenas as afirmações I e II são verdadeiras.
e) Apenas a afirmação I é verdadeira.

2.
Com o polimorfismo podemos projetar e implementar
sistemas que são facilmente extensíveis – novas classes
podem ser adicionadas a partes gerais do programa
com pouca ou nenhuma modificação, contanto que as
novas classes façam parte da hierarquia de herança que o
programa processa genericamente (DEITEL, 2003, p. 336).

Assinale a alternativa que contém apenas expressões relacionadas


diretamente ao polimorfismo.
a) Muitas formas, identificação precisa de um objeto.
b) Ocultação de detalhes de implementação, herança de características.
c) Simplificação do objeto, muitas formas.
d) Muitas formas, alteração da forma herdada do objeto pai.
e) Definição genérica do objeto, herança de dados.

Fique ligado
Nesta unidade, estudamos:
• Conceito de modelo.
• Agrupamento de dados e operações em um modelo.
• Exemplos de modelos.
• Especialização de modelos, conforme o contexto.
• Conceito do paradigma de orientação a objetos.
• Conceitos e exemplos de abstração, herança, polimorfismo
e encapsulamento.

Para concluir o estudo da unidade


Caro aluno, chegamos ao fim desta unidade!
Por causa da diversidade de enfoques, da grande quantidade
de material disponível e da natural necessidade em aprofundar-se

U2 - Introdução à programação orientada a objetos 73


no tema, a mensagem de final de unidade não poderia ser outra:
não pare por aqui seus estudos sobre o paradigma de orientação
a objetos. Todo o conhecimento adquirido nesta etapa lhe será
precioso quando começar a modelar soluções usando classes,
atributos e métodos. Com o uso dos recursos de herança,
encapsulamento e polimorfismo, para citar alguns, você conseguirá
desenvolver programas flexíveis e muito bem modularizados, o
que facilitará a manutenção e alteração do código.
Os sites a seguir apresentam farto material sobre os temas que
merecem sua atenção:
Disponível em: <www.caelum.com.br>. Acesso em: 28 abr.
2017.
Disponível em: <http://www.devmedia.com.br/>. Acesso em:
28 abr. 2017.
Bons estudos!

Atividades de aprendizagem da unidade


1. O criador da expressão Programação Orientada a Objetos (POO) foi
Alan Kay, o mesmo que criou a linguagem Smalltalk. No entanto, mesmo
antes do termo ter sido criado, ideias conceituais sobre Orientação a
Objetos já estavam sendo aplicadas na linguagem de programação Simula
67 (DOUGLAS, 2015).
Assinale a alternativa que contém a sentença que mais bem caracteriza o
paradigma de orientação a objetos.
a) Paradigma fortemente baseado em funções e procedimentos.
b) Paradigma apropriado para representações em ambiente gráfico.
c) Paradigma apropriado para representações matemáticas.
d) Paradigma que representa dados e comportamentos da vida real.
e) Paradigma fortemente baseado em aspectos e eventos.

2.
Imagine uma forma (molde) de bonecos de gessos.
Pois bem, essa é nossa classe ou tipo, ou seja, define o
formato, tamanho e diversos outros aspectos dos objetos
fabricados, no caso, os bonecos de gesso. Percebeu a
diferença? A classe é um molde para os objetos. Quando
se diz: “Instância de uma classe ou tipo”, nada mais é do
que o objeto dessa classe ou tipo. (SANTIAGO, 2017, [s.p.])

74 U2 - Introdução à programação orientada a objetos


Assinale a alternativa que contém a sequência que indica corretamente as
características (CA) e comportamentos (CO) de um modelo que represente
um objeto “cachorro”.
Cor do pelo, tom do latido, latir, pular, dormir.
a) CO, CA, CA, CA, CA.
b) CA, CA, CA, CO, CO.
c) CA, CO, CO, CO, CA.
d) CA, CA, CO, CO, CO.
e) CO, CA, CO, CA, CO.

3. Encapsulamento é derivado do termo encapsular, que em programação


orientada a objetos significa separar o programa em partes, o mais isolado
possível. A ideia é tornar o software mais flexível, fácil de modificar e de
criar novas implementações (DAVID, 2017).
Considerando o encapsulamento como uma das características do
paradigma de orientação a objetos. Assinale a alternativa que melhor o
define.
a) Definição de um tipo e do comportamento que esse tipo deve possuir.
b) Efeito direto da utilização de herança e polimorfismo no código.
c) Funcionalidade que permite reaproveitamento direto de classes.
d) Característica que contribui para um comportamento do modelo
assuma várias formas.
e) Característica que permite proteger dados e comportamentos de um
modelo de acessos não autorizados.

4. Um modelo comumente contém operações ou procedimentos


associados a ele. Essas operações são listas de comandos que processarão
os dados contidos no próprio modelo (e, em alguns casos, dados
adicionais). Em um restaurante hipotético, algumas dessas operações
poderiam incluir modificação do estado de um pedido, encerramento
do pedido e apresentação de conta ao cliente, por exemplo (SANTOS,
2003).
Considerando características e utilizações dos modelos na orientação a
objetos, analise as afirmações que seguem e assinale a alternativa que
contém apenas indicações de afirmações verdadeiras.
I) Um modelo pode derivar de outro modelo, tornando-se submodelo
deste.
II) A criação de um modelo completo chamado Pessoa é vantajosa em
relação à criação de modelos mais especializados de Pessoa.
III) É possível a criação de modelos que apenas contenham operações ou
apenas contenham dados.
a) Apenas a afirmação III é verdadeira.

U2 - Introdução à programação orientada a objetos 75


b) As afirmações I, II e III são verdadeiras.
c) Apenas as afirmações I e III são verdadeiras.
d) Apenas a afirmação I é verdadeira.
e) Apenas as afirmações I e II são verdadeiras.

5.
Objetos são instâncias de classes que determinam
qual informação um objeto contém e como ele pode
manipulá-la. Um programa desenvolvido com uma
linguagem de programação orientada a objetos manipula
estruturas de dados através dos objetos da mesma forma
que um programa em linguagem tradicional utiliza
variáveis. (DAVID, 2017, [s.p.])

Considerando as características da Herança, assinale a alternativa que


contém a sequência de expressões que completam corretamente a
sentença que segue.
A herança permite ____________ e ___________ modelos já criados
e testados. É por meio da especialização que um novo modelo define
comportamentos e assim se torna uma versão especializada do
________________.
a) Estender - reaproveitar - submodelo.
b) Estender - reaproveitar - supermodelo.
c) Estender - reescrever - submodelo.
d) Estender - encapsular - supermodelo.
e) Generalizar - reaproveitar - submodelo.

76 U2 - Introdução à programação orientada a objetos


Referências
ARNOLD, K.; GOSLING, J.; HOLMES, D. A linguagem de programação Java. 4. ed.
Porto Alegre: Bookman, 2007.

DAVID, H. Encapsulamento, polimorfismo, herança em Java. Disponível em:


<http://www.devmedia.com.br/encapsulamento-polimorfismo-heranca-em-
java/12991>. Acesso em: 28 abr. 2017.

DEITEL, H. M. Java: Como programar. 6. ed. Porto Alegre: Bookman, 2003.

DOUGLAS, M. Você sabe, com certeza, o que é orientação a objetos? Objecto


Pascal Programming, 27 dez. 2015.

MACHADO, H. Os 4 pilares da programação orientada a objetos. 2015 Disponível


em: <http://www.devmedia.com.br/os-4-pilares-da-programacao-orientada-a-
objetos/9264>. Acesso em: 4 fev. 2017.

SANTOS, R. Introdução à programação orientada a objetos usando Java. Rio de


Janeiro: Campus, 2003.

SANTIAGO, F. Orientação a objetos: simples assim! Disponível em: <http://www.


devmedia.com.br/orientacao-a-objetos-simples-assim/3254>. Acesso em: 28 abr.
2017.

U2 - Introdução à programação orientada a objetos 77


Unidade 3

Classes e objetos

Adriano Sepe

Objetivos de aprendizagem
Compreender como os conceitos abordados na
introdução de programação orientada a objetos são
aplicados a uma linguagem de programação moderna e
elegante. Apresentaremos passo a passo como criamos um
projeto dentro da ferramenta Visual Studio, e a partir disso,
desenvolveremos uma versão de um aplicativo demonstrativo,
utilizando programação estruturada, por fim utilizaremos a
abordagem orientada a objetos.

Seção 1 | Aprendendo através da prática


Introduzindo uma dinâmica baseada na observação de um problema
modelo, e como resolvê-lo através da programação estruturada.

Seção 2 | Base conceitual


Conceitos-chave da programação orientada a objetos, mas dentro
de um contexto de uma linguagem de programação, nesse caso C#.
Avançaremos para abordagens relacionadas a conceitos necessários para
a construção de aplicações mais complexas.

Seção 3 | Codificando orientado a objetos


Desenvolvendo a dinâmica apresentada na Seção 1, através dos
conceitos abordados nas Seções 2 e 3.
Introdução à unidade
Vamos estudar como os conceitos da programação orientada
a objetos podem ser aplicados, através de uma linguagem que
dê o devido suporte. Rediscutiremos conceitos, mas com uma
abordagem prática, pois dispomos de uma linguagem que pode
ser utilizada para operar a máquina e executar tarefas.

Ainda nesta unidade vamos criar dois projetos, ambos com os


mesmos requisitos, mas concebidos com técnicas diferenciadas.
No primeiro, iremos resolver o nosso problema utilizando a
abordagem estruturada, dividindo assim nosso programa em
pequenos blocos de códigos (ASCENCIO; CAMPOS, 2007), mas
mantendo a abordagem sequenciada. Já no segundo, aplicaremos
a técnica de programação orientada a objetos e será possível
observar como pequenos blocos podem ser interligados, e serão
executados sequencialmente.

Para todos esses casos vamos utilizar a ferramenta Visual Studio


e a linguagem C# (leia c sharp), logo em nossa primeira seção
vamos demonstrar passo a passo como podemos criar um projeto
dentro do Visual Studio, e assim colocarmos o nosso código para
rodar. Para o nosso segundo projeto, esses passos não serão
ilustrados, mas podem ser efetuados da mesma forma.

A partir do momento em que nos debruçamos em uma


linguagem, será necessário que conheçamos o seu dialeto
(palavras reservadas) e sua gramática (sintaxe), e caso possível
possamos assimilá-los e memorizá-los, até para conseguirmos ter
algum tipo de desenvoltura ao nos colocarmos na condição de
autores de códigos.

U3 - Classes e objetos 81
82 U3 - Classes e objetos
Seção 1

Aprendendo através da prática


Introdução à seção

Aprenderemos através da prática, vamos construir o


conhecimento partindo de um enunciado, que descreverá as
características que uma aplicação deverá ter. A partir dessa
apresentação demonstraremos como isso poderá ser feito através
do ferramental disponível.

1.1 Definições

Dentro da técnica de desenvolvimento orientado a objetos,


classe, sem dúvida, é um conceito primário e fundamental. É
através da classe que descrevemos todas as características que um
objeto conceitual ou físico possui.

Vamos partir para uma dinâmica baseada em um problema


modelo, isso quer dizer que antes de vasculharmos os conceitos
vamos discutir um problema, que inicialmente será resolvido
utilizando a técnica da programação procedural. Isso porque,
conforme observamos, é algo natural para nós, então vamos avaliar
desde a simples codificação de uma lógica qualquer, até o processo
de entrada de dados. Ao final dessa dinâmica vamos ter resolvido o
mesmo problema utilizando a programação estruturada e também
a orientada a objetos. Essa dinâmica está organizada da seguinte
forma, nesta seção apresentaremos o problema, a ferramenta e
a solução, utilizando para isso a programação estruturada; já nas
Seções 2 e 3 discutiremos conceitos aplicadas à linguagem C#;
por fim concluiremos, através da Seção 3, onde abordaremos a
resolução, mas com técnicas orientadas a objetos.

U3 - Classes e objetos 83
1.2 Introduzindo uma dinâmica

Considere o seguinte exercício, que resolveremos utilizando


inicialmente a programação procedural. Isso nos ajudará, como
fizemos na Unidade 1, visualizar dois níveis de organização. Vamos
dizer assim: de onde partimos e onde devemos chegar. Vejamos a
problematização através desse exercício extraído de uma lista de
atividades que comumente é resolvida por alunos do primeiro ou
segundo semestre de programação, vamos a ele:

Faça um programa que leia o código, a descrição, o valor unitário


e a quantidade em estoque dos 50 produtos comercializados por
uma papelaria. Essas informações devem ser armazenadas em um
registro do tipo vetor. Depois da leitura, faça:

• Uma rotina que permita alterar a descrição, o valor unitário


e a quantidade em estoque de um determinado produto,
que deverá ser localizado através de seu código.

• Uma rotina que mostre todos os produtos cuja descrição


comece com uma determinada letra (informada pelo
usuário).

• Mostre todos os produtos com quantidade em estoque


inferior a cinco unidades.

Antes de iniciarmos a resolução vamos entender que nosso


objetivo é atender a todos os requisitos. Vamos procurar resolvê-
lo passo a passo, permitindo que todas as alterações possam
ser devidamente reproduzidas em um ambiente parecido. Para
resolução desse problema modelo utilizaremos uma linguagem
que suporte a programação procedural, bem como a programação
orientada a objetos. Vamos utilizar uma dupla bem conhecida por
muitos estudantes que iniciam seus estudos sobre programação.
Essa dupla pode ser utilizada sem custo de licenciamento, isso para
máquinas que estejam rodando o sistema operacional Windows.
Vamos utilizar a ferramenta IDE Visual Studio 2015, que pode ser
adquirido através do link <https://www.visualstudio.com/pt-br/vs/
community/>, nela vamos utilizar a linguagem C#. Na medida que
algumas características técnicas forem aparecendo, vamos fazer
pequenos destaques técnicos, e evidenciar o que for importante.

84 U3 - Classes e objetos
1.3 Projeto estruturado

Inicialmente, vamos entender aquilo que deveremos fazer,


nessa etapa é natural que não consigamos visualizar o código,
mas o entendimento dos requisitos, isso sim é fundamental. Para
facilitar vamos fazer uma pequena lista, descrevendo os requisitos
de forma linear, vejamos como isso poderia ser feito:

• Definir uma estrutura de dados composta para representar


o produto.

• Criar uma rotina para receber os 50 produtos.

• Desenvolver uma rotina que permita alterar os atributos de


um produto.

• Desenvolver uma rotina que permita listar os produtos que


iniciem com uma determinada letra.

• Desenvolver uma rotina que permita visualizar os produtos


com baixa em estoque.

• Disponibilizar um menu onde o usuário poderá optar


por uma das seguintes funções: “Alterar Produto”, “Listar
Produtos (Filtro Letra)”, “Baixa de Estoque” e “Sair”.

Vale destacar que, basicamente temos a famosa dinâmica de


entrada, processamento e saída. Natural você se questionar, mas
essa aplicação não terminará no final de um ciclo de processamento.
Essa observação é pertinente, pois o ciclo de entradas acontecerá
enquanto o usuário não optar por sair. Neste caso, ao invés do
usuário rodar mais de uma vez a aplicação, ele poderá usufruir de
várias saídas a partir dos dados já coletados. Sem mais delongas,
vamos criar o nosso projeto chamado Estoque, vejamos como
isso poderá ser feito.

Vamos focar inicialmente no bloco principal, que


necessariamente deve ser chamado de main, ele é o ponto de
entrada de nossa aplicação. Vejamos o estado inicial do nosso
programa já com alguns comentários, também foram extraídos
elementos que não serão observados neste momento:

U3 - Classes e objetos 85
namespace Estoque

class Program

// Ponto de entrada da nossa aplicação

static void Main(string[] args)

// O fluxo de execução de nossa aplicação

// será definido nesse bloco

Fonte: elaborado pelo autor.

Lembre-se nessa primeira versão de que vamos construir o


nosso código utilizando as técnicas de programação estruturada.

Seguindo o nosso roteiro vamos definir uma estrutura de dados


composta para representar o nosso produto, para isso utilizaremos
o recurso chamado struct, vejamos como isso se apresenta:

// Definição de uma estrutura de dados que representará o


nosso produto

// cada campo será utilizado para armazenar um dado


relativo à nossa

// estrutura de dados complexa

struct Produto {

public int Codigo;

public string Descricao;

86 U3 - Classes e objetos
public double ValorUnitario;

public double QtdeEstoque;

Fonte: elaborado pelo autor.

Existe uma grande semelhança com a declaração de Habitante,


isso lá na Unidade 1. Essa similaridade é natural nas linguagens,
até porque trabalham em cima de uma mesma arquitetura
computacional, então metaforicamente poderíamos compreender
que, podemos mudar a carenagem de um carro, remodelando
conforme o nosso anseio, mas independente da forma que
dermos, esse estará ligado a um chassi que definirá características
básicas.

Ainda sobre a nossa estrutura de dados, vale destacar a palavra


reservada public, ela define as regras de acesso à informação
associada. Logo mais vamos discutir o conceito de modificadores
de visibilidades, isso nos ajudará a compreender como a orientação
a objetos é algo que permite estabelecer limites.

Na sequência estabeleceremos o objetivo de receber os dados


dos 50 produtos, aqui utilizaremos os mecanismos padrão de
interação com o usuário, isso para uma aplicação do tipo texto,
sendo que a entrada padrão é o teclado, já a saída será o monitor.
Esse algoritmo será construído baseado no acesso aos produtos
através de parâmetro, aqui chamamos de “estoque”, na sequência
executará um laço, permitindo que, para cada produto o usuário
possa informar os dados esperados: código, descrição, valor unitário
e quantidade em estoque. Vejamos como esse bloco se apresenta:

static void ExecutaLeitura(Produto[] estoque)

for (int i = 0; i < 50; i++)

Console.WriteLine("Obtendo Estoque...");

U3 - Classes e objetos 87
// Interage com o usuário indicando um novo produto

Console.WriteLine("Produto N: {0}", i + 1);

// Interage solicitando o Código

Console.Write("Código: ");

estoque[i].Codigo = Convert.ToInt32(Console.ReadLine());

// Interage solicitando a Descrição

Console.Write("Descrição: ");

estoque[i].Descricao = Console.ReadLine();

// Interage solicitando o Valor Unitário

Console.Write("Valor Unitário: ");

estoque[i].ValorUnitario = Convert.ToDouble(Console.
ReadLine());

// Interage solicitando o Valor Unitário

Console.Write("Qtde Estoque: ");

estoque[i].QtdeEstoque = Convert.ToDouble(Console.
ReadLine());

// Remove todos os caracteres da tela

Console.Clear();

}
Fonte: elaborado pelo autor.

88 U3 - Classes e objetos
Como podemos observar, a saída padrão se dá através das
instruções Console.Write ou Console.WriteLine, já na entrada
utilizamos apenas o Console.ReadLine. Outro ponto que vale
o destaque está no fato da sintaxe baseada em ponto, isso é
conceitual e faz parte das características da linguagem no aspecto
léxico. Observa-se que tanto a leitura quanto a escrita partem de
um ponto em comum, o Console. Este, por sua vez, representa
a aplicação. Assim, semanticamente devemos compreender
que a leitura de uma linha de dados (ReadLine) será feita através
do Console (tela), já a escrita (Write ou WriteLine) será feita no
Console, dessa forma ambas as ações partem de um mesmo
contexto, o Console. Isso é um prenúncio da organização que
obtemos através da orientação a objetos, mas deixamos isso para
depois. Uma palavra reservada nova, chamada static, define o
contexto em que o bloco de código será executado, isso também
será abordado mais à frente, considere apenas que haverá escopos
diferentes para execução de um bloco, perceba que para esse
projeto estruturado sempre utilizaremos o contexto estático.

Seguindo na lista dos nossos objetivos, vamos desenvolver a


rotina que permitirá a alteração de um produto, vale a ressalva
que, caso não encontre o produto com o código informado,
então nada será efetuado. Esse algoritmo será construído baseado
no acesso aos produtos através de parâmetro, aqui chamamos
de “estoque”, na sequência fará uma interação com o usuário
solicitando o código do produto a ser alterado, só então, através
de um laço, avaliará produto por produto, e caso o critério de
filtro seja atendido, código igual ao informado, então solicitará
novamente os dados do produto ao usuário.

static void Alterar(Produto[] estoque)

// Interage solicitando o Código

Console.Write("Código: ");

int codigo = Convert.ToInt32(Console.ReadLine());

U3 - Classes e objetos 89
// Controlará o localização de algum produto

int encontrou = 0;

for (int i = 0; i < 50; i++)

if (estoque[i].Codigo == codigo)

encontrou++;

// Interage solicitando a Descrição

Console.Write("Descrição: ");

estoque[i].Descricao = Console.ReadLine();

// Interage solicitando o Valor Unitário

Console.Write("Valor Unitário: ");

estoque[i].ValorUnitario = Convert.ToDouble(Console.
ReadLine());

// Interage solicitando o Valor Unitário

Console.Write("Qtde Estoque: ");

estoque[i].QtdeEstoque = Convert.ToDouble(Console.
ReadLine());

90 U3 - Classes e objetos
// Se encontrou for igual a zero, então não foi localização
nenhum produto

// com o código informado

if (encontrou == 0)

Console.WriteLine("Nenhum produto encontrado.");

// Vamos aqui bloquear a execução para que o usuário posso


visualizar a saída

Console.Write("Pressione qualquer tecla para continuar.");

Console.ReadKey();

}
Fonte: elaborado pelo autor.

A seguir vamos avaliar como podemos atender ao próximo


requisito da nossa lista, devemos criar uma rotina que permita
apresentar ao usuário as mercadorias que iniciem com um
determinado caractere, que será fornecido pelo usuário no
momento da execução da rotina. Esse algoritmo será construído
baseado no acesso aos produtos através de parâmetro, aqui
chamamos de “estoque”, na sequência fará uma interação com o
usuário solicitando a letra na qual deve pesquisar, só então, através
de um laço, avaliará produto por produto, e caso o critério de filtro
seja atendido, se o primeiro caractere da descrição for igual ao
informado, então apresentará os detalhes. Vejamos a codificação
desse algoritmo:

static void ListaProdutos(Produto[] estoque)

// Interagindo com o usuário

U3 - Classes e objetos 91
Console.WriteLine("Listando produtos...");

// Interage solicitando a Letra

Console.Write("Letra: ");

// Com esse código conseguimos extraír o primeiro

// caracter informado

char letra = Console.ReadLine().First();

// Controlará o localização de algum produto

int encontrou = 0;

// Isso acontecerá para todas as mercadorias

for (int i = 0; i < 50; i++)

// Caso o primeiro caracter da mercadoria

// for igual a letra então ...

if (estoque[i].Descricao[0] == letra)

encontrou++;

// Interagindo apresentando o Código

Console.WriteLine("Código: {0}", estoque[i].Codigo);

// Interagindo apresentando a Descrição

92 U3 - Classes e objetos
Console.WriteLine("Descrição: {0}", estoque[i].Descricao);

// Interagindo apresentando o Valor Unitário

Console.WriteLine("Valor Unitário: {0}", estoque[i].


ValorUnitario);

// Interagindo apresentando a Qtde Estoque

Console.WriteLine("Qtde Estoque: {0}", estoque[i].


QtdeEstoque);

// Se encontrou for igual a zero, então não foi localização


nenhum produto

// que inicie com o caracter pesquisado

if (encontrou == 0)

Console.WriteLine("Nenhum produto encontrado.");

// Vamos aqui bloquear a execução para que o usuário posso


visualizar a saída

Console.Write("Pressione qualquer tecla para continuar.");

Console.ReadKey();

}
Fonte: elaborado pelo autor.

U3 - Classes e objetos 93
Nossa última rotina relativa às funcionalidades requeridas no
enunciado deverá apresentar os produtos com baixa em estoque,
então nesse caso não haverá a interação do usuário, apenas a
saída do programa baseado nos dados já alimentados, o estoque,
bem como a quantidade mínima, que neste caso foi fixada em 5.
Esse algoritmo será construído baseado no acesso aos produtos
através de parâmetro, aqui chamamos de “estoque”, na sequência
através de um laço avaliará produto por produto, e caso o critério
de filtro seja atendido, quantidade inferior a 5, então apresentará
os detalhes do produto com baixa em estoque. Vejamos o código
desenvolvido para isso:

static void ListarEstoqueBaixo(Produto[] estoque)

// Interagindo com o usuário

Console.WriteLine("Analisando estoque...");

int encontrou = 0;

// Isso acontecerá para todas as mercadorias

for (int i = 0; i < 50; i++)

// Caso o primeiro caracter da mercadoria

// for igual a letra então ...

if (estoque[i].QtdeEstoque < 5)

encontrou++;

// Interagindo apresentando o Código

94 U3 - Classes e objetos
Console.WriteLine("Mercadoria {0} (Qtde: {1})", estoque[i].
Codigo, estoque[i].QtdeEstoque);

// Se encontrou for igual a zero, então estoque OK!

if (encontrou == 0)

Console.WriteLine("Nenhum produto está com estoque


baixo no momento.");

// Vamos aqui bloquear a execução para que o usuário posso


visualizar a saída

Console.Write("Pressione qualquer tecla para continuar.");

Console.ReadKey();

}
Fonte: elaborado pelo autor.

Vejamos que todos os requisitos foram atendidos através da


modularização das rotinas desejadas, falta-nos agora criarmos
o nosso aplicativo, através dele o usuário poderia “executar” as
rotinas necessárias, e assim obter as saídas desejadas. Esse bloco
será o ponto de entrada da nossa aplicação, onde ao iniciar deverá
garantir a construção da estrutura de dados que representará o
nosso estoque (vetor de 50 produtos), na sequência permitirá
que os dados sejam alimentados pelo usuário, isso através do
acionamento do método ExecutaLeitura, por fim é esperado
que ele mantenha a aplicação em funcionamento, para isso
utilizaremos um laço que solicitará ao usuário uma opção, baseada

U3 - Classes e objetos 95
em um menu apresentado, inclusive a opção que permitirá finalizar
a aplicação. Vejamos como isso poderia ser feito!

// Ponto de entrada da nossa aplicação

static void Main(string[] args)

// Criamos uma variável estoque contendo 50 produtos

Produto[] estoque = new Produto[50];

// Executa a rotina de leitura

ExecutaLeitura(estoque);

// Remove todos os caracteres da tela

Console.Clear();

bool sair = false;

do

// Interage com o usuário apresentando o Menu

Console.WriteLine("OPÇÕES DISPONÍVEÍS *******");

Console.WriteLine("[A]lterar Produto");

Console.WriteLine("[L]istar Produtos (Letra)");

Console.WriteLine("[E]stoque Baixo");

Console.WriteLine("[S]air");

96 U3 - Classes e objetos
// Solicita a opção desejada

// para facilitar vamos garantir que mesmo

// digitando um caracter minusculo, vamos converter

// para maiusculo

var op = Console.ReadLine().ToUpper();

// Remove todos os caracteres da tela

Console.Clear();

switch (op)

case "A":

Alterar(estoque);

break;

case "L":

ListaProdutos(estoque);

break;

case "E":

ListarEstoqueBaixo(estoque);

break;

case "S":

sair = true;

break;

default:

U3 - Classes e objetos 97
// Interage com o usuário

Console.WriteLine("Opção inválida!");

// Vamos aqui bloquear a execução para que o usuário


visualize

Console.Write("Pressione qualquer tecla para continuar.");

Console.ReadKey();

break;

// Remove todos os caracteres da tela

Console.Clear();

} while (!sair);

Console.WriteLine("Aplicativo finalizado...");

}
Fonte: elaborado pelo autor.

Para melhor entendimento, na sequência, você poderá visualizar


como nosso arquivo Program.cs ficou estruturado.

using System;

using System.Linq;

namespace Estoque

struct Produto

98 U3 - Classes e objetos
{

public int Codigo;

public string Descricao;

public double ValorUnitario;

public double QtdeEstoque;

class Program

static void ExecutaLeitura(Produto[] estoque) { ... }

static void Alterar(Produto[] estoque) { ... }

static void ListaProdutos(Produto[] estoque) { ... }

static void ListarEstoqueBaixo(Produto[] estoque) { ... }

static void Main(string[] args) { ... }

}
Fonte: elaborado pelo autor.

U3 - Classes e objetos 99
Utilizamos alguns recursos que são exclusivos da linguagem
C#, recursos como Console e Convert não devem ser focados,
pois como foi comentado, são exclusivos da linguagem. Deve-se
perceber a mudança na forma de nomear as variáveis e parâmetros,
isso se considerarmos o código desenvolvido na Unidade 1 e agora
nesta unidade, isso é sem dúvida um ponto de confusão para
quando iniciamos em uma tecnologia. Essas variações podem ter
origem na linguagem, vamos entender como a sintaxe de escrita,
ou então por convenções, que são nada mais que combinados
entre os profissionais de uma tecnologia. Colocando de uma
outra forma, seriam as regras de criação de código que um grande
número de profissionais adota, sem que isso seja uma regra
imposta pela sintaxe da linguagem.

Na próxima vez que retornarmos para discutir os requisitos


dessa aplicação será através da técnica de programação a objetos.
Observe que a forma com que a aplicação foi desenvolvida não
estabelece verdades absolutas. Assim, é natural que haja críticas
sobre como poderíamos melhorá-la, ou até como não deveríamos
ter codificado, mas isso é assunto para um outro livro, focado em
melhores práticas e padrões.

1.4 Olhando para objetos

Conforme apresentado anteriormente, um elemento primário


da técnica de programação orientada a objetos se chama classe. É
através da classe que descrevemos como um determinado objeto
se apresenta, na perspectiva computacional. Dessa forma vamos
avaliar alguns objetos que possuiremos em nossa aplicação. Antes
de construirmos a aplicação Estoque, fizemos uma lista com todos
os requisitos necessários para atender à solicitação, agora vamos
mudar a estratégia: identificaremos quais são os objetos distintos,
e para cada um vamos listar as suas características e também
as funções que deverá desempenhar. Vamos começar com os
objetos

• Menu: representa o elemento visual que receberá interações


do usuário para seleção da opção.

• Produto: representa o produto propriamente dito.

100 U3 - Classes e objetos


• Estoque: representa o coletivo de produto, pois ações
como analisar os produtos em baixa, ou até localizar um
produto que precisa ser alterado, são ações relativas ao
estoque.

• Tela: representa a tela de nosso aplicativo, por mais que


exista um recurso na tecnologia chamado Console,
devemos compreender que podemos sim abstrair e assim
encapsular.

O processo de identificação desses objetos é baseado na


capacidade que temos de abstrair, como conseguimos observar os
elementos existentes no processo, e também, como conseguimos
nomeá-los. Devemos compreender que esse processo não é exato,
o que encontraremos são desenvolvedores que melhor conseguem
perceber e identificar os objetos importantes. Devemos olhar para
essa atividade como algo subjetivo, que dependerá do autor, mas
que necessariamente deverá ser capaz de ser explicado.

Vamos, na sequência, extrair as características de cada um dos


objetos identificados. As características são as informações que
cada objeto possui, isso dentro do contexto do problema. Vejamos
como isso poderia ser feito:

• Produto: código, descrição, valor unitário e quantidade em


estoque.

• Estoque: lista de produtos.

Deve-se perceber que não foi observada nenhuma característica


particular para os objetos tela e menu.

Ainda analisando os nossos objetos, vamos listar alguns possíveis


procedimentos que podem ser executados:

• Menu: solicitar opção.

• Produto: verifica estoque, alterar descrição, alterar valor


unitário e alterar quantidade em estoque.

• Estoque: adicionar produtos, alterar produto, listar produtos

U3 - Classes e objetos 101


que iniciem com um caractere e listar produtos com baixa
de estoque.

• Tela: limpar, solicitar uma letra, solicitar que pressione


qualquer tecla e mostrar mensagem.

O processo de identificação dos objetos e suas ações pode


variar de pessoa para pessoa, mas ainda assim devemos efetuar
essa separação conforme enxergamos as coisas no mundo real,
por mais que alguns objetos possam ser apenas conceituais, por
exemplo o menu.

Antes de avançarmos com a codificação, vamos mergulhar em


importantes recursos que serão usados na sequência.

Para saber mais


Para saber mais a respeito da abstração e como isso pode ajudá-lo
a compreender os elementos de uma aplicação, e principalmente
como eles se relacionam, faça a leitura do artigo “Abstração –
Programação Orientada a Objetos” fornecido pela Scriptcase. Para
isso utilize o link a seguir:
<http://scriptcaseblog.com.br/abstracao-programacao-
orientada-a-objetos/>.

Questão para reflexão


Abstração é algo natural ao ser humano?

Atividades de aprendizagem
1. Quando desejamos enviar uma mensagem para a saída padrão
(Monitor) a partir de uma aplicação do tipo “Console Application”,
podemos utilizar quais opções?
a) As ações Read e ReadLine da estrutura Console.
b) As ações Write e WriteLine da estrutura Screen.
c) As ações Write e WriteLine da estrutura Console.
d) Utilizando a função printf.
e) Utilizando o fluxo de saída cout.

102 U3 - Classes e objetos


2. Para representação de dados complexos podemos utilizar qual
recurso em C#?
a) Tipos primitivos.
b) Estrutura (Struct).
c) Acesso indireto de memória.
d) Array.
e) Função.

U3 - Classes e objetos 103


104 U3 - Classes e objetos
Seção 2

Base conceitual
Introdução à seção

Veremos, através desta seção, alguns conceitos-chave que já


foram expostos, mas que serão devidamente reapresentados em
um contexto de uma linguagem, e que podem ser reproduzidos
para melhor associação.

2.1 Classes

Como já foi repetido outras vezes, a classe descreve os objetos,


define como cada indivíduo deverá ser representado, por isso
associamos a uma forma, ainda podemos associar a classe como
uma planta de uma casa, sempre que respeitarmos a execução
daquela planta, então teremos indivíduos (casas) idênticas, isso
em um mundo perfeito e exato (PAGE-JONES, 2001). Vamos
observar a seguir como uma classe deve ser descrita utilizando C#.
Vejamos que existem regras de escrita, e se não as respeitarmos
possivelmente teremos sérias dificuldades.

class MinhaClasse

Fonte: elaborado pelo autor.

A partir dos limitadores { e } delimitamos o conteúdo de nossa


classe.

U3 - Classes e objetos 105


2.2 Atributos

Ao observarmos um objeto qualquer, conseguimos extrair


características dele, por exemplo, ao analisarmos uma TV,
naturalmente extraímos o seu tamanho, peso, marca, modelo, se é
inteligente, se suporta 3D e assim por diante. Vamos observar algo
bem diferente, por exemplo, de um pássaro extraímos facilmente
características como tipo de plumagem, sua família, cor, peso,
velocidade máxima de voo, tipo de caça e assim vai. Entenda que
ninguém aqui possivelmente é especialista em aves, mas são
características que entendemos que todo pássaro possui, lembre-se
da abstração. Vejamos algo mais da nossa realidade, vamos analisar
um tablet. Vale a sua ajuda para identificarmos as características que
todo tablet possui. Vejamos o que conseguimos identificar: marca,
modelo, cor, arquitetura, processador, quantidade de memória
RAM, capacidade de armazenamento, suporte a extensão por SD
Card, sistema operacional, versão e, por fim, tamanho. Perceba: isso
fazemos naturalmente, descrevendo as características de um objeto.
Em orientação a objetos definimos as características como atributos,
que tecnicamente serão variáveis com um determinado escopo, do
objeto. Diferentemente dos escopos associados às variáveis locais, ou
globais, os atributos também são chamados de variáveis da instância,
isso porque pertencem ao objeto (DEITEL, 2003).

class MinhaClasse

int atributo1;

int atrinuto2;

string atributo3, atributo4;

Fonte: elaborado pelo autor.

106 U3 - Classes e objetos


Assim como uma variável, todo atributo deve ter necessariamente
um tipo (MSDN, 2017). Novamente destacamos aqui a semelhança
com a declaração de uma variável e até a um tipo de dado
complexo, visto na Unidade 1. Atributos de um mesmo tipo de
dado podem ser declarados em uma mesma linha, separados
por vírgula, conforme se pode observar os atributos atributo3 e
atributo4, mas destacamos que a boa prática é que cada atributo
seja declarado em uma linha individual. Vamos exercitar aqui
declarando uma classe Tablet com todos as características que
conseguimos extrair (DEITEL, 2003).

class Tablet

string marca;

string modelo;

string cor;

string arquitetura;

string processador;

int memoriaRam;

int armazenamentoInterno;

int maximoExpansao;

string sistemaOperacional;

double tamanhoTela;

Fonte: elaborado pelo autor.

Perceba que a mesma simplicidade que temos para extrair os


atributos, também teremos para declará-los dentro da nossa nova
classe (PAGE-JONES, 2001). Entenda que alguma complexidade
poderá existir principalmente quando não conhecermos
em detalhes o objeto observado. Também haverá, caso não

U3 - Classes e objetos 107


conhecermos os tipos de dados existentes na linguagem, mas
em ambas as situações conseguimos contornar com uma certa
agilidade, o primeiro buscando mais detalhes com pessoas ou
documentos, correndo atrás das informações, já no segundo caso
basta buscarmos a lista de tipos de dados em alguma referência da
linguagem desejada.

Algumas tecnologias podem utilizar o termo variável da instância


para se referir aos atributos. Isso também pode ser aplicado à
linguagem C#, pois são equivalentes, e tecnicamente os atributos
são organizados como variáveis pertencentes a uma instância.

2.3 Objetos

Baseado na ideia apresentada há pouco, os objetos serão um


indivíduo extraído, ou produzido pela sua fábrica, a classe (MSDN,
2017). Lembre-se a classe foi associada a conceitos como forma,
também a uma planta de uma casa, já o objeto devemos associar
como ao bolo feito a partir da forma, ou então a casa construída a
partir de uma planta, em ambos os casos, sempre que executarmos
a produção em um ambiente controlado, sem variação de matérias-
primas e/ou tempo, chuva, sol, variação de temperatura, sempre
obteremos um indivíduo, ou podemos chamar de ocorrência,
sempre serão idênticos, isso como já observado em um ambiente
exato e controlado, o que é natural à computação.

Dentro da filosofia de desenvolvimento orientado a objetos,


nosso objetivo primário sempre será a escrita das classes, e então,
a utilização dos objetos para execução de uma tarefa. Isso será
melhor esclarecido na sequência com a resolução da aplicação de
controle de Estoque, mas como nosso objetivo aqui é apresentar
o conceito, vejamos a partir da classe Tablet como poderíamos
extrair um objeto, e então a sua utilização.

class Tablet

public string marca;

public string modelo;

108 U3 - Classes e objetos


public string cor;

public string arquitetura;

public string processador;

public int memoriaRam;

public int armazenamentoInterno;

public int maximoExpansao;

public string sistemaOperacional;

public double tamanhoTela;

class Program

static void Main()

Tablet tb1 = new Tablet();

tb1.cor = "Branco";

tb1.marca = "Samsung";

// Imagine algum algoritmo aqui

Fonte: elaborado pelo autor.

U3 - Classes e objetos 109


Primeiramente, é importante observar que, para funcionar esse
pedaço, alteramos a classe Tablet acrescentando antes de cada
atributo a palavra reservada public, ela será explicada na subseção
que explica os modificadores de visibilidade. Nosso objetivo será o
entendimento da primeira linha de código existente no bloco Main
do nosso programa. Vamos ao que realmente está acontecendo. Do
lado esquerdo do sinal de atribuição (igual) temos a declaração de
uma variável chamada tb1 do tipo Tablet, vale o destaque que, uma
classe a partir do momento que é declarada, podemos enxergá-
la como um tipo de dado complexo, isso porque possivelmente
vamos declarar variáveis do tipo da classe. Já do lado direito temos
o operador new, que será utilizado para criar o objeto da classe,
ainda considerando esse pedaço de instrução, haverá a execução
do método construtor, que servirá para inicializar o nosso objeto,
veremos na sequência uma subseção exclusiva para falarmos
sobre isso.

O fato a ser observado é que após a execução da primeira linha


de código, teremos um objeto criado e armazenado na memória
volátil, e através da variável tb1 poderemos manipulá-lo, alterando
neste indivíduo as suas características. Devemos compreender
que, assim como todo tablet tem cor, modelo, processador e
assim por diante, isso se aplica ao coletivo de tablets. Quando
avaliamos o indivíduo, um tablet, nesse caso ele terá também essas
características, mas elas estarão definidas, possuirão um valor.
Imagine você comprando um tablet, naturalmente ele terá todas
essas características definidas, exemplo: LG, Preto de 7 polegadas.
Veja que a diferença conceitual e técnica, entre a classe e do
objeto, está no fato que a classe declara o que o objeto terá, já o
objeto possui. Assim devemos compreender que, se a classe Tablet
define que todo tablet tem um atributo (característica) chamada
fabricante, então todo indivíduo dessa classe necessariamente terá
esse atributo definido (LG, Samsung, HP e outros). Esse valor estará
definido para cada instância de tablet.

Instância é um termo muito utilizado para designar um objeto


criado da classe, então podemos associar objeto ao termo
instância, sem qualquer prejuízo.

110 U3 - Classes e objetos


2.4 Métodos

As ações que um indivíduo poderá fazer, quer dizer, aquilo que


podemos fazer com o objeto, as funções que poderemos executar,
essas são chamadas de métodos no escopo da programação
orientada a objetos (PAGE-JONES, 2001). Devemos compreender
que métodos, ações ou comportamentos são sinônimos em
nosso meio, e além disso devemos compreender que métodos
se referem àquilo que podemos fazer com um objeto. É muito
comum que os métodos sejam nomeados utilizando um ou mais
verbos, pois expressam algo que pode ser feito com o objeto
(DEITEL, 2003).

Como exemplo, se avaliarmos aquilo que podemos fazer com


um tablet, identificamos ações como ligar, desligar, aumentar o
volume, diminuir o volume, reiniciar e até acessar a tela principal
(home). Essas ações são comuns à maioria dos tablets, podendo
haver exceções, mas como estamos avaliando os comportamentos
inerentes à maioria (coletivo), não devemos nos preocupar.

Tecnicamente, métodos são blocos de códigos que podem ser


executados a partir de um objeto.

Vejamos como poderíamos implementar um simples liga


ou desliga em nossa classe Tablet, mas com uma observação,
fisicamente não existem dois botões, então, nesse caso, não
existirão dois métodos, apenas um, e chamaremos de LigaDesliga.

A partir desse ponto, para simplificar o entendimento, vamos


remover vários atributos que foram levantados para a classe tablet,
caso esteja reproduzindo em um ambiente de testes, por favor
mantenha-os, mas para evidenciar aquilo que é importante, então
assim o faremos.

class Tablet

// Atributo(s) (Características)

string marca;

U3 - Classes e objetos 111


bool ligado;

// Método(s) (Comportamentos / Ações)

void LigaDesliga()

if (ligado)

ligado = false;

Console.WriteLine("Desligado");

else

ligado = true;

Console.WriteLine("Ligado");

Fonte: elaborado pelo autor.

Perceba que o método nada mais é que um bloco de código


nomeado.

Destacamos que, todos os métodos definidos na classe podem


acessar os atributos livremente, pois estão organizados dentro de
uma estrutura única (MSDN, 2017). Conceitualmente, fazem parte
de uma estrutura só, sendo assim, métodos tem livre acesso aos
atributos privados, desde que pertençam a mesma classe.

112 U3 - Classes e objetos


2.5 Modificadores de visibilidade

Uma das características que mais distingue as linguagens que


suportam orientação a objetos, daquelas que não suportam, está na
preocupação e segurança no acesso a recursos. Existem princípios
que nos levam a construir nossas classes, definindo restrições
de acesso a alguns recursos internos (PAGE-JONES, 2001). Uma
dessas características mais comuns para isso, será a utilização
de modificadores de acesso, eles definem para um determinado
recurso quem poderá acessá-lo. Vejamos a partir do exemplo da
classe Tablet o uso dos modificadores:

public class Tablet

// Atributo(s) (Características)

public string marca;

private bool ligado;

// Método(s) (Comportamentos / Ações)

public void LigaDesliga()

if (ligado)

ligado = false;

Console.WriteLine("Desligado");

else

ligado = true;

U3 - Classes e objetos 113


Console.WriteLine("Ligado");

Fonte: elaborado pelo autor.

O modificador public foi acrescentado para a declaração da


classe Tablet e para o atributo marca, também adicionamos ao
método LigaDesliga. Todo membro de uma classe declarado
como público, estará acessível para todos os outros recursos do
programa, sem restrições (MSDN, 2017). Vejamos o contraste com
o modificador private, este foi utilizado para o atributo ligado.
Nesse caso, o acesso a essa informação estará disponível apenas
pelo próprio objeto, não sendo permitido o acesso para mais
ninguém. Vejamos um pequeno pedaço de código que demonstra
a utilização dessa classe:

static void Main()

Tablet samsung = new Tablet();

samsung.marca = "Samsung";

samsung.lidado = true; // Erro na construção da aplicação

samsung.LigaDesliga();

Fonte: elaborado pelo autor.

114 U3 - Classes e objetos


Perceba que só a partir do objeto é que podemos demonstrar
a utilização da classe, conforme observamos anteriormente, caso
esse código seja reproduzido, será possível observar que um erro
será gerado na linha que tenta alterar o atributo ligado, isso porque
ele é privado. Essa é a real razão da existência dos modificadores
de acesso. Quando desejamos permitimos o acesso direto a
algum recurso, seja ele um atributo ou então um método, então
colocamos como public, caso contrário como private. Isso faz
bastante diferença quando avaliada a segurança e flexibilidade
do código. Perceba no código apresentado que não haveria a
justificativa para alterar o atributo ligado para true, isso porque o
próprio método LigaDesliga o faria, certo desse pensamento, o
uso do modificador impediu o acesso direto ao atributo, pois na
classe há um processo para isso (DEITEL, 2003).

Como analogia ao mundo real podemos comparar os


modificadores de visibilidade às funcionalidades de um veículo
automotor: o motorista tem total acesso aos instrumentos para
condução do veículo. Agora, imaginemos a existência de uma
função que altera o funcionamento do motor baseado no tipo
de combustível. Entenda que essa função não estará acessível ao
motorista: isso está baseado em um conceito chamado detalhes
de implementação. Quando utilizamos um recurso, seja físico
ou não, temos acesso a uma interface pública, e através dela
operamos o recurso. Já os detalhes de implementação, aquilo que
internamente existe para sustentar o funcionamento, não temos
quaisquer acessos. Ainda dentro desse processo de entender o
conceito, pensemos a respeito de um brinquedo para uma criança
de três anos: aquilo que está acessível aos dedos, ouvidos e olhos
da criança, podemos chamar de interface pública, já a parte interna
do brinquedo não, dentro dessa perspectiva devemos compreender
que os detalhes de implementação também expõem uma
característica de segurança. Imaginemos esse mesmo brinquedo
quebrado, a partir da exposição dos detalhes de implementação.
A segurança que antes era garantida considerando uma idade
mínima, a partir da exposição não existirá mais: a criança poderá
se ferir.

Devemos compreender os modificadores como recursos que


permitirão que a técnica seja melhor implementada, expondo

U3 - Classes e objetos 115


aquilo que deverá ser manipulável, e escondendo aquilo que é
pertinente ao objeto (MSDN, 2017).

Existem outros modificadores que serão explicados em outra


unidade, pois estão relacionados a técnicas ainda não discutidas.

2.6 Flexibilizando métodos

Existem ações que, para ocorrerem, precisam de parâmetros,


e ainda podem gerar um retorno. Vamos imaginar uma simples
compra, para que ela ocorra devemos fornecer uma forma
de pagamento, por exemplo, o dinheiro, já em contrapartida
recebemos a mercadoria desejada. Trazendo aqui para a nossa
realidade, devemos compreender que métodos podem ter
parâmetros, podendo variar de 0 a N, e podem gerar ou não um
retorno, variando de 0 a 1 (MSDN, 2017). Vejamos a nossa classe
Tablet com a adição de dois novos métodos, um com a passagem
de parâmetro, já o outro com o retorno:

public class Tablet

// Atributo(s) (Características)

private string marca;

private bool ligado;

// Método(s) (Comportamentos / Ações)

public void LigaDesliga() { ... }

// Retorna o estado do tablet com relação a ligado/


desligado

public bool EstaLigado()

116 U3 - Classes e objetos


return ligado;

// Altera a marca associada ao objeto em questão

public void AlterarMarca(string novaMarca)

marca = novaMarca;

public string RecuperaMarca()

return marca;

}
Fonte: elaborado pelo autor.

O método EstaLigado retorna um valor verdade indicando


o estado do tablet, relativo a Ligado/Desligado, já o método
AlterarMarca não retorna nada, mas exige que um parâmetro
chamado novaMarca seja informado, entenda que ao acionarmos
esse método conseguiremos alterar a marca do tablet. Essa marca
é uma característica controlada por um atributo, que antes era
público e agora se encontra privado. Isso se justifica porque antes
não tínhamos uma interface pública para permitir que ele fosse
alterado (MSDN, 2017). Agora sim, possuímos um método que
permite alterar e um outro que permite obter. O método para obter
a marca foi chamado de RecuperaMarca, ele utiliza o recurso de
retorno para externalizar a marca, vejamos como seria a utilização
dessa interface pública.

U3 - Classes e objetos 117


static void Main()

Tablet tb1 = new Tablet();

tb1.AlterarMarca("Samsung");

tb1.LigaDesliga();

if (samsung.EstaLigado())

Console.WriteLine("Tablet {0} ligado!", tb1.


RecuperaMarca());

else

Console.WriteLine("Tablet {0} desligado!", tb1.


RecuperaMarca());

Fonte: elaborado pelo autor.

Sobrecarga de métodos

Métodos são comportamentos inerentes a um coletivo (classe),


mas que são executados no contexto do indivíduo (objeto). Existem
comportamentos que podem ocorrer para um objeto, mas com
parâmetros diferentes (PAGE-JONES, 2001). Vejamos exemplo da
transação de vendas: sempre que desejamos adquirir algo, após
a devida seleção, efetuamos a transação, e para isso, passamos
o parâmetro dinheiro para o vendedor, e então obtemos como

118 U3 - Classes e objetos


retorno a mercadoria desejada. Mas existem variações para o mesmo
processo de transação de venda. Imagine a compra da mesma
mercadoria, mas o parâmetro de pagamento não será dinheiro, e
sim através de cartão. Vamos mais além, imagine que o parâmetro
de pagamento será dinheiro e uma mercadoria de troca, perceba
que para todos esses casos a mesma operação será executada, com
variação ou na quantidade ou no tipo dos parâmetros, hora dinheiro,
hora cartão, hora dinheiro e crédito de troca.

Ações podem ocorrer, mas a partir de parâmetros distintos.


Trazendo para o nosso exemplo, vamos fazer alguns ajustes para
ilustrar esse recurso, vejamos o resultado:

public class Tablet

// Atributo(s) (Características)

private string marca;

private bool ligado;

private string sistemaOperaciona;

private int versao;

public void Atualizar(int novaVersao)

versao = novaVersao;

public void Atualizar(string novoSO, int novaVersao)

sistemaOperaciona = novoSO;

U3 - Classes e objetos 119


versao = novaVersao;

Fonte: elaborado pelo autor.

Adicionamos dois novos atributos, sistemaOperacional e versao,


também adicionamos dois métodos com o nome “Atualizar”. Como
esses métodos possuem o mesmo nome, natural observarmos
que possuirá o mesmo sentido. Como o mesmo processo pode
ocorrer, mas com a quantidade de parâmetros variando, então
temos aqui uma sobrecarga de método: uma mesma ação
podendo ser executada a partir de parâmetros diferentes, no tipo
ou na quantidade.

2.7 Métodos construtores

Existe um tipo particular de método, chamado de método


construtor, isso porque são utilizados na etapa de construção
dos objetos. Devemos compreender que eles só poderão ser
acionados (invocados) no momento da criação do objeto, e a
responsabilidade de um método construtor é única: garantir
que o objeto seja devidamente inicializado, assegurando assim
a consistência do objeto (PAGE-JONES, 2001). Dentro dessa
máxima também utilizamos os métodos construtores para
personalizar os objetos no momento de sua criação. Para reforçar,
utilizamos os métodos construtores para inicializar os objetos, seja
no aspecto de consistência, também no aspecto de inicialização
personalizada (DEITEL, 2003). Como regra, os construtores devem
necessariamente ter o mesmo nome da classe, mas o seu retorno
deve ser omitido. Vejamos através do código a seguir, como
podemos utilizar o método construtor para garantir esses pontos
observados:

public class Tablet

120 U3 - Classes e objetos


// Atributo(s) (Características)

...

private string sistemaOperaciona;

private int versao;

public Tablet()

versao = 1;

...

Fonte: elaborado pelo autor.

No exemplo anterior utilizamos o método construtor para


garantir a consistência do objeto (MSDN, 2017). Imaginemos agora
a seguinte regra, todo o objeto tablet deverá ter a versão 1, exceto
se informado. Naturalmente, essa é uma regra criada aqui, para
exemplificarmos, mas entenda que o método construtor poderá ser
utilizado para garantir que os atributos do objeto sejam inicializados.
Isso para valores diferentes daqueles que consideramos padrões
(abordaremos isso na sequência). Agora vamos avaliar outra
possibilidade na utilização do método construtor, a inicialização
personalizada:

public class Tablet

// Atributo(s) (Características)

U3 - Classes e objetos 121


...

private string sistemaOperaciona;

private int versao;

public Tablet()

versao = 1;

public Tablet(string pSO, int pVersao)

sistemaOperaciona = pSO;

versao = pVersao;

...

class Program

static void Main()

Tablet tb1 = new Tablet("Android", 12);

...

}
Fonte:elaborado pelo autor.

122 U3 - Classes e objetos


Perceba primeiramente que, um novo construtor foi adicionado,
caracterizando uma sobrecarga, porque a quantidade de
parâmetros variou. Nesse segundo construtor existe a possibilidade
de informar tanto o sistema operacional quanto a versão. Por
isso chamamos de inicialização personalizada, pois permite que
um objeto receba os seus valores diretamente no momento da
extração (operador new).

2.8 Membros da classe

Desde o momento que apresentamos os conceitos de atributos


e métodos, sempre nos referimos a um recurso que pertence ao
objeto, mas que por padrão é declarado na classe. Isso corrobora
com os conceitos, lembre-se de que a classe define, mas é o objeto
que possui (MSDN, 2017). Então, dentro dessa linha de raciocínio
identificamos aquilo que pertence ao coletivo, declaramos no
coletivo, mas ao executar, executamos no contexto do objeto,
vejamos um pequeno pedaço de código que ilustrará isso:

class Program

static void Main()

Tablet tb1 = new Tablet();

tb1.Atualizar("Android", 12);

...

Fonte: elaborado pelo autor.

U3 - Classes e objetos 123


Vejamos que o método “Atualizar” é executado a partir de uma
instância da classe Tablet, essa por sua vez está associada a variável
tb1. Podemos olhar isso como um padrão, pois será responsável
por quase que a totalidade das vezes que adicionarmos um
membro a uma classe, seja ele um método, método construtor ou
mesmo um atributo.

Existem situações que essa regra não se aplica, expondo assim


uma limitação. Vejamos essa máxima: analisamos o coletivo
para identificarmos os comportamentos e características-padrão,
membros do coletivo, mas que por fim são executados no contexto
de um objeto. Existem situações envolvendo comportamentos e/
ou atributos que não pertencem ao objeto, mas ao coletivo. Nesse
caso, podemos utilizar o recurso de declaração de membros da
classe. Já o caso padrão, onde o membro pertence ao objeto,
chamamos comumente de membros do objeto. Em outras
palavras, membros do objeto devem ser executados no contexto
de um objeto, enquanto membros da classe pertencem ao coletivo
(nesse caso são executados no contexto de uma classe) (DEITEL,
2003). Vejamos o exemplo a seguinte, através do qual poderemos
perceber que a distinção entre um membro da classe para um
membro do objeto está na inclusão da palavra reservada static.

public class Tablet

// Atributo(s) (Características)

...

// Armazenará a última versão do tablet com SO Android

private static int AndroidUltimaVersao;

// Construtor da classe que inicializa (personaliza) os


atributo(s), esse construtor não será chamado diretamente, sendo
assim, não é permitido a exitência de mais de um construtor
estático

124 U3 - Classes e objetos


static Tablet()

AndroidUltimaVersao = 15;

// Método da classe que permite obter um tablet na última


versão

public static Tablet NovoAndroidUltimaVersao()

return new Tablet("Android", AndroidUltimaVersao);

// Permite incrementar a última versão do SO Android

public static void IncrementaAndroidUltimaVersao()

AndroidUltimaVersao++;

Fonte: elaborado pelo autor.

Como já observado, para utilizarmos um membro da classe


devemos fazê-lo no contexto da classe, diferenciando-se dos
membros do objeto. Sintaticamente, acessamos os membros
utilizando o padrão NomeDaClasse.MembroDaClasse(), isso para
métodos. Vejamos a utilização dos membros criados em nosso
último trecho de códigos:

static void Main()

U3 - Classes e objetos 125


{

Tablet samsungV15 = Tablet.NovoAndroidUltimaVersao();

...

Tablet.IncrementaAndroidUltimaVersao();

Tablet samsungV16 = Tablet.NovoAndroidUltimaVersao();

...

Fonte: elaborado pelo autor.

Quando dizemos “no contexto de um objeto” ou então “no


contexto de uma classe”, sintaticamente estamos nos referindo
ao que precede o membro acessado, como já visto, podendo
ser “meuObjeto.” ou “MinhaClasse.”. Esses são os contextos
apresentados.

Para saber mais


Para saber um pouco mais sobre a utilização de membros de
instância em C#, por favor consulte o material de referência
fornecido pela própria Microsoft. Para isso utilize o seguinte link:
<https://msdn.microsoft.com/pt-br/library/79b3xss3.aspx>.

Questão para reflexão


Quais são os atributos e métodos de um equipamento como o ar-
condicionado?

126 U3 - Classes e objetos


Atividades de aprendizagem
1. Os atributos definem:
a) As características de um objeto.
b) As ações que um objeto executa.
c) Os detalhes internos de implementação de uma classe.
d) Informações relativa ao gerenciamento de memória.
e) Elementos que necessariamente serão privados.

2. Quais são as vantagens que devem ser percebidas para a utilização de


modificadores de acesso?

U3 - Classes e objetos 127


128 U3 - Classes e objetos
Seção 3

Codificando orientado a objetos


Introdução à seção

Está na hora de criarmos algo que seja funcional e baseado


em toda a perspectiva da programação orientada a objetos.
Observaremos como é possível olharmos para um programa
computacional e não apenas enxergarmos o código, mas também
blocos que se encaixam.

3.1 Resolvendo a dinâmica

Voltando à dinâmica introduzida lá na Seção 1, vamos agora


trabalhar e aplicar os conceitos de orientação, associados a uma
prática utilizando a linguagem C#. No decorrer dessa resolução,
deve-se observar as alternativas utilizadas, bem como as técnicas
de encapsulamento. Consideramos aqui que a sintaxe deixa de ser
de menor relevância até porque não a conseguiremos desenvolver
com desenvoltura, isso quer dizer com alguma velocidade se não
nos adequarmos às regras de escrita.

Partiremos do mesmo ponto que na criação da nossa


aplicação, mas considerando a técnica de programação
estruturada. Vamos criar um projeto do tipo “Console Application”
chamado EstoqueOO. Após a criação vamos adicionar classes que
representam os coletivos envolvidos, e para isso utilize a menu
Project > Add Class, assim como a captura a seguir:

U3 - Classes e objetos 129


Figura 3.1 | Adicionando a classe produto

Fonte: elaborada pelo autor.

3.2 Classe produto

Essa classe representa a classe Produto, com seus atributos


privados, para garantir a segurança no armazenamento das
informações, pois podemos impedir, através de uma lógica
simples, a entrada de dados inválidos. Nos métodos Apresentacao1
e Apresentacao2 foi utilizado o recurso chamado interpolação de
Strings, que permite criar uma nova String com a inserção dos
valores de variáveis.

public class Produto {

private int _codigo;

private string _descricao;

private double _valorUnitario;

private double _qtdeEstoque;

public Produto(int codigo) {

_codigo = codigo;

130 U3 - Classes e objetos


}

public int RecuperaCodigo() {

return _codigo;

public string RecuperaDescricao() {

return _descricao;

public bool EstoqueBaixo() {

return _qtdeEstoque < 5;

public void AlterarDescricao(string descricao) {

_descricao = descricao;

public void AlterarValorUnitario(double valorUnitario) {

_valorUnitario = valorUnitario;

public void AlterarQtdeEstoque(double qtdeEstoque) {

_qtdeEstoque = qtdeEstoque;

U3 - Classes e objetos 131


}

public string Apresentacao1() {

return $"Mercadoria {_codigo} (Qtde: {_


qtdeEstoque})";

public string Apresentacao2() {

return $"Código: {_codigo}\nDescrição: {_


descricao}\nValor Unitário: {_valorUnitario}\nQtde Estoque: {_
qtdeEstoque})";

Fonte: elaborado pelo autor.

3.3 Classe tela

Para facilitar as interações com o usuário, criamos uma classe


que representa a tela, tanto para a perspectiva de saída de dados
(Write) quanto para a entrada de dados (Read). Aproveitamos para
criar métodos de escrita que encapsulem a lógica de conversão,
que melhora em muito o processo de desenvolvimento, até
porque deixamos de nos preocupar com detalhes, esse é um dos
conceitos centrais do encapsulamento.

public class Tela {

public void Limpar() {

Console.Clear();

132 U3 - Classes e objetos


public string RequisitarLetra() {

return Console.ReadLine().ToUpper();

public void EsperarTecla() {

Console.Write("Pressione qualquer tecla para


continuar.");

Console.ReadKey();

public void Show(string mensagem) {

Console.WriteLine(mensagem);

public string LerString(string mensagem) {

Console.Write(mensagem + ": ");

return Console.ReadLine();

public char LerChar(string mensagem) {

return LerString(mensagem).First();

public int LerInt(string mensagem) {

U3 - Classes e objetos 133


return Convert.ToInt32(LerString(mensagem));

public double LerDouble(string mensagem) {

return Convert.ToDouble(LerString(mensagem));

Fonte: elaborado pelo autor.

Classe Menu

Ainda com o objetivo de encapsular para melhorar a legibilidade,


criamos uma classe chamada Menu. Ela tem por objetivo apresentar
e retornar a opção selecionada pelo usuário. Veja como as classes
trabalham de forma colaborativa, naturalmente isso acontece
através dos objetos. No construtor da classe Menu foi estabelecido
a necessidade que seja informado o objeto Tela.

public class Menu {

private Tela _tela;

public Menu(Tela tela) {

_tela = tela;

public string SolicitarOpcao() {

do {

_tela.Show("OPÇÕES DISPONÍVEÍS
*******");

134 U3 - Classes e objetos


_tela.Show("[A]lterar Produto");

_tela.Show("[L]istar Produtos (Letra)");

_tela.Show("[E]stoque Baixo");

_tela.Show("[S]air");

var op = _tela.RequisitarLetra();

Console.Clear();

switch (op) {

case "A":

case "L":

case "E":

case "S":

return op;

default:

_ t e l a . S h o w ( " O p ç ã o
inválida!");

_tela.EsperarTecla();

break;

_tela.Limpar();

U3 - Classes e objetos 135


} while (true);

Fonte: elaborado pelo autor.

3.4 Classe estoque

Representando o estoque, essa classe tem o objetivo de


encapsular todas as lógicas relacionadas ao coletivo de produto.
Para atendermos à ideia de criarmos classes com objetivos bem
definidos, perceba que não é responsabilidade dessa classe interagir
com o usuário (Tela), sendo assim utilizamos um recurso chamado
lista genérica para conseguirmos externalizar os produtos que
iniciem com uma letra ou até mesmo que estejam com estoque
baixo. A utilização dessa lista tem o propósito de apoiar algoritmos
onde não conseguimos determinar o número de indivíduos que
serão inferidos, baseados em algum critério. Assim, optamos por
utilizar uma estrutura que permite de forma dinâmica adicionar um
novo elemento produto, após a identificação se o mesmo atende
ao critério de inferência.

public class Estoque {

private int _conta;

private Produto[] _itens;

public Estoque() {

_itens = new Produto[50];

public void Adicionar(Produto novo) {

_itens[_conta++] = novo;

136 U3 - Classes e objetos


}

public Produto Localiza(int codigo) {

foreach (var produto in _itens) {

if (produto.RecuperaCodigo() == codigo) {

return produto;

return null;

public Produto[] Filtrar(char letra) {

List<Produto> temp = new List<Produto>();

foreach (var produto in _itens) {

if (produto.RecuperaDescricao()[0] ==
letra) {

temp.Add(produto);

return temp.ToArray();

U3 - Classes e objetos 137


public Produto[] EstoqueBaixo() {

List<Produto> temp = new List<Produto>();

foreach (var produto in _itens) {

if (produto.EstoqueBaixo()) {

temp.Add(produto);

return temp.ToArray();

Fonte: elaborado pelo autor.

3.5 Classe program

Seguindo uma linha parecida com aquela que desenvolvemos


no projeto estruturado, respeitando até os nomes dos métodos
antes utilizados. Apenas para simplificar encapsulamos a leitura de
uma mercadoria em um sobrecarga do método ExecutaLeitura.
Veja que o segundo parâmetro variou no seu tipo.

class Program {

static void ExecutaLeitura(Tela tela, Produto produto) {

produto.AlterarDescricao(tela.
LerString("Descrição"));

produto.AlterarValorUnitario(tela.LerDouble("Valor
Unitário"));

produto.AlterarQtdeEstoque(tela.LerDouble("Qtde
Estoque"));

138 U3 - Classes e objetos


}

static void ExecutaLeitura(Tela tela, Estoque estoque) {

for (int i = 0; i < 50; i++) {

tela.Show("Obtendo Estoque...");

tela.Show("Produto N: " + (i + 1));

Produto produto = new Produto(tela.


LerInt("Código"));

ExecutaLeitura(tela, produto);

estoque.Adicionar(produto);

tela.Limpar();

static void Alterar(Tela tela, Estoque estoque) {

tela.Show("Alterando produto...");

int codigo = tela.LerInt("Código");

Produto p = estoque.Localiza(codigo);

U3 - Classes e objetos 139


if (p != null) {

ExecutaLeitura(tela, p);

else {

tela.Show("Nenhum produto
encontrado.");

tela.EsperarTecla();

static void ListarProdutos(Tela tela, Estoque estoque) {

tela.Show("Listando produtos...");

char letra = tela.LerChar("Letra");

Produto[] localizados = estoque.Filtrar(letra);

if (localizados.Length != 0) {

foreach (var produto in localizados) {

t e l a . S h o w ( p r o d u t o .
Apresentacao2());

else {

tela.Show("Nenhum produto

140 U3 - Classes e objetos


encontrado.");

tela.EsperarTecla();

static void ListarEstoqueBaixo(Tela tela, Estoque estoque) {

tela.Show("Analisando estoque...");

Produto[] localizados = estoque.EstoqueBaixo();

if (localizados.Length != 0) {

foreach (var produto in localizados) {

t e l a . S h o w ( p r o d u t o .
Apresentacao1());

else {

tela.Show("Nenhum produto está com


estoque baixo no momento.");

tela.EsperarTecla();

U3 - Classes e objetos 141


static void Main(string[] args) {

Tela tela = new Tela();

Estoque estoque = new Estoque();

Menu menu = new Menu(tela);

ExecutaLeitura(tela, estoque);

bool sair = false;

do {

string opcao = menu.SolicitarOpcao();

switch (opcao) {

case "A":

Alterar(tela, estoque);

break;

case "L":

L i s t a r P r o d u t o s ( t e l a ,
estoque);

break;

case "E":

ListarEstoqueBaixo(tela,
estoque);

break;

case "S":

sair = true;

break;

142 U3 - Classes e objetos


}

tela.Limpar();

} while (!sair);

Fonte: elaborado pelo autor.

Assim concluímos a nossa aplicação, utilizando elementos que


se relacionam, e principalmente se ajudam. Assumimos aqui a ideia
de delegar responsabilidade a objetos que conseguiram relevância,
visualizados inicialmente através do projeto estruturado, e limitados
através da técnica de programação orientada a objetos.

Para saber mais


A orientação a objetos é um paradigma que precisa ser entendido
e dominado. Como sugestão, utilize a apostila “C# e Orientação
a Objetos” fornecido pela Caelum. Para acessar utilize o seguinte
link: <https://www.caelum.com.br/apostila-csharp-orientacao-
objetos/>.

Questão para reflexão


Será que a organização do código pode atrapalhar em algum
momento?

Atividades de aprendizagem
1. A identificação das classes, atributos e métodos está principalmente
relacionada a qual fator?
a) Características da linguagem.
b) Arquitetura da tecnologia.
c) Preceitos da técnica utilizada.
d) Capacidade de abstração.
e) Complexidade do algoritmo.

U3 - Classes e objetos 143


2. Segundo Deitel (2003, p. 16), “humanos aprendem sobre os objetos
existentes estudando seus atributos e observando seus comportamentos”.
O autor também afirmou que diferentes objetos podem ter atributos
semelhantes e podem exibir comportamentos semelhantes. Concluindo
com o pensamento de que é possível fazer comparações entre bebês e
adultos, e entre humanos e chimpanzés.
Identifique as características e comportamentos semelhantes entre uma
criança e um adulto.

Fique ligado
Nesta unidade, estudamos:
• Conceitos de orientação a objetos.
• Classe.
• Atributos.
• Objetos.
• Métodos.
• Modificadores de visibilidade.
• Parâmetros em métodos.
• Sobrecarga de métodos.
• Métodos construtores.
• Membros da classe.
• Desenvolvimento estruturado na prática.
• Desenvolvimento orientado a objetos na prática.

Para concluir o estudo da unidade


Caro aluno, finalizamos mais uma unidade!
Conceitos, técnicas e práticas relacionados ao paradigma
orientado a objetos foram apresentados. Também partimos de
uma problematização, que inicialmente foi resolvida utilizando a
técnica de programação estruturada, e fechamos com a resolução
do mesmo problema, mas com uma abordagem orientada a
objetos. Assim, consolidamos vários importantes conceitos, e na
sequência conceitos mais robustos serão apresentados.

144 U3 - Classes e objetos


Atividades de aprendizagem da unidade
1. As classes em C# facilitam a criação de tipos de dados abstratos
(TDAs), os quais ocultam sua implementação dos clientes (ou usuários
do objeto de classe). Um problema das linguagens de programação
procedimentais é que o código cliente frequentemente e dependente
dos detalhes da implementação dos dados usados no código (DEITEL,
2003, p. 243).
Avalie as seguintes assertivas relacionadas ao conceito de ocultação:
I. Para uma classe, sua interface pública corresponde àquilo que é
acessível aos clientes externos à classe.
II. A interface privada refere-se aos detalhes de implementação de uma
classe.
III. Utilizamos os modificadores de visibilidade para determinar o que
pertence à interface pública ou privada.
IV. Para restringirmos o acesso a um membro de uma classe utilizamos a
palavra reservada restrict.
V. Para deixarmos acessível um membro utilizamos a palavra reservada
public.
Assinale a alternativa CORRETA:
a) I, II, III e V são verdadeiras.
b) I, III, IV e V são verdadeiras.
c) I, II e IV são verdadeiras.
d) I, II e III são verdadeiras.
e) I, II, III e IV são verdadeiras.

2. Os programadores de C se concentram na escrita de funções. Eles


agrupam em uma função ações que executam alguma tarefa e, então,
agrupam as funções para formar um programa. Certamente, os dados
são importantes em C, mas eles existem principalmente para suportar
as ações que as funções executam. Os verbos, em um documento de
requisitos de sistema descrevendo os requisitos de um novo aplicativo,
ajudam o programador de C a determinar o conjunto de funções que
trabalharão juntas para implementar o sistema (DEITEL, 2003, p. 242).
A forma com que o desenvolvedor da linguagem em C visualiza os dados
está relacionada a qual fator:
a) Paradigma.
b) Conhecimento.
c) Obsolescência.
d) Imaturidade.
e) Despreparo.

U3 - Classes e objetos 145


3. Os programadores de C# se concentram na criação de seus próprios
tipos definidos pelo usuário, chamados classes. Também nos referimos
às classes como tipos definidos pelo programador. Cada classe contém
dados e um conjunto de métodos que manipulam os dados (DEITEL,
2003, p. 243).
Partindo da máxima que os desenvolvedores definem as classes com
seus atributos e métodos, considerando a classe Carro, relacione as
colunas Tipo com Membro:

Tipo Membro
A - Método Construtor A – Acelerar
B – Método da Instância B – GerarNumeroChassi
C – Variável da Instância C – Cor
D - Método da Classe D – Carro

a) A – D; B – A; C – C; D – B.
b) A – B; B – A; C – C; D – D.
c) A – D; B – A; C – D; D – C.
d) A – D; B – C; C – A; D – B.
e) A – A; B – D; C – C; D – B.

4. Cada membro em uma classe tem uma proposta e uma função, o


conhecimento sobre o conceito e a prática relativa a cada recurso
permitirá o uso maximizado dos recursos relacionados à técnica.
Baseado na função de cada um dos membros apresentados a seguir,
relacione as colunas Tipo e Definição:

Tipo Membro
A – Método Construtor A – Define um comportamento relacionado ao
indivíduo.
B – Método da Instância B – Define um comportamento relacionado ao
coletivo.
C – Variável da Instância C – Define uma característica do indivíduo.
D – Método da Classe D – Permite inicializar uma instância no momento
da sua criação.

a) A – D; B – A; C – C; D – B.
b) A – B; B – A; C – C; D – D.
c) A – D; B – A; C – D; D – C.
d) A – D; B – C; C – A; D – B.
e) A – A; B – D; C – C; D – B.

146 U3 - Classes e objetos


5. Uma mesma ação pode ocorrer a partir de parâmetros diferentes,
como exemplo podemos citar o atendimento de um banco por telefone,
onde para iniciarmos o processo de atendimento podemos informar o
número de um cartão de crédito ou então o nosso CPF, em ambos os
casos o sistema conseguirá diferenciar e iniciar o teleatendimento.
Essa característica também foi pensada na programação orientada
a objetos, com total suportem em C#. Assinale a alternativa que
corresponde ao conceito técnico que apresenta esse suporte:
a) Construtor.
b) Parâmetros.
c) Abstração.
d) Sobrecarga de métodos.
e) Métodos estáticos.

U3 - Classes e objetos 147


148 U3 - Classes e objetos
Referências
DEITEL, Harvery M. C#: como programar. São Paulo: Pearson Education, 2003.
PAGE-JONES, M. Fundamentos do desenho orientado a objeto com UML. São Paulo:
MAKRON Books, 2001.
ASCENCIO, Ana F. G., CAMPOS, Edilene A. V. Fundamentos da programação de
computadores: Algoritmos, Pascal, C/C++ e Java. 2. ed. São Paulo: Pearson, 2007.
MSDN. Classes (Guia de Programação em C#). (2017). Disponível em: <https://msdn.
microsoft.com/pt-br/library/x9afc042.aspx>. Acesso em: 25 mar. 2017.

U3 - Classes e objetos 149


Unidade 4

Herança e polimorfismo

Roque Maitino Neto

Objetivos de aprendizagem
Esta unidade tem como objetivo a apresentação dos
recursos de herança e polimorfismo, próprios do paradigma
de orientação a objetos. O estudo destes temas possibilitará
a criação de programas reutilizáveis e apropriados para
aumentarem de escala.

Seção 1 | Herança

Nesta seção será apresentado o conceito de reaproveitamento de


classes pelo uso de herança e delegação, duas das características mais
úteis e interessantes da programação orientada a objetos.

Seção 2 | Polimorfismo
Nesta seção serão abordados os conceitos básicos de polimorfismo e
os benefícios que ele poderá trazer ao código. Será apresentado exemplo
de um método que assumirá diversas formas, dependendo da necessidade
da aplicação. Por fim, será abordada a palavra reservada super, como meio
de acesso a métodos de uma superclasse.
Introdução à unidade
Caro aluno, seja bem-vindo à quarta unidade do nosso livro
didático!

Desde que você começou a estudar o paradigma da orientação


a objetos é muito provável que tenha procurado – e encontrado
– semelhanças e relacionamentos diretos entre a programação
procedural e a programação OO. Existem bons motivos
para acreditarmos que você tenha associado métodos com
procedimentos (ou funções), classes com tipos definidos pelo
usuário e atributos com variáveis, por exemplo. Afinal, nada mais
comum e saudável do que procurar por referências já conhecidas
quando se aprende algo novo.

Há, no entanto, duas características próprias da programação


orientada a objetos que não encontram relacionamento no mundo
procedural: a herança e o polimorfismo. Implicitamente, estas
duas características entregam ao desenvolvedor a capacidade
de reaproveitar classes de modo elegante e eficiente e tornam a
programação orientada a objetos tão interessante.

Esta unidade está dividida em duas seções: a primeira trata


especificamente do reaproveitamento de classes por meio da
herança e da delegação. Usando definições objetivas e exemplos
simples, essas duas ferramentas da orientação a objetos são
abordadas de modo a conferir a você o conhecimento necessário
para usá-las em favor de um código escalável, bem legível e
reutilizável.

A segunda seção aborda o polimorfismo, colocando-o na


condição de recurso que também permitirá a extensão de
características de classes, mas através da referência feita de várias
formas a um objeto.

Boa leitura!

U4 - Herança e polimorfismo 153


154 U4 - Herança e polimorfismo
Seção 1
Herança
Introdução à seção

A herança é uma das características mais interessantes da


orientação a objetos e seu efeito imediato é tornar possível a
reutilização de classes. Por meio de procedimento bastante
simples será possível, por exemplo, criarmos uma nova classe que
estende outra já definida pelo programador e assim aproveitarmos
todo o potencial e utilidade do conceito de herança. Esta seção
começa pela abordagem do conceito de herança, passa pela
análise de alguns exemplos de implementação desta característica
na linguagem Java e termina com o estudo de delegação,
particularidade da orientação a objetos que compõe o conceito
de reutilização de classes.

1.1 Conceito de herança


Herança é uma característica do paradigma de orientação a
objetos por meio da qual um objeto filho herda características
e comportamentos do objeto mãe. É pela implementação da
herança que conseguimos estabelecer relação entre uma classe
mãe e uma classe filha, de modo que a segunda se torne uma
extensão da primeira, herdando diretamente os métodos e os
atributos públicos da classe mãe.

Deitel (2005) ensina que herança é uma forma de reutilização de


software na qual uma nova classe é criada, absorvendo membros
de uma classe existente e aprimorada com capacidades novas ou
modificadas.

Na segunda unidade do livro, quando foi introduzido o conceito


de herança, foi feita menção à relação entre Funcionário (classe
pai) e Gerente (classe filha). Na condição de classe mais abaixo
na hierarquia, Gerente foi descrita como uma especialização de
Funcionário, já que pode herdar as características gerais daquela e
com suas próprias características específicas.
U4 - Herança e polimorfismo 155
Usando nomenclatura mais usual, teremos que a classe Funcionário
é uma superclasse de Gerente e Gerente é uma subclasse de
Funcionário. É interessante destacar que herança é sempre utilizada
em Java, mesmo que não explicitamente. Quando uma classe é criada
e não há referência alguma à sua superclasse, implicitamente a classe
criada é derivada diretamente da classe Object (RICARTE, 2000).

Alguns exemplos de herança podem ser observados no


Quadro 4.1. Note que as superclasses tendem a ser mais
gerais e as subclasses mais específicas. Com efeito, as classes
AlunoDeGraduacao e AlunoDePosGraduacao, ambas mostradas
no Quadro 4.1, herdam características da classe Aluno.

Quadro 4.1 | Exemplos de herança


Superclasse Subclasses
Aluno AlunoDeGraduacao, AlunoDePosGraduacao
Forma Circulo, Retangulo, Triangulo
Financiamento FinanciamentoDeCarro, FinanciamentoDeCasa
Empregado CorpoDocente, Funcionario
ContaBancaria ContaCorrente, ContaPoupanca
Fonte: Deitel (2005, p. 302).

O conceito não parece ser complicado. Nosso próximo desafio


é implementá-lo na linguagem Java. Adiante!

1.2 Implementação em Java


Seu conhecimento sobre classes e objetos adquirido na seção
passada será decisivo na implementação de herança em Java.
Tomaremos como primeiro exemplo uma classe chamada Ponto.
Em seu estado mais simples, ela serve para representar pontos em
um plano bidimensional (ARNOLD; GOSLING; HOLMES, 2007).

public class Ponto {

public double x, y;

public void inicializa() {

Ponto inferiorEsquerdo = new Ponto();

Ponto superiorDireito = new Ponto();


156 U4 - Herança e polimorfismo
Ponto pontoCentral = new Ponto();

inferiorEsquerdo.x = 0.0;

inferiorEsquerdo.y = 0.0;

superiorDireito.x = 1280.0;

superiorDireito.y = 1024.0;

pontoCentral.x = 640.0;

pontoCentral.y = 512.0;

Fonte: Adaptado de: Arnold, Gosling e Holmes (2007, p. 39).

Cada objeto Ponto é único e possui sua própria cópia de x e y,


que são inicializados em três regiões diferentes do plano.

Pois bem, imagine agora que nossa necessidade seja mostrar


um pixel em tela ao invés de um ponto no plano. Além das
coordenadas x e y, este pixel requer mais um dado: a cor. É evidente
o estreito relacionamento que existe entre a tela e o plano e o
pixel e o ponto, de modo que podemos aproveitar características e
comportamentos da classe ponto na classe pixel. Vejamos como:

public class Pixel extends Ponto {

Cor cor;

public void limpa() {

super.limpa();

cor = null;

}
Fonte: Adaptado de: Arnold; Gosling; Holmes (2007, p. 49).

U4 - Herança e polimorfismo 157


Repare na palavra reservada extends, colocada na linha de
descrição da classe Ponto. Ela torna a classe Pixel uma extensão da
classe Ponto e permite que seus dados e comportamentos sejam
usados na primeira. Os demais elementos da classe Pixel, incluindo
a palavra reservada super serão tratados adiante.

Nosso segundo exemplo estabelece relação entre as classes


funcionário e gerente, conforme introduzido no começo desta
seção. O código que segue foi retirado de Caelum – Apostila Java
e Orientação a Objetos.

public class Funcionario {

String nome;

String cpf;

double salário;

// Os métodos devem vir aqui.

}
Fonte: <https://www.caelum.com.br/apostila-java-orientacao-objetos/heranca-reescrita-e-
polimorfismo/#repetindo-cdigo>. Acesso em: 28 fev. 2017.

A classe Funcionario contém campos que são comuns a


qualquer tipo ou variação de funcionário que se possa encontrar
em uma organização. Estes campos são nome, cpf e salário. Sem
a aplicação da herança, teríamos uma classe gerente como a que
segue:

public class Gerente {

String nome;

String cpf;

double salario;

int senha;

int numeroDeFuncionariosGerenciados;

158 U4 - Herança e polimorfismo


public boolean autentica(int senha) {

if (this.senha == senha) {

System.out.println("Acesso Permitido!");

return true;

} else {

System.out.println("Acesso Negado!");

return false;

// outros métodos

Fonte: <https://www.caelum.com.br/apostila-java-orientacao-objetos/heranca-reescrita-e-
polimorfismo/#repetindo-cdigo>. Acesso em: 28 fev. 2017.

Questão para reflexão


Repare que os campos nome, cpf e salário tiveram que ser repetidos
na classe Gerente. Imagine se tivéssemos que repetir boa parte do
código a cada criação de tipos de funcionários com uma pequena
variação em relação à classe funcionário original.

Caelum (2011) completa: se um dia precisarmos adicionar uma


nova informação para todos os funcionários, precisaremos passar
por todas as classes de funcionário e adicionar esse atributo.

A solução natural para este caso é a aplicação da herança.


Observe a classe Gerente, reescrita para estender a classe
Funcionário.

public class Gerente extends Funcionario {

int senha;

U4 - Herança e polimorfismo 159


int numeroDeFuncionariosGerenciados;

public boolean autentica(int senha) {

if (this.senha == senha) {

System.out.println("Acesso Permitido!");

return true;

} else {

System.out.println("Acesso Negado!");

return false;

Fonte: <https://www.caelum.com.br/apostila-java-orientacao-objetos/heranca-reescrita-e-
polimorfismo/#repetindo-cdigo>. Acesso em: 28 fev. 2017.

Apenas os campos senha e numeroDeFuncionariosGerenciados


foram acrescentados, já que fazem parte do contexto da classe
Gerente.

Para saber mais


Acesse: <https://www.youtube.com/watch?v=MOXLCjL4Ik4>.
Acesso em: 27 fev. 2017. Em pouco menos de 17 minutos a relação
de herança entre as classes professor e aluno é minuciosamente
exemplificada.

Fica claro, então, que a utilização da herança permite que


classes mais genéricas sejam reaproveitadas para compor classes
mais específicas, o que confere segurança e racionalidade ao
processo de desenvolvimento de aplicações.

160 U4 - Herança e polimorfismo


Antes de terminarmos nosso estudo sobre herança, vale a pena
levantarmos mais uma questão para reflexão:

Questão para reflexão


Como os detalhes da implementação da classe mais genérica
são expostos na classe que herda suas características, é legítimo
afirmar que o encapsulamento pode ser violado com a utilização
da herança?

A herança, no entanto, não é a única forma de reaproveitarmos


classes. Outra maneira prevê a inclusão de uma instância da classe
mãe como um dos campos da classe filha. Sigamos adiante.

1.3 Delegação ou composição


De acordo com Santos (2003), uma das características mais
interessantes das linguagens de programação orientadas a
objetos é a capacidade de facilitar a reutilização de código, ou
seja, o aproveitamento de classes e seus métodos que já estejam
escritos e que já tenham o seu funcionamento testado e aprovado.
Como você lembra, foi exatamente a reutilização do código que
caracterizou e tornou o uso da herança tão atraente.

A delegação (ou composição) será abordada como a segunda


característica do paradigma de Orientação a Objetos que nos
permite reutilizar classes. Santos (2003) nos ensina que podemos
criar novas classes que estendem outra classe base se incluirmos
uma instância da classe base como um dos campos da nova
classe, que será então composta de campos específicos e de uma
instância de uma classe base.

Para ilustrar este conceito tomaremos como exemplo a classe


DataHora, adaptada de Santos (2003). Devemos considerar a
existência da classe Data – que representa uma data com dia,
mês e ano – e da classe Hora – que representa um horário com
hora, minuto e segundo. As duas classes contêm métodos que
inicializam e verificam a validade de seus campos e imprimem os
valores de seus campos no formato correto.

U4 - Herança e polimorfismo 161


A exibição simultânea de uma data e de uma hora se dá pela
classe DataHora, codificada na sequência.

public class DataHora {

private Data estaData;

private Hora estaHora;

DataHora (byte hora, byte minuto, byte segundo, byte dia,


byte mês, short ano) {

estaData = new Data(dia, mês, ano);

estaHora = new Hora (hora, minuto, segundo);

DataHora (byte dia, byte mês, short ano) {

estaData = new Data(dia, mês, ano);

estaHora = new Hora(byte) 0, (byte) 0, (byte) 0);

public String toString() {

return estaData + " "+estaHora;

Fonte: Santos (2003, p. 114).

Nesta classe, a data e a hora são representadas por instâncias


das respectivas classes que estão inseridas na classe DataHora.
Todo processamento entre DataHora e as classes Data e Hora
será feito pela chamada de métodos. Assim, fica demonstrada a
reutilização de classes por meio da Delegação.

162 U4 - Herança e polimorfismo


Note que os únicos campos na classe DataHora são instância
da classe Data e instância da classe Hora e todas as informações
a serem representadas por DataHora estão nessas instâncias. Se
fosse necessário, outros campos poderiam também ser declarados.

Atividades de aprendizagem
1. "A extensão de classes pode ser usada para diversos objetivos. É mais
comumente usada para especialização – em que a classe estendida
define novos comportamentos e assim se torna uma versão especializada
da superclasse. A extensão de classe pode envolver apenas a alteração
de um método herdado, talvez para torná-lo mais eficiente" (ARNOLD;
GOSLING; HOLMES, 2007, p. 92).
Assinale a alternativa que contém a sentença que melhor define herança
em nosso contexto.
a) Reutilização de código por meio da supressão de métodos e atributos
repetidos.
b) Passagem automática de atributos para classes mãe.
c) Extensão de métodos e atributos de uma classe.
d) Reutilização de código por meio de classes abstratas.
e) Representação de modelos com os mesmos métodos e atributos.

2. "Como você representaria um gato doméstico e um tigre em uma


estrutura de herança? Um gato doméstico seria a versão especializada de
um tigre? Qual seria a subclasse e quem seria a superclasse? Ou os dois
são subclasses de alguma outra classe?" (SIERRA; BATES, 2005, p. 125).
Considerando as características e comportamentos conhecidos de
um cirurgião e de um clínico geral, assinale a alternativa que contém a
estrutura de hierarquia apropriada para o caso.
a) A classe Clínico Geral deve ser a superclasse e Cirurgião deve ser a
subclasse.
b) A classe Clínico Geral deve ser a subclasse e Cirurgião deve ser a
superclasse.
c) As classes Clínico geral e Cirurgião não guardam semelhanças que
permitam sua estruturação hierárquica.
d) A classe Clínico Geral deve ser considerada a classe genérica e
Cirurgião a classe específica.
e) As classes Cirurgião e Clínico Geral devem ser consideradas subclasses
da classe Médico.

U4 - Herança e polimorfismo 163


164 U4 - Herança e polimorfismo
Seção 2
Polimorfismo
Introdução à seção

Para começarmos a construir nosso conhecimento sobre


polimorfismo em programação orientada a objetos é necessário
que antes relembremos o significado da expressão. A palavra
polimorfismo é composta por dois termos: Poli significa muitos ou
diversidade de. Morfismo remete a formas. Assim, ao dominarmos
o recurso do polimorfismo em Java teremos a possibilidade de
usar um objeto de forma demandada pela aplicação.

2.1 Utilização do polimorfismo


Pelo estudo da Seção 1 compreendemos Herança como a
transmissão de dados e operações de uma classe genérica para
uma classe específica. O que não foi dito é que a criação de classes
derivadas estabelece uma relação é-um-tipo-de. Por exemplo, um
Funcionário é-um-tipo-de Pessoa e um Aluno De Pós-Graduação
é-um-tipo-de Aluno.

É justamente a relação é-um-tipo-de que permite a existência


do polimorfismo.

O polimorfismo permite a manipulação de instâncias de


classes que herdam de uma mesma classe ancestral de
forma unificada: podemos receber métodos que recebam
instâncias de uma classe C e os mesmos métodos serão
capazes de processar instâncias de qualquer classe que
herde da classe C, já que qualquer classe que herde de C
é-um-tipo-de C. (SANTOS, 2003, p. 140)

Pelo estudo da Seção 1 compreendemos Herança como a


transmissão de dados e operações de uma classe genérica para
uma classe específica. O que não foi dito é que a criação de classes
derivadas estabelece uma relação é-um-tipo-de. Por exemplo, um

U4 - Herança e polimorfismo 165


Funcionário é-um-tipo-de Pessoa e um Aluno De Pós-Graduação
é-um-tipo-de Aluno.

É justamente a relação é-um-tipo-de que permite a existência


do polimorfismo.

Caelum (2011) assevera que polimorfismo é a capacidade de


um objeto poder ser referenciado de várias formas, o que não quer
dizer que o objeto fica se transformando. Ao contrário disso, um
objeto nasce de um tipo e morre daquele tipo. O que pode mudar
é a maneira como nos referimos a ele.

Tomemos este trecho de código adaptado de Santos (2003)


como exemplo:

public class ConcessionariaDeAutomoveis {

public static void main (String[] args) {

Automovel a1 = new Automovel ("Fiat", "Bege",


Automovel.MOVIDOAALCOOL);

AutomovelBasico a2 = new AutomovelBasico


("Corsa", "Cinza", Automovel.MOVIDOAGASOLINA);

AutomovelBasico a3 = new AutomovelBasico


("Gol", "Branco", Automovel.MOVIDOAGASOLINA, false, false, true);

AutomovelDeLuxo a4 = new AutomovelDeLuxo


("Citroen", "Preto", Automovel.MOVIDOAGASOLINA);

AutomovelDeLuxo a5 = new AutomovelDeLuxo


("Toyota", "Prata", Automovel.MOVIDOAGASOLINA, true, true, false,



true, true, true);

imprime (a1);

imprime (a2);

imprime (a3);

166 U4 - Herança e polimorfismo


imprime (a4);

imprime (a5);

public static void imprime (Automovel a){

System.out.println ("Seguem os dados do


automóvel escolhido:");

System.out.print (a);

System.out.println("Valor: "+a.quantoCusta());

System.out.println(a.quantasPrestações() +"
prestações de "+ (a.quantoCusta() / a.quantasPrestações()));

Imagine que nossa concessionária de automóveis informe


o preço e o número de prestações de seus carros por meio
de impressão dos dados em tela. Imagine também que a
concessionária é bem pequena e que tem à venda apenas cinco
automóveis. O método main – aquele que permite a execução da
classe – contém declaração de instâncias da classe Automovel,
AutomovelBasico e AutomovelDeLuxo e as usa para mostrar o
valor da prestação de cada automóvel.

O método imprime e mostra os dados de uma instância de


qualquer classe filha de Automovel e serão chamados os métodos
quantoCusta, quantasPrestações e toString serão chamados para
compor a informação no momento da impressão. Está, assim,
demonstrada a capacidade da programação orientada a objetos
de implementar métodos que podem assumir várias formas. Está
demonstrado o polimorfismo.

U4 - Herança e polimorfismo 167


Questão para reflexão
Com a implementação da herança, todos os membros da superclasse
serão acessíveis obrigatoriamente na classe derivada? Ou o uso de
modificadores de acesso em atributos e métodos poderá barrar o
acesso a eles via classe filha?

2.2 Acesso a métodos das superclasses


Já sabemos que a extensão de classes com os recursos que
o Java nos oferece é real e bastante útil. Podemos, no entanto,
aumentar ainda mais a reutilização do código. Por meio do
uso da palavra reservada super é possível acessar métodos da
classe ancestral (classe pai) que podem realizar parte ou todo o
processamento necessário na nova classe. De acordo com Santos
(2003), existem dois meios de se reutilizar métodos de classes
ancestrais que não tenham sido declarados como privados:

a) No primeiro meio, se a execução do método for a mesma


para a classe pai e a classe filha, então as instâncias da classe
filha podem chamar diretamente o método como se fosse delas
próprias.

b) A segunda maneira de executar da classe ancestral leva


em consideração que não existe na classe pai um método que
execute exatamente o que o programador deseja na classe filha. No
entanto, existem métodos que executam parte da tarefa, deixando
para comandos inseridos pelo programador a execução da parte
não executada do processamento desejado. Este fenômeno
ocorre em construtores, cuja função em muitos casos é inicializar
os campos das classes.

Vale ressaltar que criar um método na classe filha com a


mesma assinatura do método existente na classe pai equivale a
sobrescrever o método da classe pai.

Os métodos da classe ancestral são chamados pelo super,


seguida de um ponto e do nome do método. Um possível
exemplo é super.limpa(). Em relação ao construtor da classe
ancestral, a chamada é feita apenas com a palavra super, seguida

168 U4 - Herança e polimorfismo


dos argumentos a serem passados. Não existe a necessidade do
uso da palavra super caso um método de uma classe ancestral seja
herdado pela classe filha, podendo ser chamado como se tivesse
sido declarado na própria classe.

Para saber mais


Arnold, Gosling e Holmes (2007) respondem, na página 119 de
seu livro (ver em Referências) dão dicas para a resolução de
uma questão intrigante: como e quando estender classes. O
reaproveitamento de classes é sempre a melhor saída? Quando
não devo usá-lo. Faça a leitura e direcione-se!

Não deixe de fazer as atividades propostas, bons estudos e até


a próxima seção!

Atividades de aprendizagem
1. "Você poderá escrever códigos realmente flexíveis. Códigos que serão
mais claros (mais eficientes e simples). Códigos que não serão apenas
mais fáceis de desenvolver, mas também muito mais fáceis de estender,
de maneira que você nunca imaginou no momento em que originalmente
os escreveu" (SIERRA; BATES, 2005, p. 135).
Assinale a alternativa que contém a sentença que caracteriza corretamente
o que se obtém ao se utilizar o recurso do polimorfismo.
a) Com o polimorfismo – e apenas com ele – é possível implementar o
conceito de herança.
b) Com o polimorfismo – e apenas com ele – é possível escrever código
com boa legibilidade.
c) Apenas o polimorfismo impede que uma referência e um objeto sejam
distintos, o que provocaria erro de compilação.
d) Com o polimorfismo é possível escrever um código que não precisa
ser alterado quando novos tipos de subclasse forem introduzidos no
programa.
e) Com o polimorfismo – e somente com ele – é possível implementar
herança e criar código com boa legibilidade no mesmo programa.

2. "Com o polimorfismo, podemos projetar e implementar sistemas que


são facilmente extensíveis – novas classes podem ser adicionadas a partes
gerais do programa com pouca ou nenhuma modificação, contanto que
as novas classes façam parte da hierarquia de herança que o programa

U4 - Herança e polimorfismo 169


processa genericamente" (DEITEL, 2003, p. 336).
Considerando o contexto apresentado, avalie as seguintes asserções e a
relação proposta entre elas.
I) Estabelecida por meio da utilização do polimorfismo, a criação de
classes derivadas estabelece uma relação é-um-tipo-de.
PORQUE
II) É justamente a relação é-um-tipo-de que permite a implementação
do polimorfismo.
A respeito dessas asserções, assinale a opção CORRETA:
a) As asserções I e II são proposições verdadeiras e a II é uma justificativa
da I.
b) As asserções I e II são proposições verdadeiras, mas a II não é uma
justificativa da I.
c) A asserção I é uma proposição verdadeira, e a II é uma proposição falsa.
d) A asserção I é uma proposição falsa, e a II é uma proposição verdadeira.
e) As asserções I e II são proposições falsas.

Fique ligado
Nesta unidade, estudamos:
- Conceito de herança.
- Aplicações de herança em Java.
- Exemplos de herança.
- Delegação (ou composição).
- Conceito e utilização de polimorfismo.
- Acesso a métodos das superclasses.

Para concluir o estudo da unidade


Caro aluno, chegamos ao fim desta unidade! A correta
utilização da herança e do polimorfismo se dá pela criteriosa
escolha de situações em que elas devem ser aplicadas. Criar uma
estrutura hierárquica para modelos que não se compatibilizam é
tornar o código mais difícil de elaborar e de entender. A prática
constante levará você à melhor definição do que deve ou não
ganhar estrutura de superclasse e subclasse. Mais uma vez
recomendamos os sites <www.caelum.com.br> e <http://www.

170 U4 - Herança e polimorfismo


devmedia.com.br/>. Eles disponibilizam farto e variado material
sobre os temas que tratamos nesta seção.
Bons estudos!

Atividades de aprendizagem da unidade


1. A figura que segue representa dados e comportamentos da classe
Animal.
Animal
comida
limites
localização
fazerBarulho()
comer()
dormir()
circular()

Considerando as características e comportamentos que um objeto


desta classe pode ter em um game, assinale a alternativa que contém
a particularidade da programação orientada a objetos, que permite a
criação de ações específicas em um método para que ele se adéque às
particularidades de cada animal.
a) Polimorfismo.
b) Herança.
c) Sobrecarga.
d) Sobreposição.
e) Abstração.

2. A figura que segue representa dados e comportamentos da classe


Animal no âmbito de um game.

Animal
comida
limites
localização
fazerBarulho()
comer()
dormir()
circular()

Foram criadas as subclasses Felino e Canino e ambas herdam de Animal.


Também foi criada a classe Lobo, como uma especialização de Canino. Os
caninos podem compartilhar o próprio método circular(), já que tendem a

U4 - Herança e polimorfismo 171


se mover em grupos. Considere assim a estruturação da hierarquia entre
estas classes:
Animal

fazerBarulho()
comer()
dormir()
circular()

Canino

circular()

Lobo

fazerBarulho()
comer()
Com a criação de um objeto de Lobo (por exemplo, Lobo l = new Lobo()),
as chamadas l.fazerBarulho(), l.circular(), l.comer() e l.dormir() executarão
os métodos contidos, respectivamente, nas classes:
a) Lobo, Lobo, Canino, Aninal.
b) Animal, Lobo, Canino, Canino.
c) Animal, Canino, Animal, Animal.
d) Lobo, Canino, Lobo, Animal.
e) Lobo, Animal, Lobo, Animal.

3. Assinale a alternativa que contém as expressões que preenchem


corretamente as lacunas do texto que segue.
A ideia geral do polimorfismo é a que a _______________ pode assumir
comportamentos ____________ e gerar objetos _______________,
dependendo da condição imposta pela aplicação. Em outras palavras, um
mesmo objeto pode executar ___________ diferentes, dependendo do
momento da sua criação.
a) superclasse, idênticos, distintos, classes.
b) superclasse, diferentes, distintos, métodos.
c) subclasse, diferentes, distintos, métodos.
d) subclasse, idênticos, idênticos, métodos.
e) superclasse, idênticos, idênticos, classes.

4. Considerando acesso a métodos das superclasses, assinale a alternativa


que contém as expressões que preenchem corretamente as lacunas do
texto que segue.

172 U4 - Herança e polimorfismo


Na chamada de métodos de classes ancestrais os construtores são
chamados pela palavra ________, seguida de __________ entre
parênteses, caso existam. Não existe a necessidade do uso da palavra super
caso um método de uma classe __________ seja herdado pela classe
__________, podendo ser chamado como se tivesse sido declarado na
própria classe.
a) extends, argumentos, maior, menor.
b) super, métodos, ancestral, filha.
c) super, argumentos, ancestral, filha.
d) extends, classes, filha, ancestral.
e) superclasse, métodos, estendida, herdada.

5. Considere as duas classes que seguem. Elas foram criadas para a mesma
aplicação Java e servem para modelar um funcionário de regime integral e
um funcionário horista.

public class FuncionarioIntegral { public class FuncionarioHorista {


String nome, matricula; String nome, matricula;
int idade; int idade, totalHoras;
float salario; float salarioHora;

String retornaNome (String String retornaNome (String


nome) { nome) {
return nome; return nome;
} }

String retornaMatricula (String String retornaMatricula


matricula) { (String matricula) {
return matricula; return matricula;
} }

int retornaIdade(int idade) { int retornaIdade(int idade) {


return idade; return idade;
} }

float retornaSalario (float sala- float retornaSalario (float


rio) { salario) {
return salario; return salario;
} }

} int retornaHoras(int total-


Horas) {
return totalHo-
ras;
}

U4 - Herança e polimorfismo 173


Assinale a alternativa que contém a sequência de respostas corretas para
as seguintes questões:
I) Qual é o efeito ao alterar matricula do tipo String para inteiro?
II) Como resolver esta questão?
a) Necessidade de alteração do método retornaMatricula() apenas na
classe FuncionarioIntegral E pela aplicação de herança.
b) Necessidade de reescrita das classes E pela aplicação do polimorfismo.
c) Necessidade de alteração do método retornaMatricula() nas duas classes
E pela aplicação de herança.
d) Necessidade de alteração do método retornaMatricula() apenas na
classe FuncionarioHorista E pela aplicação de herança.
e) Necessidade de alteração do método retornaMatricula() apenas na
classe FuncionarioIntegral E pela aplicação de polimorfismo.

174 U4 - Herança e polimorfismo


Referências
ARNOLD, K.; GOSLING, J.; HOLMES, D. A linguagem de programação Java. 4. ed.
Porto Alegre: Bookman, 2007.

CAELUM. Apostila Java e orientação a objetos. Ite Caellum, 2011. Disponível em:
<https://www.caelum.com.br/apostila-java-orientacao-objetos/heranca-reescrita-
e-polimorfismo/#repetindo-cdigo>. Acesso em: 28 fev. 2017.

DEITEL, H. M. Java: como programar. 6. ed. São Paulo: Pearson Education do Brasil,
2005.

RICARTE, Ivan Luiz Marques. Herança. 2000. Disponível em: <http://www.dca.fee.


unicamp.br/courses/PooJava/heranca/index.html>. Acesso em: 18 fev. 2017.

SANTOS, Rafael. Introdução à programação orientada a objetos usando Java. Rio


de Janeiro: Campus, 2003.

SIERRA, Kathy; BATES, Bert. Use a cabeça: Java. 2. ed. Rio de Janeiro: Alta Books,
2005.

U4 - Herança e polimorfismo 175

Você também pode gostar

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy