You are on page 1of 307

Banco do Brasil

Escriturário - Agente de Tecnologia

1. Aprendizagem de máquina: Fundamentos básicos; ......................................................... 1


Noções de algoritmos de aprendizado supervisionados e não supervisionados; ................. 1
Noções de processamento de linguagem natural. ............................................................... 4
2. Banco de Dados: Banco de dados NoSQL (conceitos básicos, bancos orientados a grafos,
colunas, chave/valor e documentos); MongoDB; Linguagem SQL2008;................................... 9
Conceitos de banco de dados e sistemas gerenciadores de bancos de dados (SGBD); ... 31
Data Warehouse (modelagem conceitual para data warehouses, dados multidimensionais);
Modelagem conceitual de dados (a abordagem entidade-relacionamento); ........................... 60
Modelo relacional de dados (conceitos básicos, normalização); ........................................ 64
Postgre-SQL; .................................................................................................................... 93
3. Big data: Fundamentos; Técnicas de preparação e apresentação de dados. ................ 97
4. Desenvolvimento Mobile: linguagens/frameworks: Java/Kotlin e Swift. ........................ 102
React Native 0.59; ........................................................................................................... 117
Sistemas Android api 30 e iOS xCode 10. ....................................................................... 118
5. Estrutura de dados e algoritmos: Busca sequencial e busca binária sobre arrays;
Ordenação (métodos da bolha, ordenação por seleção, ordenação por inserção), lista
encadeada, pilha, fila e noções sobre árvore binária. .......................................................... 130
6. Ferramentas e Linguagens de Programação para manipulação de dados: Ansible; .... 215
Java (SE 11 e EE 8); ....................................................................................................... 221
TypeScript 4.0;................................................................................................................. 280
Python 3.9.X aplicada para IA/ML .................................................................................... 281
Analytics (bibliotecas Pandas, NumPy, SciPy, Matplotlib e Scikit-learn). ......................... 285

Olá Concurseiro, tudo bem?

Sabemos que estudar para concurso público não é tarefa fácil, mas acreditamos na sua
dedicação e por isso elaboramos nossa apostila com todo cuidado e nos exatos termos do
edital, para que você não estude assuntos desnecessários e nem perca tempo buscando
conteúdos faltantes. Somando sua dedicação aos nossos cuidados, esperamos que você
tenha uma ótima experiência de estudo e que consiga a tão almejada aprovação.

Pensando em auxiliar seus estudos e aprimorar nosso material, disponibilizamos o e-mail


professores@maxieduca.com.br para que possa mandar suas dúvidas, sugestões ou
questionamentos sobre o conteúdo da apostila. Todos e-mails que chegam até nós, passam
por uma triagem e são direcionados aos tutores da matéria em questão. Para o maior
aproveitamento do Sistema de Atendimento ao Concurseiro (SAC) liste os seguintes itens:

01. Apostila (concurso e cargo);


02. Disciplina (matéria);
03. Número da página onde se encontra a dúvida; e
04. Qual a dúvida.

Caso existam dúvidas em disciplinas diferentes, por favor, encaminhar em e-mails separados,
pois facilita e agiliza o processo de envio para o tutor responsável, lembrando que teremos até
cinco dias úteis para respondê-lo (a).

1678859 E-book gerado especialmente para DANIEL CRISTIAN


Não esqueça de mandar um feedback e nos contar quando for aprovado!

Bons estudos e conte sempre conosco!

1678859 E-book gerado especialmente para DANIEL CRISTIAN


1. Aprendizagem de máquina: Fundamentos básicos; Noções de algoritmos de
aprendizado supervisionados e não supervisionados;

APRENDIZADO DE MÁQUINA1

O termo Inteligência Artificial (IA) não é novo, a primeira abordagem foi feita por pesquisadores em
uma conferência de Dartmouth, no ano de 1956. Desde então, muitas expectativas envolveram o assunto,
abordada como a tecnologia do futuro, ficção científica ou realidade atual. Depende do ponto de vista,
pois atualmente a Inteligência Artificial tem se tornando realidade, apesar de que ainda é um campo de
estudo a ser muito explorado.
Porém, quando falamos de IA, o termo mais conhecido e abordado na atualidade é o de Aprendizado
de Máquina, ou Machine Learning no inglês. A área de Aprendizado de Máquina também não é nova, e
já vem sendo praticada há um bom tempo.

Tudo bem que o aumento da velocidade do processamento dos computadores, o barateamento das
tecnologias e a evolução das GPUs influenciam. Mas nunca na história da humanidade se produziu tantos
dados como atualmente. Um artigo da Scientific American revela que em 2016 a humanidade produziu
mais dados do que em toda a sua história, são cerca de 2,5 Exabytes de dados produzidos por dia. E é
essa quantidade de dados disponíveis é que alimenta e fortifica o Aprendizado de Máquina.

Mas a máquina consegue aprender?

Aprendizado de Máquina é um método de análise de dados que automatiza o desenvolvimento de


modelos analíticos. É possível utilizar algoritmos para coletar dados e aprender com os dados, levando
em consideração todo o histórico para então fazer uma determinação ou predição sobre alguma coisa ou
situação do mundo real. O que acontece é que o computador aprende através de técnicas que utilizam
algoritmos computacionais. Os algoritmos trabalham com certa base de dados, então conseguem
aprender através de treinamento e prever situações com a experiência adquirida com aqueles dados. O
mais interessante é que os algoritmos começam a trabalhar de certa forma autônoma, retornando
resultados para que nem foram programados, ou seja, começam a ter insights sobre algo específico.
Atualmente não é difícil encontrar históricos da utilização do Aprendizado de Máquina na vida real.
Exemplos de que realmente a máquina aprende com dados e treinamento. Posso citar os carros
autônomos do Google, que dirigem sozinhos (não começaram a dirigir do nada, eles aprenderam a dirigir
sem a intervenção humana), as ofertas e recomendações online da Netflix, Facebook, Amazon e outras
redes sociais e lojas virtuais, detecção de fraudes automáticas em cartões de créditos e contas bancárias,
e até uma rede neural que é capaz de sonhar. Sim, esse último exemplo é uma rede neural do Google,
que foi ensinada a reconhecer imagens e, enquanto aprende, os responsáveis do projeto dizem que a IA
consegue produzir novas imagens.

1
https://www.profissionaisti.com.br/2017/12/aprendizado-de-maquina-conceitos-e-praticas-da-area-que-esta-movendo-o-mundo/

1
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Métodos para o Aprendizado de Máquina

A pergunta que pode ter ficado no ar é, como fazer a máquina aprender? Anteriormente foi mencionado
que o combustível para a máquina aprender são os dados, porém, existem métodos de aprendizagens
que são utilizados para realizar o Aprendizado de Máquina. Basicamente, existem quatro métodos:
aprendizado supervisionado, aprendizado não supervisionado, aprendizado semi supervisionado e
aprendizado por reforço. Porém, dois são os mais utilizados, o aprendizado supervisionado e o
aprendizado não supervisionado, que são descritos logo abaixo:

Aprendizado supervisionado: neste método de aprendizado, a máquina é treinada através de um


conjunto de dados onde para cada entrada a saída é conhecida. Os dados para este tipo de método
devem possuir rótulos. Podemos comparar a um aprendizado acompanhado, onde o professor
supervisiona todo o processo de aprendizado, dizendo o que é certo, o que é errado e aonde se quer
chegar. O desafio fica para quando se quer prever futuras situações. Com uma entrada de dados, quais
serão os resultados previstos para aquela situação. Neste caso o algoritmo deve se ajustar para chegar
aos resultados corretos e com o máximo de acerto. Para isso o aprendizado pode ser constante,
aumentando assim a experiência com aquele problema. Um bom exemplo para esse caso é conseguir
prever se uma transação do cartão de crédito de certa pessoa é falsa ou não.
Aprendizado não supervisionado: neste método de aprendizagem, ao contrário do anterior, os
dados não possuem rótulo, ou seja, a saída correta não é informada. Podemos comparar a um
aprendizado autodidata, onde o aluno tem que descobrir o caminho e alcançar os resultados. Neste caso,
o algoritmo deve descobrir a base de dados e o que está sendo mostrado, explorando os dados e
buscando encontrar alguma estrutura neles. Dependendo da técnica utilizada, o algoritmo vai encontrar
agrupamentos entre esses dados, aproximando os dados que tem alguma semelhança entre si. Um
exemplo pode ser a classificação de pessoas que tenham a tendência de adquirir diabetes, analisando a
sua alimentação e rotina, porém não é informado quais pessoas realmente tem ou não diabetes. Neste
caso o algoritmo tem que agrupar e contextualizar esses dados para chegar a predição correta dos casos.

Processo de aprendizado

Como o ser humano, a máquina precisa passar por alguns processos para que ela possa aprender.
Como discutido anteriormente, os dados são essenciais, então o primeiro passo é escolher quais dados
serão trabalhados e disponibilizados para o processo do Aprendizado de Máquina.
Porém, nem todo conjunto de dados, seja ele adquirido em bases de dados abertas, coletados em
tempo real ou de um banco de dados, estará de forma estruturada e pronta para ser passados para a
máquina aprender. Como nós, que precisamos de um material para estudo de qualidade para podermos
aprender, a máquina precisa de um conjunto de dados razoável para também poder aprender. Como
citado, muitas das vezes os dados estão completamente desestruturados e fora de um padrão e para isso
é necessário a realização da técnica de Data Cleaning.
Data Cleaning, ou Limpeza de Dados, é uma técnica utilizada para trabalhar com os dados. Nesse
processo é realizada a normalização, a estruturação, padronização e contextualização dos dados. Pode
ser que nem todas as bases de dados precisem passar por esse processo, porém, é bom validar
dependendo do problema e qual o resultado desejado.
Após ter um conjunto de dados estruturados, o próximo passo neste processo de aprendizado é a
aplicação de um método de aprendizagem juntamente com uma técnica de Aprendizado de Máquina. As
técnicas de Aprendizado de Máquina utilizam algoritmos, que por sua vez faz o trabalho computacional e
todo o processamento dos dados.
Logo abaixo, algumas técnicas de Aprendizado de Máquina:

Redes Neurais: são baseadas em como o cérebro humano funciona. Existem diferentes tipos de
Redes Neurais, mas basicamente elas consistem em um conjunto de nós (ou neurônios) dispostos em
várias camadas com interconexões ponderadas entre eles. Cada neurônio combina um conjunto de
valores de entrada para produzir um valor de saída, que por sua vez é passado para outros neurônios
nas camadas seguintes.
Árvore de Decisão: essa técnica utiliza algoritmos do formato de árvore. Cada árvore possui nó,
ramos, folhas e ligações entre eles. Um ramo é um conjunto de nó que testa cada atributo da classificação
e cada ramo corresponde ao valor do atributo. E as folhas atribui uma classificação para os ramos. A
classificação é o resultado final que se quer chegar ou prever com o algoritmo.

2
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Regressão: é a técnica utilizada quando se quer prever valores de algo futuro, baseado em
comportamento de variáveis passadas. Um exemplo seria prever o valor de mercado de um imóvel
utilizando um algoritmo de regressão linear.

Depois da aplicação da técnica de aprendizado e do treinamento do algoritmo, devemos confirmar o


seu desempenho para prever os casos. Nesse momento é a hora da aplicação da prova. Existe algumas
métricas que podem ser utilizadas para assim ter uma medida que fale a quantidade de acertos, erros ou
o desempenho da técnica utilizada. Abaixo, são descritas algumas métricas interessantes:

Acurácia: essa métrica é a mais básica para medir o aprendizado, leva em consideração a proporção
de predições corretas, sem levar em consideração a quantidade de erros.
Sensibilidade: é a proporção de verdadeiros positivos, ou seja, a capacidade do algoritmo em prever
casos corretamente para os casos que são realmente verdadeiros.
Especificidade: A proporção de verdadeiros negativos: a capacidade do algoritmo em predizer
situações erradas que realmente são falsas.
Eficiência: é a média aritmética da sensibilidade e especificidade. Indica se o algoritmo está mais
suscetível a ter verdadeiros positivos ou verdadeiros negativos.

O Aprendizado de Máquina é uma área que está movendo o mundo e influenciado no nosso dia a dia.
A área de IA já é uma realidade e está presente nas nossas atividades, seja no trabalho, estudo ou no
lazer.
Uma coisa é certa: a organização ou profissional que investir em Inteligência Artificial tem a grande
chance de sair à frente competitivamente dos demais em uma das áreas que mais cresce e evolui na
atualidade.

Questões

01. (Banco do Brasil - Escriturário – CESGRANRIO/2018) Um desenvolvedor de uma instituição


bancária foi designado para tentar usar técnicas de aprendizado de máquina para, dado o saldo diário
durante um ano de um cliente, classificá-lo como BOM ou MAU candidato a receber um cartão de crédito
VIP. Para isso, a única informação que pode usar — e que ele recebeu — é um conjunto de treinamento
com 50.000 clientes pré- classificados pelos seus gerentes, contendo 365 campos com os saldos diários
e um campo com o número 1, caso o cliente fosse um BOM candidato, ou o número 0 (zero), caso fosse
um MAU candidato. Essas respostas são consideradas corretas.

Considerando as práticas tradicionais de aprendizado de máquina, o desenvolvedor deve escolher um


algoritmo:
(A) supervisionado, porque humanos precisarão verificar a execução do algoritmo.
(B) supervisionado, porque o conjunto de treinamento possui dados e rótulo, sendo necessário
aprender a função que prediz o rótulo correto.
(C) não supervisionado, porque humanos não precisarão verificar a execução do algoritmo.
(D) não supervisionado, porque o conjunto de treinamento possui dados e rótulo, sendo necessário
aprender a função que prediz o rótulo correto.
(E) não supervisionado, porque, no futuro, os rótulos não estarão disponíveis.

02. (SERPRO - Analista - CESPE) Em relação às soluções existentes para o problema de tomada de
decisão nas organizações, julgue os itens a seguir.

Algumas das principais técnicas utilizadas para a realização de Datamining são: estatística,
aprendizado de máquina, datawarehouse e recuperação de informações.
( ) Certo ( ) Errado

Respostas

01. Resposta: B
Os algoritmos de aprendizado supervisionados fazem previsões com base em um conjunto de
exemplos. Por exemplo, as cotações históricas podem ser usadas para arriscar palpites em preços
futuros. Cada exemplo usado para treinamento é rotulado com o valor de seu interesse — neste caso, o
preço da ação. Um algoritmo de aprendizado supervisionado procura por padrões nesses rótulos de valor.

3
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Ele pode usar qualquer informação que possa ser relevante – o dia da semana, a temporada, os dados
financeiros da empresa, o tipo de setor, a presença de eventos geopolíticos perturbadores – e cada
algoritmo procura tipos diferentes de padrões. Depois que o algoritmo tiver encontrado o melhor padrão
possível, usará esse padrão para fazer previsões para dados de testes sem rótulos — os preços de
amanhã.
No aprendizado não supervisionado, os pontos de dados não têm rótulos associados a eles. Em vez
disso, a meta de um algoritmo de aprendizado sem supervisão é organizar os dados de alguma forma ou
descrever sua estrutura. Isso pode significar agrupá-los em clusters ou encontrar diferentes maneiras de
consultar dados complexos para que eles pareçam mais simples ou mais organizados.

02. Resposta: Certo


A questão fala sobre algumas das principais técnicas utilizadas para a realização de Datamining.
Então a questão informa claramente, que se trata de técnicas para realizar o datamining, e não que
são técnicas do datamining.

Noções de processamento de linguagem natural.

PROCESSAMENTO DE LINGUAGEM NATURAL: ENTENDA COMO FUNCIONA, IMPORTÂNCIA


E APLICAÇÃO2

O processamento de linguagem natural, ou apenas PLN, é uma tecnologia que permite que
computadores entendam, interpretem e manipulem a linguagem humana.
A ideia por trás dessa solução é dar aos computadores a capacidade de lidar com textos escritos por
pessoas, isso inclui analisar o seu contexto, considerar diferenças de linguagem, retirar informações,
ponderar sobre sentidos das frases e até compor textos em resposta.
Para as empresas, o processamento de linguagem natural ajuda a promover uma comunicação mais
fluida e efetiva entre máquinas e humanos, aprimorando, principalmente, o serviço de atendimento ao
cliente.
Mas por que soluções como essa são tão importantes nesse setor? Entre os motivos está a promoção
de experiências melhores para os consumidores, mais dinâmicas, otimizadas e precisas.
O relatório “Tendências para experiência do cliente 2021”, da Zendesk, mostrou que 65% dos clientes
querem comprar de empresas que oferecem transações on-line fáceis e rápidas.
Quanto ao atendimento, especificamente, 64% dos entrevistados disseram que usaram um novo canal
de suporte em 2020, e 73% pretendem continuar usando ao longo deste ano.
Inclusive, um dos canais de atendimento que ganhou mais destaque nos últimos tempos foram os
aplicativos de mensagem.
O uso do WhatsApp, por exemplo, cresceu acima de 90% entre as opções de suporte no Brasil em
2020.
Em ferramentas como essa, o processamento de linguagem natural pode aprimorar ainda mais o
atendimento ao cliente, aumentando o seu nível de satisfação e de interação com a marca.

Neste artigo, você vai entender tudo sobre PLN, incluindo:


- O que é processamento de linguagem natural
- Principais objetivos do PLN
- Como o processo de linguagem natural evoluiu ao longo dos tempos
- Como funciona o processo de linguagem natural
- Quais são os maiores desafios do processamento de linguagem natural
- Por que o PLN é importante para as empresas
- Quais são os métodos do processamento de linguagem natural
- Em quais situações do dia a dia pode ser usado o PLN

2
https://www.zendesk.com.br/blog/processamento-de-linguagem-natural/

4
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O que é processamento de linguagem natural?

PLN, processamento de linguagem natural, ou NLP, natural language processing, é uma área da
Inteligência Artificial (IA) que tem como objetivo desenvolver tecnologias que permitam que máquinas
entendam a linguagem humana.
O processamento de linguagem natural funciona como uma espécie de “tradutor” que garante que,
quando um humano se comunica com um computador, esse consiga entender claramente o que essa
pessoa quer dizer.
O conceito diz respeito, basicamente, às soluções que já utilizamos no nosso dia a dia. Dois bons
exemplos são quando digitamos algo em sites de pesquisa e esperamos que esse nos entenda e dê a
resposta que precisamos, ou quando damos um comando de voz para uma assistente virtual.
Porém, esse processo não é assim tão simples. A tecnologia PLN não só permite que o dispositivo
compreenda a linguagem do usuário, como também garante que a sua experiência seja cada vez melhor.
Além disso, o processamento de linguagem natural capacita os dispositivos para que criem respostas
aos usuários, que podem ser tanto escritas quanto em áudios.
Em resumo, o NLP entende, interpreta e simula a linguagem natural dos seres humanos, promovendo
uma interação e uma conversação bastante similar à que acontece entre duas pessoas.

Principais objetivos do PLN


O processamento de linguagem natural faz parte do leque de conceitos da Inteligência Artificial, tais
como machine learning e deep learning que, em resumo, são tecnologias que ajudam máquinas a se
comunicarem cada vez melhor com os humanos.
Dica de leitura: “Entenda a diferença entre inteligência artificial, machine learning e deep learning”
O principal objetivo do processamento de linguagem natural é garantir que máquinas (computadores
e diferentes dispositivos) entendam com perfeição o que uma pessoa está dizendo.
Ou seja, o PLN prepara esses equipamentos para “conversarem” com os usuários, melhorando suas
experiências em diversos aspectos.
Essa tecnologia é bastante complexa, ela permite, por exemplo, que mensagens enviadas por um
cliente em uma plataforma de atendimento sejam lidas e interpretadas.
E quando falamos “interpretadas”, isso contempla compreender os diferentes contextos que uma
mensagem escrita ou falada pode ter.
O processamento de linguagem natural garante que o computador reconheça o contexto da
mensagem, faça uma análise morfológica, semântica e sintática, analise sentimos e interprete sentidos.
Com base nisso, a máquina obtém a capacidade de entender e de criar respostas para atender o que
foi apontado pelo cliente.
O PLN também permite extrair informações dessas conversações e, com isso, aprimorar a
comunicação que está sendo realizada com a pessoa.
Vale destacar que, mesmo com todos os avanços tecnológicos, o processamento de linguagem natural
foi essencial para atender a uma necessidade bem pontual das empresas, que era de melhorar o
tratamento computacional de diferentes linguagens (escritas e faladas).
Esse melhoramento, por sua vez, reflete no aprimoramento de diferentes soluções, produtos e serviços
entregues aos clientes.

Como o processo de linguagem natural evoluiu ao longo dos tempos?

O NLP não é uma ciência assim tão nova. No entanto, está avançando e se aprimorando rapidamente
por conta da necessidade cada vez maior de melhorar o relacionamento entre máquinas e pessoas.
Esse avanço também é devido à transformação digital das empresas, que acontece paralelamente à
utilização mais ampla de soluções de Big Data e de algoritmos mais complexos e robustos para diferentes
finalidades.

A origem do processamento de linguagem natural


O processamento de linguagem natural que conhecemos atualmente tem como base um teste
chamado Teste de Turing, de 1950.
Proposto por Alan Mathison Turing, matemático e cientista da computação, e publicado no artigo
Computing Machinery and Intelligence, o objetivo era conseguir identificar quem era a máquina e quem
era a pessoa em uma conversa entre eles, considerando apenas as perguntas e respostas dessa
interação.

5
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Mas para que Turing conseguisse propor esse teste, antes dele, outros cientistas já faziam estudos
com foco no PLN.

A primeira máquina de tradução


A história aponta que na década de 1940, surgiu a primeira máquina de tradução, denominada Machine
Translation.
O equipamento foi desenvolvido pelos cientistas Booth e Weaver, que usaram como base a criptografia
utilizada na Segunda Guerra Mundial para interpretar comunicações de inimigos.
A partir desse feito, diversos outros projetos foram desenvolvidos, inicialmente com a intenção de
diferenciar idiomas e resolver problemas de tradução de palavras.

A inclusão do PLN no campo da linguística


Em 1950, o estudo sobre o processamento de linguagem natural sofreu um importante avanço.
O cientista Noam Chomsky publicou um estudo que abordava o conceito de gramática gerativa, que
considerava as transformações sofridas pela linguagem, bem como o seu aspecto criativo.
Assim, o PLN que, até então, fazia parte das pesquisas voltadas para a área de computação, passou
a integrar também o campo da linguística.
Também por isso, hoje, os estudos sobre processamento de linguagem natural envolvem Inteligência
Artificial, ciência da informação e linguística.
Ainda que o apontamento de Chomsky tivesse grande peso no NLP, o Comitê de Processamento de
Linguagem, ALPAC, entendeu que ainda não era possível realizar uma tradução completa utilizando esse
recurso.
Com isso, os estudos somente voltaram a ter ênfase no final da década de 1960, início dos anos 1970,
também pelas mãos de Chomsky, através do um trabalho do cientista voltado para competências
linguísticas

A criação do ELIZA
Ainda que os estudos de Chomsky fossem difíceis de serem interpretados, alguns cientistas tentaram
comprovar que a sua teoria poderia se tornar realidade.
Um dos trabalhos que foi desenvolvido com essa finalidade foi o software chamado ELIZA, criado com
o objetivo de promover uma conversa entre um psicólogo (computador) e um paciente (humano).
ELIZA se baseava nas informações que tinha sobre as emoções e pensamentos dos seres humanos
para elaborar respostas aos questionamentos do paciente, criando, assim, uma conversação entre
homem e máquina.
Se, porventura, o apontado pelo paciente estivesse além do conhecimento do software, ELIZA
elaborava respostas genéricas para promover a continuidade do processo.

O PLN nos dias de hoje


Em setembro de 2019, o Allen Institute for AI criou o Aristo, software que integra tecnologias voltadas
para leitura, aprendizagem, raciocínio e explicações.
A solução conseguiu, rapidamente, resolver um teste científico, similar a um aplicado a um aluno do
9º ano escolar aqui do Brasil, com 90% de acerto.
Em um primeiro momento, essa tarefa parece algo extremamente simples, especialmente quando
atribuída a um computador.
No entanto, para que o Aristo conseguisse esse feito, foi preciso que ele entendesse e analisasse
bilhares de documentos para extrair apenas as informações que precisava.
Juntando todos esses avanços do processamento de linguagem natural, ao aprimoramento do
machine learning e do deep learning, hoje, as soluções que utilizam o PLN estão cada dia mais aptas a
passarem com êxito pelo Teste de Turing.
O uso em chatbots, por exemplo, ajuda a compreender os sentimentos dos clientes com relação a
produtos e serviços e, com isso, promover um atendimento mais humanizado, ainda que esteja sendo
realizado por uma máquina.

Como funciona o processo de linguagem natural?


Para a criação do PLN é preciso utilizar diferentes técnicas que visam interpretar a linguagem humana,
isso inclui métodos estatísticos, abordagem de algoritmos e machine learning.
Mas para contemplar todos esses pontos, o processamento de linguagem natural é dividido em níveis
e em tipos de abordagem.

6
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Níveis de processamento
Os níveis de processamento são definidos com base nas diferentes análises que uma forma de
linguagem por ter.
Por isso, são divididos em 7 níveis distintos, organizados de acordo com a dificuldade de
implementação. São eles:
- Fonologia
- Morfologia
- Léxico
- Sintático
- Semântico
- Discurso
- Pragmático
- Fonologia

Nível de processamento voltado para o desenvolvimento de aplicativos de voz, a fonologia tem como
objetivo entender e interpretar o som das palavras, considerando, inclusive, a maneira como são
pronunciadas.

Morfologia
Visa entender a composição das palavras, dividindo-as em morfemas que, ainda que sejam fragmentos
com significado, não representam, propriamente, uma palavra.

Léxico
Este nível de processamento tem como função interpretar o significado individual de cada palavra dita
em uma interação entre humano e máquina.

Sintático
O sintático, por sua vez, faz a análise completa de cada frase dita ou escrita na conversão.

Semântico
O processo semântico complementa o anterior, e busca compreender o significado da frase em
questão.

Discurso
Já o discurso tem uma função mais ampla. Seu objetivo é fazer uma análise completa e encontrar o
significado do texto, seja ele falado ou escrito.

Pragmático
O último nível do funcionamento do processamento de linguagem natural interpreta a mensagem e
extrai informações e significados extras que podem não estar explícitos nas palavras.

Tipos de abordagem
Os tipos de abordagem do PLN dizem respeito à maneira como os softwares vão tratar os níveis de
processamento que acabamos de mencionar.

Para isso, são divididos em 4 diferentes categorias, sendo elas:


- Simbólica
- Estatística
- Conexionista
- Híbrida

Simbólica
A abordagem simbólica tem como base regras bem definidas e estruturadas de linguística. Por não
terem ambiguidades, são criados algoritmos que possibilitam a realização de processamentos de
linguagem simples.

7
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Estatística
Já a abordagem estatística se baseia no próprio texto ou fala para realizar as suas deduções de
interpretação. Para isso, são utilizados modelos matemáticos que dispensam o emprego das regras
linguísticas.

Conexionista
Similar à abordagem anterior, a conexionista também cria modelos genéricos para a criação e
interpretação das linguagens. Porém, mescla teorias de conhecimento com aprendizado estatístico para
conseguir deduzir, transformar e manipular os textos.

Híbrida
A abordagem híbrida, por sua vez, reúne todas as anteriores. Sua utilização ajuda a tratar de maneira
mais profunda problemas relacionados ao processamento de linguagem natural, conferindo maior
flexibilidade a essa atividade.

Quais são os maiores desafios do processamento de linguagem natural?


Neste ponto você deve estar pensando quanto o processamento de linguagem natural pode resolver
diversas questões do dia a dia da sua empresa, especialmente no que diz respeito ao atendimento ao
cliente, certo?
Obviamente, esse pensamento está correto, visto que um dos objetivos de utilizar a tecnologia NLP é
justamente esse.
No entanto, por mais que aprimore diversos pontos desse relacionamento, há alguns desafios que
essa solução ainda precisa enfrentar para se tornar mais completa e precisa.

Os três que mais se destacam são:


- Se ajustar à evolução do idioma
- Compreender o que há além das palavras
- Evoluir dentro do seu próprio objetivo

Se ajustar à evolução do idioma


Você já deve ter ouvido que a língua portuguesa é um idioma vivo, não é? Isso acontece porque a
linguagem sofre uma série de influências, incluindo as mudanças e hábitos da sociedade.
Um bom exemplo disso é a palavra “você”. Em tempos passados, esse pronome de tratamento era
“vossa mercê”, que evolui para “vosmecê”, até chegar ao formato que utilizamos hoje, “você”.
No entanto, se considerarmos uma linguagem coloquial, ou seja, mais informal, esse pronome também
pode se resumir a “cê”, por exemplo, “Cê vai hoje de carro para o trabalho?”
Além disso, é preciso considerar que, em várias regiões do Brasil, o “você” não é tão utilizado, sendo
substituído pelo “tu” com a mesma finalidade.
Esse é só um exemplo de como o nosso idioma pode mudar com o passar do tempo e de acordo com
a região onde falado.
Por isso, um dos desafios do processamento de linguagem natural é garantir que os computadores e
dispositivos entendam todas essas mudanças e se adaptem a elas conforme evoluem.

Compreender o que há além das palavras


Pontuações, tom de voz, erro de digitação, grafias incorretas, tom de voz etc. Essas são mais algumas
variações que as palavras e as frases podem sofrer e que afetam a interpretação e o entendimento das
soluções com tecnologia PLN.
Imagine a seguinte situação: um cliente entra em contato com uma empresa através do chat de suporte
help desk e digita a seguinte frase: “Quero solicitar o concerto da minha máquina de lavar”.
Se o software interpretar o significado da palavra “concerto” isoladamente, é bem provável que não
consiga prosseguir com o atendimento adequadamente.
Há também a questão do significado das palavras, que pode variar bastante de região para região. Por
exemplo, um cliente pode solicitar o “reparo da sua calçada”, enquanto outro o “reparo do seu passeio”.
Alguns podem acionar o seguro do carro via robô do WhatsApp e informar na mensagem como ponto
de referência o "semáforo da Rua 2”, já outros “o farol da Rua 2”, ou ainda “a sinaleira da Rua 2”.
Por isso, um dos desafios do processamento de linguagem natural é considerar todas essas variações
e possibilidades, de modo que a solução realmente consiga oferecer um atendimento rápido e preciso
aos usuários.

8
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Evoluir dentro do seu próprio objetivo
Um último obstáculo a ser superado pelo PLN é continuar a evolução dentro do seu próprio conceito.
O que queremos dizer é estar em constante aperfeiçoamento para aprimorar a compreensão da
linguagem humana e estabelecer uma comunicação cada vez mais natural e fluida.
Isso também é fundamental para atender às novas necessidades do mercado, que inclui as mudanças
de comportamento do consumidor e suas expectativas junto às marcas.

2. Banco de Dados: Banco de dados NoSQL (conceitos básicos, bancos


orientados a grafos, colunas, chave/valor e documentos); MongoDB; Linguagem
SQL2008;

NOSQL

Banco de Dados NoSQL (Key/Value, Orientados a Documentos e Grafos)34

O termo NoSQL (do inglês Not Only SQL) foi utilizado pela primeira vez em 1998 para designar um
banco de dados não relacional de código aberto. É um termo genérico para uma classe definida de banco
de dados não-relacionais e ainda faz referência aos bancos de dados relacionais mais populares do
mercado: MySQL, PostgreSQL, etc.
Com a popularização da internet e das redes sociais, a geração de conteúdo por dispositivos móveis
e o número cada vez maior de pessoas e dispositivos conectados, faz com que o trabalho de
armazenamento de dados com o objetivo de utilizá-los em ferramentas analíticas, comece a esbarrar nas
questões de escalabilidade e custos de manutenção desses dados. Bancos de dados relacionais
escalam, mas quanto maior o seu tamanho mais caro se torna essa escalabilidade, seja pelo custo de
novas máquinas, ou pelo aumento de especialistas nos bancos de dados utilizados. Os não relacionais,
permitem uma escalabilidade mais barata e menos trabalhosa, pois não exigem máquinas extremamente
poderosas, e como sua manutenção é fácil permite que seja necessário um número menor de
profissionais.
Existem quatro principais tipos de banco de dados NoSQL, que são:
Chave/Valor (Key/Value): conhecidos como tabelas de hash distribuídas, armazenam objetos
indexados por chaves, e facilita a busca por esses objetos a partir de suas chaves.
Vantagens: Gerência bem o tamanho, processa uma quantidade constante de leituras e escritas, é
rápido e é amigo do programador.
Alguns bancos que utilizam esse padrão são: DynamoDb, Couchbase, Riak, Azure, Table Storage,
Redis, Tokyo Cabinet, Berkeley DB, etc.

Características:
- Comparando com um banco de dados relacional, é uma única tabela com duas colunas: uma chave
primária e um valor.
- É o banco NoSQL mais simples de todos.
- Cada item no banco é apenas o nome de um atributo (chave) e seu respectivo valor.
- Podemos compará-lo a um hashmap.
- Os dados são acessados somente pela chave (os valores são transparentes ao sistema).
- Flexibilidade na estrura dos dados.
- Extremamente rápido.
- Suporta quantidades extraordinariamente grande de dados.

Aplicação:
- Propósito geral.
- Aplicações mobile (metadados do aplicativo).

Orientados a Documentos: os documentos dos bancos dessa categoria, são coleções de atributos e
valores, onde um atributo pode ser multi-valorado.
Em geral, os bancos de dados orientados a documento não possuem esquema, ou seja, os
documentos armazenados não precisam possuir estrutura em comum.

3
Fonte: http://jordankobellarz.github.io/nosql/2015/01/27/modelos-de-bancos-no-sql.html
4
Fonte: http://www.codate.com.br/banco-de-dados-nosql/

9
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Essa característica faz deles boas opções para o armazenamento de dados semi estruturados.
Vantagens: Modelagem de dados natural, amigo do programador, desenvolvimento rápido e amigo da
web(CRUD).
Alguns bancos que utilizam esse padrão são: MongoDB, CouchDB, RavenDB, etc.

Características
- Modelo mais próximo do relacional, enquanto um BD relacional guarda os dados em linhas e colunas,
o BD orientado a documentos guarda em documentos.
- Os documentos geralmente são armazenados como JSON.
- São amigáveis à programação orientada a objetos, visto que cada documento é um objeto que contém
vários atributos (campos) com seus respectivos tipos.
- Em vez de armazenar dados correlacionados em locais diferentes, esse modelo permite que tudo
fique em um só documento, eliminando a necessidade de joins.
- Os campos podem mudar de um documento para outro, permitindo flexibilidade na estrutura do
banco, mesmo após a implementação.
- As querys são relativamente menores do que em um banco relacional.
- É o mais usado dentre os bancos não relacionais.

Aplicação:
- É um banco de propósito geral.

Famílias de Colunas: bancos relacionais normalmente guardam os registros das tabelas


contiguamente no disco. Por exemplo, caso se queira guardar id, nome e endereço de usuários em um
banco de dados relacional, os registros seriam:

Id1, Nome1, Endereço1;


Id2, Nome2, Endereço2.

Essa estrutura torna a escrita muito rápida, pois todos os dados de um registro são colocados no disco
com uma única escrita no banco. Essa estrutura também é eficiente caso se queira ler registros inteiros.
Mas para situações onde se quer ler algumas poucas colunas de muitos registros, essa estrutura é pouco
eficiente, pois muitos blocos do disco terão de ser lidos.
Para esses casos onde se quer otimizar a leitura de dados estruturados, bancos de dados de famílias
de colunas são mais interessantes, pois eles guardam os dados contiguamente por coluna.
O exemplo anterior em um banco de dados dessa categoria ficaria: Id1, Id2; Nome1, Nome2;
Endereço1, Endereço2. Os bancos de dados de famílias de colunas são mais interessantes para
processamento analítico online (OLAP). O Bigtable é uma implementação da Google dessa categoria de
bancos de dados.
Vantagens: Gerência bem o tamanho, processa uma grande quantidade de carga via fluxos, alta
disponibilidade, múltiplos data-centers e MapReduce.
Outros bancos de dados que são orientados a coluna: Cassandra, Hypertable, Amazon SimpleDB, etc.

Características:
- É um banco chave-valor multidimensional, ou seja, cada tupla (chave, valor) pode conter várias tuplas
encadeadas.
- As colunas podem ser agrupadas por famílias de colunas.
- O acesso é feito através da chave da coluna.

Aplicação:
- Propósito geral.

Orientado a Grafos: diferente de outros bancos de dados NoSQL, esse está diretamente relacionado
a um modelo de dados estabelecido, o modelo de grafos. A ideia desse modelo é representar os dados
e / ou o esquema dos dados como grafos dirigidos, ou como estruturas que generalizem a noção de
grafos.
O modelo de grafos é aplicável quando “informações sobre a inter-conectividade ou a topologia dos
dados são mais importantes, ou tão importante quanto os dados propriamente ditos”.
O modelo orientado a grafos possui três componentes básicos: os nós (são os vértices do grafo), os
relacionamentos (são as arestas) e as propriedades (ou atributos) dos nós e relacionamentos.

10
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Vantagens: Auxilia em problemas complicados com grafos e é rápido.
Alguns bancos que utilizam esse padrão são: Neo4J, Infinite Graph, InforGrid, HyperGraphDB, etc.

Características:
- Usa grafos para armazenar os dados
- Baseado na teoria dos grafos (rede de elementos interconectados)
- Cada nó representa uma entidade (como uma pessoa, empresa, conta, etc.), que contém suas
respectivas propriedades
- Não requer operações de join
- Requer uma grande curva de aprendizado
- Flexibilidade na estrutura dos nós

Aplicação:
- Redes sociais.
- Árvores genealógicas.
- Controle de acesso.
- Georreferenciamento.

Outros Bancos NoSQL (Pouco Usuais)


- Orientado a objetos.
- Orientado a serviços.
- Multivalor.
- Multidimensional.

Características em comum:
- Todos esses modelos permitem flexibilidade no projeto do banco de dados.
- São amigáveis à programação ágil.
- Podem ser consistentes ou ter eventuais consistências.

A seguir será demonstrado o Mongodb que é um dos bancos de dados NoSQL.

Mongodb: um Banco de Dados NoSQL5

Vamos falar um pouco sobre o Mongodb que é um banco de dados NoSQL que não utiliza modelagem
relacional, o que é para muitos uma novidade assustadora, pois estamos acostumados a utilizar o modelo
relacional. Os modelos relacionais utilizam uma abordagem bidimensional que representa quantas
dimensões quisermos a partir de relacionamentos com linhas e colunas. Entretanto, para consultas com
grandes quantidades de dados podemos encontrar problemas de performance especialmente quando
precisamos distribuir os dados em vários computadores. O Mongodb não utiliza este conceito e oferece
uma alta performance em armazenamento de grandes quantidades de dados e rapidez nas consultas.
Nesse post vamos falar sobre agregação(embeded), associação(reference), ids automáticos e Schemas.
Os registros são documentos Fazendo uma comparação com banco de dados que utilizam modelagem
relacional para persistirmos dados no banco precisamos criar uma base de dados antes, entretanto no
Mongodb não precisamos criá-la pois isso é feito automaticamente. Abaixo segue dois exemplos do
modelo relacional e o utilizado pelo Mongodb: SQL

5
Fonte: http://www.codate.com.br/mongodb-um-banco-de-dados-nosql/

11
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Associações (References) no Mongodb
Fazendo uma analogia com SQL podemos dizer que no Mongodb a associação é como se fosse uma
foreign key do modelo relacional. Podemos incluir referências e vincular um documento ao outro.
Exemplo:

12
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Agregação (Embeded) no Mongodb
Podemos incorporar sub-documentos a um documento, como assim? Vamos utilizar o exemplo
anterior, realizamos uma associação e vinculamos o documento acadêmico com o documento curso
inserindo o campo curso_id. Se fossemos realizar uma consulta o banco acessaria os dois documentos,
porém, poderíamos incorporar o curso diretamente no documento acadêmico o que permitiria ao banco
realizar apenas uma operação de consulta. Exemplo:

Id’s automáticos(ObjectId) No mongo os documentos são identificados através de um campo id que


atua como uma chave primária. Cada documento possui seu id que possivelmente será único. Se o
programador não informar o id do documento por padrão o mongoDB irá adicionar um campo id
automaticamente que conterá um ObjectId. Exemplo: Vamos criar um documento que e salvá-lo. Repare
que não identificamos um id para o documento acadêmico:

Agora vamos pesquisar esse mesmo documento através do comando “db.academico.find();”. Deve
aparecer uma resposta similar à seguinte (o atributo _id é um identificador exclusivo e provavelmente
será diferente no seu resultado):

13
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Schemas
Nos bancos de dados que utilizam modelagem relacional é necessário realizar um esquema de tabelas
e atributos que farão parte de uma estrutura de dados fixa, ou seja, se durante um projeto precisarmos
inserir um novo atributo em uma tabela do banco de dados seremos obrigados a acrescentá-lo na tabela
incluindo uma coluna com campos vazios. O Mongodb não exige uma estrutura fixa, assim, podemos
atualizar apenas um documento, com a adição de novos campos, por exemplo, sem causar problemas
na estrutura do banco de dados. Binary JSON Permite representação de algumas formas de dados que
não fazem parte do padrão JSON(Date, BinData, outros). Através do comando instanceof retornará um
boolean para testar se um valor é uma instância de algum tipo: acadêmico.id instanceof ObjectId
Resultado: true. Através do comando typeof podemos saber qual é o tipo do atributo. typeof acadêmico.id

SQL SERVER

Conceito

O MS SQL Server é um SGBD (Sistema Gerenciador de Banco de Dados Relacional) desenvolvido


pela Microsoft, suai criação foi dada a partir de uma parceria com a Sybase, sta parceria durou até 1994,
com o lançamento da versão para Windows NT e desde então a Microsoft mantém a manutenção do
produto6. O Microsoft SQL Server é software bastante flexível e robusto, podendo ser utilizado em
pequenas aplicações (versões express) até mesmo a aplicações com milhões de usuários e
computadores acessando grandes quantidades de dados a partir da Internet ao mesmo tempo. Suas
linguagens de consulta primárias são T-SQL e ANSI SQL.
Por ser um banco de dados relacional, as tabelas devem ficar contidas dentro de uma base de dados,
iniciaremos a explicação dos comandos a partir da criação de uma base de dados e suas tabelas.
Lembrando que, as informações contidas nas tabelas devem possuir tipos específicos para seu
armazenamento:
Integer: para números inteiros;
Float ou Real: utilizado para Números Reais;
DateTime: votado a datas e horas
VarChar(XX): utilizado para textos, entre os parênteses é necessário colocar o tamanho máximo que
o texto pode ter, exemplo varchar(2), nesse caso o texto poderá ter no máximo 2 caracteres.

Abaixo vejamos alguns exemplos desde a criação da base de dados até comandos para seleções.
Para criações gerais SQL Server via T-SQL utilizamos o comando Create que tem como função criar
bases de dados e tabelas.

Sintaxe Geral para Criação de Bases de Dados:

create database NomeBase;

Para a criação uma tabela é necessário além de utilizar os comandos básicos incluir os atributos da
tabela:

Create table nome_tabela(


Atributo 1
Atributo 2
...)

Veja um exemplo para criação de uma tabela para armazenar informações de cliente:

create table clientes (


codigo int identity(1,1) not null,
nome varchar(30) not null,
cidade varchar(20) null,
data_nasc char(1) not null,

6
https://technet.microsoft.com/pt-br/library/ms174173(v=sql.105).aspx
https://msdn.microsoft.com/pt-br/library
http://www.macoratti.net/14/02/sql_tsql1.htm

14
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Primary Key(codigo)
);

Inserir dados na tabela cliente:

Insert into clientes (cli_codigo, cli_nome, cli_cidade, cli_sexo, cli_email)


values (1,'Luis Alexandre', 'Tupã', 'M','le.boyago@gmail.com');

Ou
Insert into clientes values (1,'Luis Alexandre', 'Tupã', 'M','le.boyago@gmail.com');

Visualizar, consultar, pesquisar, selecionar [SELECT]:

Select cli_codigo, cli_nome, cli_cidade, cli_sexo from clientes;

Ou

Select * from clientes;


* significa tudo

Cláusula Order By
A cláusula Order By muda a ordem de apresentação do resultado da pesquisa e possibilita colocar
também em ordem ascendente ou descendente.

Ex.: Select * from clientes order by cli_nome;

Restrição Where (Quando)


Com where é possivel restringir os dados (tuplas) que serão listados, ou seja, efetuar uma condição
para que o mesmo apareça na listagem

Operadores Lógicos
= igual, > maior, >= maior ou igual, < menor, <= menor ou igual

Ex.:

Select * from clientes where cli_codigo >= 2;

Select * from clientes where cli_codigo >= 2 and cli_codigo <= 5;

Select * from clientes where cli_codigo >= 2 and cli_codigo <= 5 and cli_cidade = ‘Tupã’;

Select * from clientes where cli_codigo >= 2 and cli_codigo <= 5 and cli_cidade = ‘Tupã’ and cli_sexo
= ‘F’;

Select * from clientes where cli_codigo >= 2 and cli_codigo <= 5 and cli_cidade = ‘Tupã’ and cli_sexo
= ‘F’ order by

cli_nome;
Drop
Elimina uma tabela, seus dados, atributos e referências.
Ex.: drop table clientes

Alter
Este comando permite inserir/eliminar atributos nas tabelas já existentes.
Ex: alter table clientes add cli_email varchar (30);
alter table clientes drop cli_email; --apaga este atributo
modificar estrura das colunas:

alter table clientes modify cli_email varchar (40);

15
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Update
Este comando permite alterar os dados da tabela
Ex: update clientes set cli_sexo = 'M';
update clientes set cli_sexo = 'F' where cli_codigo =2;
update clientes set cli_cidade = 'Lajeado', cli_nome = 'Giulia
Santos' where cli_codigo =3;

Delete
Este comando permite exluir registros das tabelas
Ex: delete from clientes; --exclui todos os registros da tabela

delete from clientes where cli_sexo = 'F';

delete from clientes where cli_cidade like 'C%';

Truncate
Este comando elimina de forma irreversível todos os dados da tabela
Ex: truncate table clientes;

Funções de Agregação

Avg
Este comando calcula o valor médio de uma determinada coluna.
Ex: select avg(cli_salario) as media_salarial from clientes;

Count
Este comando conta as linhas de uma tabela.
Ex: select count(cli_codigo) from clientes;

select count(*) as quantidade_registros from clientes;

Sum
Este comando calcula a somatória de todos os valores de uma coluna.
Ex: select sum(cli_salario) as valor_total from clientes;

select sum(cli_salario) as valor_total from clientes where cli_sexo = 'F';

Min
Esta função sql retorna o valor mínimo encontrado em uma coluna.
Ex: select min(cli_salario) as valor_minimo from clientes;

select min(cli_salario) as salario_minimo from clientes where


cli_codigo in(1,3)

Max
Esta função sql retorna o valor máximo encontrado em uma coluna.
Ex: select max(cli_salario) as valor_minimo from clientes;
select max(cli_salario) as salario_minimo from clientes where
cli_codigo in(1,3)
Like – Próximo a

Esta função sql retorna um valor “próximo a” alguma palavra ou número digitado, para isso utiliza de
caractere coringa “%”
Ex: select cli_nome from like ‘ana%’; - ele retorna todos os clientes que iniciem seus nomes com ana
select cli_nome from like ‘%ana%’; - ele retorna todos os clientes que tenham em seus nomes as letras
ana exemplo Ana Lucia, Mariana

16
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Funções Escalares de Texto

Upper
Esta função sql converte para maiúsculas todos os caracteres.
Ex: select cli_codigo, upper(cli_nome) ,cli_cidade, cli_sexo, cli_email, cli_salario from clientes;

Lower
Esta função sql converte para minúsculas todos os caracteres.
Ex: select cli_codigo, upper(cli_nome) as Nome, cli_cidade, cli_sexo, lower(cli_email), cli_salario from
clientes;

Agrupar por.
Agrupar dados para qualquer banco de dados.
Ex.: select cli_sexo as Sexo, count(cli_sexo) as quantos
from clientes group by cli_sexo;

Resultado: Retorna quantos clientes são do sexo masculino e quantos são do sexo feminino.

Média
select AVG (cli_salario) from clientes group by cli_sexo;

Neste exemplo acima irá retornar a media salarial agrupando por sexo

Tabela de Cidades -- para evitar redundância de dados

CREATE table cidades


(
cid_codigo int identity(1,1) not null,
cid_nome varchar(30),
primary key (cid_codigo)

);
Chave Estrangeira --Foreign Key
Efetua ligações entre as tabelas. Implementa integridade dos dados.
Alter table clientes
Add foreign key (cli_codcid) references cidades (cid_codigo);

Com constraint
Alter table clientes
Add constraint chave_estrang_cidade foreign key (cli_codcid)
references cidades (cid_codigo)
on delete RESTRICT
on update CASCADE;

Para apagar uma chave estrangeira:


alter table clientes drop FOREIGN key chave_estrang_cidade;

Union - Union All

Auto Incremento
Preenche automaticamente o conteúdo de um campo inteiro, toda vez que um novo registro for inserido
na tabela.

create table clientes


(
cli_codigo int identity(1,1) not null,
cli_nome varchar(30) not null,
cli_cidade varchar(20),
cli_sexo char(1),

17
1678859 E-book gerado especialmente para DANIEL CRISTIAN
primary key (cli_codigo)
);

Veja que para inserir você não precisa mais colocar o código.
Ex.: Insert into clientes (cli_nome, cli_cidade, cli_sexo, cli_email,
cli_salario, cli_codcid, cli_indicador)
values ('Selvino Santos', 'Lajeado','M','selvino@infotarde.com.br',50,1,’xzxzxzxzx’);

Assim como no em qualquer SGBD voltado a linguagem SQL os comados são muito semelhantes, a
seguir veremos alguns comandos básicos.

Criar uma Base de Dados (Criar um Banco de Dados)

create database locadora;

Criar uma tabela chamada clientes e filmes:

create table clientes (


cli_codigo int,
cli_nome varchar(30),
cli_cidade varchar(20),
cli_sexo char(1)
);

create table filmes


(
fil_codigo int,
fil_nome varchar(30),
fil_genero varchar(15),
fil_preco numeric(4,2)
);

Inserir dados na tabela cliente:

Insert into clientes (cli_codigo, cli_nome, cli_cidade, cli_sexo, cli_email) values (1,'Luis Alexandre',
'Tupã', 'M','le.boyago@gmail.com');

Ou

Insert into clientes values (1,'Luis Alexandre', 'Tupã', 'M','le.boyago@gmail.com');

SQL Server Management Studio

O SQL Server Management Studio é um ambiente integrado para acessar, configurar, gerenciar,
administrar e desenvolver todos os componentes do SQL Server. O SQL Server Management Studio
combina um amplo grupo de ferramentas gráficas com diversos editores de script avançados para dar
acesso ao SQL Server para os desenvolvedores e administradores de todos os níveis de habilidade.
O SQL Server Management Studio combina os recursos do Enterprise Manager, Query Analyzer e
Analysis Manager, incluídos em versões anteriores do SQL Server, em um único ambiente. Além disso,
o SQL Server Management Studio trabalha com todos os componentes do SQL Server, como Reporting
Services, Integration Servicese SQL Server Compact 3.5 SP2. Desenvolvedores terão uma experiência
familiar e os administradores de banco de dados terão um único utilitário abrangente que combina
ferramentas gráficas fáceis de usar com sofisticadas capacidades de script.
- Para acessar o SQL Server Management Studio
Na barra de ferramentas, clique em Iniciar, aponte para Todos os Programas, aponte para Microsoft
SQL Server SQL Server 2008 e clique em SQL Server Management Studio.
Com o SQL Server Management Studio, o desenvolvedor e o administrador de banco de dados podem
desenvolver ou administrar quaisquer componentes do Mecanismo de Banco de Dados.

18
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Abrindo o SQL Server Management Studio
Para abrir o SQL Server Management Studio
No menu Iniciar, aponte para Todos os Programas, aponte para Microsoft SQL Server 2014 e clique
em SQL Server Management Studio.
O SQL Server Management Studio não é instalado por padrão. Se o Management Studio não estiver
disponível, instale-o executando a Instalação. O Management Studio não está disponível com o SQL
Server Express. O Management Studio Express está disponível como um download gratuito do Centro de
Download da Microsoft, mas tem uma interface de usuário diferente do que está descrito neste tutorial.
Na caixa de diálogo Conectar ao Servidor, verifique as configurações padrão e depois clique em
Conectar. Para fazer a conexão, a caixa de diálogo Nome do servidor deve conter o nome do computador
em que o SQL Server está instalado. Se o Mecanismo de Banco de Dados for uma instância nomeada, a
caixa Nome do Servidor também deverá conter o nome da instância no formato
<computer_name>\<instance_name>.

Componentes do Management Studio


O Management Studio apresenta informações em janelas dedicadas a tipos específicos de
informações. Informações de banco de dados são exibidas no Pesquisador de Objetos e janelas de
documentos.
Pesquisador de Objetos é uma exibição de árvore de todos os objetos de banco de dados em um
servidor. Isso pode incluir os bancos de dados do Mecanismo de Banco de Dados do SQL Server,
Analysis Services, Reporting Services e Integration Services. O Pesquisador de Objetos inclui
informações de todos os servidores aos quais está conectado. Ao abrir o Management Studio, você é
solicitado a conectar o Pesquisador de Objetos às configurações utilizadas na última vez. Você pode
clicar duas vezes em qualquer componente de Servidores Registrados e conectar-se a ele, mas não é
necessário registrar um servidor para fazer a conexão.
A janela de documentos é a maior parte do Management Studio. As janelas de documentos podem
conter editores de consulta e janelas de navegador. Por padrão, é exibida a página de Resumo, conectada
à instância do Mecanismo de Banco de Dados no computador atual.

Exibindo Janelas Adicionais


Para exibir a janela de Servidores Registrados
No menu Exibir, clique em Servidores Registrados.
A janela de Servidores Registrados será exibida acima do Pesquisador de Objetos. Servidores
Registrados relaciona servidores gerenciados frequentemente. Você pode adicionar ou remover
servidores dessa lista. Os únicos servidores relacionados serão as instâncias do SQL Server no
computador em que você estiver executando o Management Studio.
Se seu servidor não aparecer em Servidores Registrados, clique com o botão direito do mouse em
Mecanismo de Banco de Dados e clique em Atualizar Registro do Servidor Local.

Transact-SQL

Para muitos SQL é igual em qualquer banco de dados. E quem acha isso não está de todo errado.
Pra quem não sabe SQL é um acrônomo para Structured Query Language ou em português Linguagem
de consulta estruturada.
SQL é a linguagem utilizada nos bancos de dados e apesar de ser padronizada pela ANSI e ISO ela
possui muitas variações e cada uma com sua peculiaridade como:
PL/SQL (Oracle)
T-SQL (Microsoft)
Entre outras….
Bom mas vamos manter o foco vamos falar de T-SQL (Transaction – SQL).
T-SQL é a variação de SQL de propriedade da Microsoft e Sysbases
Não significa que a Microsoft tem um SQL só dela. T-SQL (e todas as variações de SQL) tem que
seguir todo o padrão Ansi e Iso, porém tem algumas diferenças ou funcionalidades que as outras
variações não tem e vice-versa.
A linguagem SQL, comumente referenciada como padrão SQL ANSI/ISO e administrada pelo National
Committee on Information Technology Standards (NCITS), é a linguagem universal utilizada pelos SGBDs
para acessar e manipular dados e objetos em um banco de dados. No entanto, como os fabricantes estão
livres para fazer suas próprias adaptações, esta possui vários “dialetos”. Muitos SGBDs, entre eles o SQL
Server, utilizam o mesmo tipo de dialeto como sua linguagem primária de acesso a dados. O padrão SQL

19
1678859 E-book gerado especialmente para DANIEL CRISTIAN
possui várias versões (cada uma tendo o ano em que foi adotada), e o padrão SQL-92 (adotado em 1992)
é o mais aceito entre os SGBDs.
O SQL Server possui seu próprio “dialeto” da linguagem SQL – o Transact-SQL (T-SQL). Ou seja, a
linguagem Transact-SQL segue o padrão SQL-92, mas adiciona alguns atributos (ou funcionalidades)
que a tornam exclusiva ao SQL Server. Por exemplo, a cláusula TOP em um comando SELECT só existe
no Transact-SQL. Se você pegar a instrução a seguir e executá-la no MySQL, verá que esta gerará um
erro.

SELECT TOP 10 * FROM <tabela>

Isto acontece porque a cláusula TOP só existe na linguagem Transact-SQL, que é exclusiva do SQL
Server. A instrução correspondente para o MYSQL seria:

SELECT * FROM <tabela> LIMIT 10

Você usa o T-SQL para criar, alterar e excluir objetos, bem como para inserir ou consultar dados em
um banco de dados SQL Server. Por exemplo, quando você escreve uma procedure no Query Analyzer,
você está usando a linguagem Transact-SQL. Quando você usa os comandos CREATE TABLE, CREATE
VIEW, CREATE PROCEDURE ou manipula dados em um banco de dados SQL Server usando os
comandos INSERT, SELECT, DELETE e UPDATE, você está usando a linguagem Transact-SQL.
A seguir um resumo dos principais comandos e recursos da linguagem T-SQL.

Variáveis
A T-SQL suporta variáveis e você pode usá-las para armazenar valores.

--Sintaxe:
---declaração
DECLARE @variable data-type
---Atribuição
SET @variavel='valor'
---Retornar valor
SELECT @variavel
---Exemplo
DECLARE @MeuNome varchar(20)
SET @MeuNome='Macoratti'
SELECT @MeuNome

Uma variável local Transact-SQL é um objeto que pode conter um valor de dados de um tipo específico.
As variáveis em lotes e scripts são normalmente usadas:
Como um contador, para contar o número de vezes que um loop é executado ou controlar quantas
vezes o loop é executado;

20
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Para reter um valor de dados a ser testado por uma instrução de controle de fluxo;
Para salvar um valor de dados a ser retornado por um código de retorno de procedimento armazenado
ou valor de retorno de função;
Uma instrução DECLARE inicializa uma variável Transact-SQL por:
Atribuição de um nome. (O nome deve ter uma única @ como o primeiro caractere.)
Atribuição de um tipo de dados fornecido por sistema ou definido pelo usuário e um tamanho. Para
variáveis numéricas, precisão e escala também são atribuídas. Para variáveis do tipo XML, uma coleção
de esquema opcional pode ser atribuída.
Definição do valor como NULL;
Exemplo: DECLARE @Contador int;
Quando uma variável é primeiramente declarada, seu valor é definido como NULL.
Para atribuir um valor à uma variável, use a instrução SET.
Este é o método preferido de atribuir um valor a uma variável. Uma variável também pode ter um valor
atribuído sendo referenciado na lista selecionada de uma instrução SELECT.

O comando GO sinaliza o final de um lote de instruções Transact-SQL para os utilitários do SQL


Server.
Sintaxe: GO [contador]
Onde:
Contador - É um número inteiro positivo. O lote que precede GO será executado pelo número de vezes
especificado.
GO não é uma instrução Transact-SQL; é um comando reconhecido pelos utilitários sqlcmd e osql e
pelo Editor de códigos SQL Server Management Studio.
GO é um comando de utilitário que não exige nenhuma permissão. Pode ser executado por qualquer
usuário.
Uma instrução Transact-SQL não pode ocupar a mesma linha que um comando GO.

IF...ELSE é um controle condicional de fluxo básico usado na T-SQL.


Impõe condições na execução de uma instrução Transact-SQL. A instrução que segue uma palavra-
chave IF e sua condição será executada se a condição for satisfeita: a expressão Booleana retorna TRUE.
A palavra-chave opcional ELSE introduz outra instrução que será executada quando a condição IF não
for satisfeita: a expressão Booleana retorna FALSE.
Sintaxe:
IF Boolean_expression
{ sql_statement | statement_block }
[ ELSE
{ sql_statement | statement_block } ]

21
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Uma construção IF...ELSE pode ser usada em lotes, em procedimentos armazenados e em consultas
ad hoc. Quando essa construção é usada em um procedimento armazenado, ela normalmente é usada
para testar a existência de algum parâmetro.

BEGIN...END engloba uma série de instruções Transact-SQL de modo que um grupo de instruções
possa ser executado.
BEGIN e END são palavras-chave da linguagem de controle de fluxo.
Sintaxe:
BEGIN
{
sql_statement | statement_block
}
END

Os blocos BEGIN...END podem ser aninhados.


Embora todas as instruções Transact-SQL sejam válidas em um bloco BEGIN...END, certas instruções
não devem ser agrupadas no mesmo lote ou bloco de instrução.

A instrução WHILE repete uma instrução ou bloco de instruções desde que a condição especificada
permaneça verdadeira.
Ela define uma condição para a execução repetida de uma instrução ou um bloco de instruções SQL.
As instruções serão executadas repetidamente desde que a condição especificada seja verdadeira.
A execução de instruções no loop WHILE pode ser controlada internamente ao loop com as palavras-
chave BREAK e CONTINUE.

WHILE Boolean_expression
{ sql_statement | statement_block |
BREAK | CONTINUE }

Argumentos

Boolean_expression
É uma expressão que retorna TRUE ou FALSE. Se a expressão booleana contiver uma instrução
SELECT, a instrução SELECT deverá ser incluída entre parênteses.
{sql_statement | statement_block}
É qualquer instrução Transact-SQL ou agrupamento de instruções, conforme definido com um bloco
de instruções. Para definir um bloco de instruções, use as palavras-chave BEGIN e END de controle de
fluxo.

22
1678859 E-book gerado especialmente para DANIEL CRISTIAN
BREAK
Provoca uma saída do loop WHILE mais interno. Todas as instruções que apareçam depois da palavra-
chave END, que marca o final do loop, serão executadas.

CONTINUE
Faz com que o loop WHILE seja reiniciado, ignorando todas as instruções depois da palavra-chave
CONTINUE

Se dois ou mais loops WHILE estiverem aninhados, o BREAK interno será encerrado para o próximo
loop mais externo. Todas as instruções após o fim da primeira execução do loop interno e o loop mais
externo seguinte serão reiniciadas.
Exemplos:
1- Loop WHILE

2- Loop WHILE usando BREAK

23
1678859 E-book gerado especialmente para DANIEL CRISTIAN
2- Loop WHILE usando BREAK e CONTINUE

Dessa forma um programa poderá executar uma instrução BREAK se, por exemplo, não houver outras
linhas a serem processadas. Uma restrição CONTINUE pode ser executada se, por exemplo, for
necessário prosseguir com a execução do código.
A expressão CASE é usada para avaliar várias condições e retornar um valor único para cada
condição. Por exemplo, ela permite mostrar um valor alternativo a ser exibido dependendo do valor de
uma coluna. Essa alteração nos dados é temporária. Portanto, não há nenhuma alteração permanente
nos dados.
A expressão CASE consiste em:
A palavra-chave CASE.
O nome da coluna a ser transformada.
As cláusulas WHEN que especificam as expressões a serem pesquisadas e as cláusulasTHEN que
especificam as expressões pelas quais substituí-las.
Uma cláusula ELSE opcional que define a expressão retornada, se nenhuma operação de comparação
for avaliada como TRUE.
A palavra-chave END.
Uma cláusula AS opcional que define um alias para a expressão CASE.
Um uso comum da expressão CASE é substituir códigos ou abreviações por valores mais legíveis
Exemplo:
O exemplo seguinte usa a função CASE para alterar a exibição de categorias dos produtos para torná-
las mais compreensíveis.

24
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A expressão CASE tem dois formatos:
A expressão CASE simples compara uma expressão com um conjunto de expressões simples para
determinar o resultado.
A expressão CASE pesquisada avalia um conjunto de expressões booleanas para determinar o
resultado.
Os dois formatos dão suporte a um argumento ELSE opcional.
CASE pode ser usada em qualquer instrução ou cláusula que permita uma expressão
válida.(Select,Update,Delete,etc.)
Uma transação (Transaction) é uma única unidade de trabalho. Se uma transação tiver êxito, todas
as modificações de dados feitas durante a transação estarão confirmadas e se tornarão parte permanente
do banco de dados. Se uma transação encontrar erros e precisar ser cancelada ou revertida, todas as
modificações de dados serão apagadas.
SQL Server opera nos modos de transação a seguir.
Transações de confirmação automática : Cada instrução individual é uma transação.
Transações explícitas : Cada transação é iniciada explicitamente com a instrução BEGIN
TRANSACTION e finalizada explicitamente com uma instrução COMMIT ou ROLLBACK.
Transações implícitas : Uma transação nova é iniciada implicitamente quando a transação anterior é
concluída, mas cada transação é explicitamente concluída com uma instrução COMMIT ou ROLLBACK.
Transações de escopo de lote : Aplicável apenas a MARS (Conjuntos de Resultados Ativos Múltiplos),
uma transação Transact-SQL explícita ou implícita iniciada em uma sessão MARS se torna uma
transação de escopo de lote. Uma transação de escopo de lote não confirmada ou revertida, quando um
lote é concluído, é revertida automaticamente pelo SQL Server
A característica de uma transação e sua diferença para uma execução em lote é que ela pode desfazer
todas as operações realizada desde o início da transação usando a instruçãoROLLBACK
TRANSACTION.
Para indicar o início de uma transação usamos a instrução BEGIN TRANSACTION OU BEGIN TRAN
Esta instrução marca o ponto inicial de uma transação local explícita. BEGIN TRANSACTION
incrementa @@TRANCOUNT em 1.
Para confirmar a execução das operações realizadas na transação usamos a instrução COMMIT
TRANSACTION OU COMMIT TRAN
Esta instrução marca o término de uma transação implícita ou explícita que teve êxito.
Se@@TRANCOUNT for igual a 1, COMMIT TRANSACTION transformará todas as modificações de
dados executadas desde o início da transação em parte permanente do banco de dados, liberará os
recursos ocupados pela transação e decrementará @@TRANCOUNT para 0. Se@@TRANCOUNT for
maior que 1, COMMIT TRANSACTION decrementará @@TRANCOUNT apenas de 1 e a transação
continuará ativa.
Exemplo:

O exemplo acima abre o banco de dados Agenda e inicia uma transação -BEGIN TRAN

25
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A seguir atualiza a coluna nome para 'Macorati' quando o Id for igual a 1
Depois atualiza a coluna para 'Jose Carlos Macoratti' quando o Id for igual a 1
Ao final confirmamos as operações e encerramos a transação atualizando o banco de dados
comCOMMIT TRAN
Para desfazer as operações realizadas desde o início da transação usamos a instrução ROLLBACK
TRANSACTION OU ROLLBACK TRAN.
Esta instrução reverte uma transação explícita ou implícita ao começo da transação ou a um ponto de
salvamento dentro da transação. Você pode usar ROLLBACK TRANSACTION para apagar todas as
modificações de dados feitas desde o começo da transação ou até um ponto de salvamento. Ela também
libera recursos mantidos pela transação.
Exemplo:

Suponha que, por qualquer motivo, a segunda instrução de atualização deve atualizar exatamente
cinco linhas.
Se @@ROWCOUNT, que controla o número de linhas afetadas por cada instrução, for igual a cinco,
a transação será confirmada caso contrário ele será cancelada.
A declaração ROLLBACK TRAN "desfaz" todo o trabalho desde a correspondente instruçãoBEGIN
TRAN. Ela não vai executar qualquer instrução de atualização.
Note que o Query Analyzer irá mostrar-lhe mensagens indicando que linhas foram atualizadas, mas
você pode consultar o banco de dados para verificar se as modificações realmente ocorreram.
Uma instrução ROLLBACK TRANSACTION não produz nenhuma mensagem para o usuário. Se forem
necessários avisos em procedimentos armazenados ou Triggers, use as instruçõesRAISERROR ou
PRINT. RAISERROR é a instrução preferida para indicar erros.
Instruções ROLLBACK TRANSACTION em procedimentos armazenados não afetam instruções
subsequentes no lote que chamou o procedimento; instruções subsequentes no lote são executadas.
Instruções ROLLBACK TRANSACTION em Triggers finalizam o lote contendo a instrução que ativou
o trigger; instruções subsequentes no lote são executadas.

Procedimento armazenado ou Stored Procedure é uma coleção de comandos SQL executados


contra um banco de dados que encapsula tarefas repetitivas, aceita parâmetros de entrada e retorna um
valor de status (para indicar aceitação ou falha na execução).
O procedimento armazenado pode reduzir o tráfego na rede, visto que os comandos são executados
diretamente no servidor. Além de melhorar a performance, criar mecanismos de segurança entre a
manipulação dos dados do Banco de Dados.
Para criar uma Stored Procedure usamos a instrução : CREATE PROCEDURE que cria um
procedimento armazenado Transact-SQL ou CLR (Common Language Runtime) no SQL Server.

26
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Assim Procedimentos armazenados são semelhantes a procedimentos em outras linguagens de
programação no sentido de que podem:
Aceitar parâmetros de entrada e retornar vários valores no formulário de parâmetros de saída para o
procedimento de chamada ou lote.
Conter instruções de programação que executam operações no banco de dados, inclusive chamar
outros procedimentos.
Retornar um valor de status a um procedimento de chamada ou lote para indicar êxito ou falha (e o
motivo da falha).
Quando um procedimento é executado pela primeira vez, ele é compilado para determinar um plano
de acesso ideal para recuperar os dados. As execuções subsequentes do procedimento poderão reutilizar
o plano já gerado se ele ainda estiver no cache de planos do Mecanismo de Banco de Dados.
Um ou mais procedimentos podem ser executados automaticamente quando o SQL Server é iniciado.
Os procedimentos devem ser criados pelo administrador do sistema no banco de dados mestre e
executados com função de servidor sysadmin como um processo de segundo plano.
A instrução CREATE PROCEDURE não pode ser combinada com outras instruções Transact-SQL em
um único lote.
Exemplo:
A seguir temos um exemplo de como criar stored procedures ou procedimentos armazenados no SQL
Server usando o banco de dados Clientes.mdf e a tabela Contatos.
Abra o DataBase Explorer e após expandir os objetos do banco de dados Clientes.mdf clique com o
botão direito do mouse sobre o objeto Stored Procedures e selecione a opção Add New Stored Procedure:

No editor do SQL Server vamos criar a stored procedure exibeContatos que irá retornar todos os
contatos cadastrados na tabela Contatos.

Digite o comando conforme acima e clique no botão Save para salvar a stored procedure no banco de
dados;
A instrução SQL usada é: SELECT * from Contatos
Repita o procedimento agora para criar a stored procedure SelecionaContatosPorIdade onde iremos
retornar todos os contatos com idade superior a uma idade definida.

Observe que criarmos o parâmetro idade do tipo int para ser usado na cláusula WHERE.
O comando SQL usado é:

27
1678859 E-book gerado especialmente para DANIEL CRISTIAN
SELECT * from Contatos WHERE idade > @ idade

Novamente repita o processo para criar a stored procedure selecionaContatosPorCidade que irá
retornar os contados para uma determinada cidade;

Observe que criarmos o parâmetro cidade do tipo varchar para ser usado na cláusula WHERE.
O comando SQL usado é:
SELECT * from Contatos WHERE cidade = @ cidade
Ao final teremos as stored procedures criadas no banco de dados e prontas para uso conforme a figura
abaixo:

Criando um Banco de Dados


Antes de começar:

- Limitações e Restrições
No máximo 32.767 bancos de dados podem ser especificados em uma instância do SQL Server.

- Pré-requisitos
A instrução CREATE DATABASE deve ser executada em modo de confirmação automática (o modo
padrão de gerenciamento de transações) e não é permitida em uma transação explícita ou implícita.

- Recomendações
O backup do banco de dados mestre deve ser feito sempre que um banco de dados de usuário for
criado, modificado ou descartado.
Ao criar um banco de dados, torne os arquivos de dados tão grandes quanto possível, com base na
quantidade máxima de dados que você espera ter no banco de dados.

- Segurança
Permissões
Requer a permissão CREATE DATABASE no banco de dados mestre, ou requer a permissão CREATE
ANY DATABASE ou ALTER ANY DATABASE.
Para manter controle sobre o uso do disco em uma instância do SQL Server, a permissão para criar
bancos de dados geralmente é limitada a algumas contas de logon.

- Usando o SQL Server Management Studio

28
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Para criar um banco de dados
No Pesquisador de Objetos, conecte-se a uma instância do Mecanismo de Banco de Dados do SQL
Server e expanda-a.
Clique com o botão direito do mouse em Bancos de Dados e clique em Novo Banco de Dados.
Em Novo Banco de Dados, digite um nome de banco de dados.
Para criar o banco de dados aceitando todos os valores padrão, clique em OK; do contrário, passe
para as etapas opcionais a seguir.
Para alterar o nome do proprietário, clique em (…) para selecionar outro proprietário.
System_CAPS_ICON_note.jpg
Observação: a opção Usar indexação de texto completo sempre está marcada e esmaecida porque,
a partir do SQL Server 2008, todos os bancos de dados de usuários são habilitados para texto completo.
Para alterar os valores padrão dos arquivos de dados primários e de log de transação, na grade
Arquivos de banco de dados , clique na célula apropriada e digite o novo valor. Para obter mais
informações, consulte Add Data or Log Files to a Database.
Para alterar o agrupamento do banco de dados, selecione a página Opções e depois marque um
agrupamento na lista.
Para alterar o modelo de recuperação, selecione a página Opções e marque um modelo de
recuperação na lista.
Para alterar opções de banco de dados, selecione a página Opções e depois modifique as opções de
banco de dados. Para obter uma descrição de cada opção, consulte Opções ALTER DATABASE SET
(Transact-SQL).
Para adicionar um novo grupo de arquivos, clique na página Grupos de Arquivos . Clique em Adicionar
e, em seguida, digite os valores para o grupo de arquivos.
Para adicionar uma propriedade estendida ao banco de dados, selecione a página Propriedades
Estendidas .
Na coluna Nome , digite um nome para a propriedade estendida.
Na coluna Valor , digite o texto da propriedade estendida. Por exemplo, digite uma ou mais instruções
que descrevem o banco de dados.
Para criar o banco de dados, clique em OK.

- Usando Transact-SQL
Para criar um banco de dados
Conecte-se ao Mecanismo de Banco de Dados.
Na barra Padrão, clique em Nova Consulta.
Copie e cole o exemplo a seguir na janela de consulta e clique em Executar. Este exemplo cria o banco
de dados Sales. Como a palavra-chave PRIMARY não é usada, o primeiro arquivo (Sales_dat) torna-se
o arquivo primário. Como nem MB nem KB é especificado no parâmetro SIZE do arquivo Sales_dat , ele
usa MB e é alocado em megabytes. O backup do banco de dados Sales_log é alocado em megabytes
porque o sufixo MB é explicitamente declarado no parâmetro SIZE .
Transact-SQL
USE master ;
GO
CREATE DATABASE Sales
ON
( NAME = Sales_dat,
FILENAME = 'C:\Program Files\Microsoft SQL
Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\saledat.mdf',
SIZE = 10,
MAXSIZE = 50,
FILEGROWTH = 5 )
LOG ON
( NAME = Sales_log,
FILENAME = 'C:\Program Files\Microsoft SQL
Server\MSSQL13.MSSQLSERVER\MSSQL\DATA\salelog.ldf',
SIZE = 5MB,
MAXSIZE = 25MB,
FILEGROWTH = 5MB ) ;
GO

29
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Excluir Banco de dados

- Antes de começar
Limitações e restrições.
Bancos de dados de sistema não podem ser excluídos.

- Pré-requisitos
Exclua todos os instantâneos do banco de dados que existam no banco de dados. Para obter mais
informações, veja Remover um instantâneo de banco de dados (Transact-SQL).
Se o banco de dados estiver envolvido em envio de logs, remova o envio do logs.
Se o banco de dados for publicado para replicação transacional, publicado ou com assinatura para
replicação de mesclagem, remova a replicação do banco de dados.

Recomendações
Pense em fazer um backup completo do banco de dados. Um banco de dados excluído só poderá ser
recriado por meio da restauração de um backup.

Segurança
Permissões.
Para executar DROP DATABASE, a um mínimo, um usuário deve ter permissão CONTROL no banco
de dados.

- Usando o SQL Server Management Studio


Para excluir um banco de dados
No Pesquisador de Objetos, conecte-se a uma instância do Mecanismo de Banco de Dados do SQL
Server e expanda-a.
Expanda Bancos de Dados, clique com o botão direito do mouse no banco de dados para excluí-lo e
depois clique em Excluir.
Confirme se o banco de dados correto está selecionado e depois clique em OK.

- Usando Transact-SQL
Para excluir um banco de dados
Conecte-se ao Mecanismo de Banco de Dados.
Na barra Padrão, clique em Nova Consulta.
Copie e cole o exemplo a seguir na janela de consulta e clique em Executar. O exemplo remove os
bancos de dados Sales e NewSales .
Transact-SQL
USE master ;
GO
DROP DATABASE Sales, NewSales ;
GO

Acompanhamento: depois de excluir um banco de dados


Faça backup do banco de dados mestre. Se o mestre precisar ser restaurado, todos os bancos de
dados que tiverem sido excluídos desde o último backup do mestre ainda terão referências nas exibições
do catálogo do sistema e poderão gerar mensagens de erro.

Excluir Arquivos de Dados ou de Log de um Banco de Dados

- Usando o SQL Server Management Studio


Para excluir arquivos de dados ou de log de um banco de dados
No Pesquisador de Objetos, conecte-se a uma instância do Mecanismo de Banco de Dados do SQL
Server e expanda-a.
Expanda Bancos de Dados, clique com o botão direito do mouse no banco de dados do qual deseja
excluir o arquivo e depois clique em Propriedades.
Selecione a página Arquivos .
Na grade Arquivos de bancos de dados , selecione o arquivo a ser excluído e, depois, clique em
Remover.
Clique em OK.

30
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Usando Transact-SQL
Para excluir arquivos de dados ou de log de um banco de dados
Conecte-se ao Mecanismo de Banco de Dados.
Na barra Padrão, clique em Nova Consulta.
Copie e cole o exemplo a seguir na janela de consulta e clique em Executar. Este exemplo remove o
arquivo test1dat4.
Transact-SQL
USE master;
GO
ALTER DATABASE AdventureWorks2012
REMOVE FILE test1dat4;
GO

Conceitos de banco de dados e sistemas gerenciadores de bancos de dados


(SGBD);

BANCO DE DADOS7

Um banco de dados é uma ferramenta para coletar e organizar informações. Os bancos de dados
podem armazenar informações sobre pessoas, produtos, pedidos ou qualquer outra coisa. Muitos bancos
de dados começam como uma lista em um programa de processamento de texto ou planilha. Conforme
a lista cresce, começam a aparecer redundâncias e inconsistências nos dados. Os dados se tornam
difíceis de entender no formato de lista, e há limitações nas maneiras de pesquisar ou puxar subconjuntos
de dados para analisar. Quando esses problemas começam a aparecer, é conveniente transferir os dados
para um banco de dados criado por um sistema de gerenciamento de banco de dados (SGBD).
Um banco de dados pode ser formado por um conjunto de tabelas, que estejam relacionadas entre si.
Um banco de dados pode conter mais de uma tabela. Por exemplo, um sistema de rastreamento de
inventário que usa três tabelas não é três bancos de dados, mas um banco de dados que contém três
tabelas. A menos que ele tenha sido criado especificamente para usar dados ou código de outra fonte.

Tipos de SGBD8

Os Sistemas de Gerenciamento de Banco de Dados – SGBD são softwares que proporcionam


armazenamento e recuperação de dados com segurança. É um conjunto de ferramentas que facilitam as
operações e os resultados com segurança.
São diversos os fornecedores de SGBD. Entre os mais conhecidos estão o Oracle, o SQL Server, o
MySQL, o PostGre, Microsoft Access (para projetos mais simples) e muitos outros.
Para a classificação dos SGBD são utilizados três principais critérios:

O Modelo de Dados
O Modelo de dados é basicamente um conjunto de conceitos utilizados para descrever um banco de
dados, existem diversas formas de representação dos dados, porém, qualquer forma que permita a
correta compreensão das estruturas de dados compreendidas no banco de dados, pode ser considerada
adequada.
Atualmente os modelos mais utilizados na maioria dos SGBD comerciais são o modelo de dados
relacional, e o modelo de dados de objetos. Os SGBD relacionais vêm evoluindo constantemente, em
particular, vêm incorporando muitos dos conceitos que foram desenvolvidos nos modelos de banco de
dados de objetos. Essa evolução criou uma nova classe de SGBD conhecidos como SGBD relacional-
objeto.

O Número de Usuários
O número de usuários suportado pelo sistema é o segundo critério utilizado para classificar os SGBD.
São conhecidos como SGBD monousuários os sistemas que suportam apenas um usuário de cada vez

7
https://support.office.com/pt-br/article/No%C3%A7%C3%B5es-b%C3%A1sicas-do-banco-de-dados-a849ac16-07c7-4a31-9948-
3c8c94a7c204?CorrelationId=14eb69b5-b34a-4da4-8879-83b9b2d15ed2&ui=pt-BR&rs=pt-BR&ad=BR
8
https://sites.google.com/site/fkbancodedados1/sistemadebancodados/sgbd---sistemas-gerenciadores-de-banco-dados

31
1678859 E-book gerado especialmente para DANIEL CRISTIAN
(sistemas raros e usados em computadores pessoais). Quando o sistema suporta múltiplos usuários de
forma concorrente (maioria dos sistemas) são conhecidos como SGBD multiusuário.

O Número de Servidores
O terceiro critério de classificação dos SGBD, se refere ao número de servidores em que o banco de
dados está distribuído. Denominamos como SGBD centralizado se os dados estão armazenados em um
único servidor, em um único computador. Apesar do banco de dados está em um único banco de dados
em um único computador, os SGBD centralizados suportam diversos usuários concorrentemente.

São classificados como SGBD distribuídos os sistemas de banco de dados cujo, o banco de dados e
o software de gerencia de banco de dados estão distribuídos através de diversos servidores conectados
por uma rede.

Composição de um Banco de Dados

Tabelas
Uma tabela de banco de dados é semelhante na aparência a uma planilha, pois os dados são
armazenados em linhas e colunas. Como resultado, geralmente é bem fácil importar uma planilha em
uma tabela de banco de dados. A principal diferença entre armazenar os dados em uma planilha e
armazená-los em um banco de dados é a maneira como os dados são organizados.
Para obter maior flexibilidade de um banco de dados, os dados precisam ser organizados em tabelas
de forma que não ocorram redundâncias. Por exemplo, se você armazenar informações sobre
funcionários, cada funcionário deverá ser inserido apenas uma vez em uma tabela que esteja configurada
apenas para manter dados de funcionários. Os dados sobre produtos serão armazenados em suas
próprias tabelas, e os dados sobre as filiais serão armazenados em outra tabela. Esse processo é
chamado normalização.

Registros
Cada linha em uma tabela é denominada um registro. Os registros são os locais onde os itens
individuais de informações são armazenados. Cada registro consiste em um ou mais campos.
Outra maneira de descrever registros e campos é visualizar um catálogo de cartões de biblioteca no
estilo antigo. Cada cartão do arquivo corresponde a um registro no banco de dados. Cada informação em
um cartão individual (autor, título e assim por diante) corresponde a um campo no banco de dados.

Campos
Os campos correspondem às colunas da tabela. Por exemplo, você pode ter uma tabela denominada
"Funcionários" em que cada registro (linha) contém informações sobre um funcionário diferente, e cada
campo (coluna) contém um tipo de informação diferente, como o nome, sobrenome, endereço e assim
por diante. Os campos devem ser designados com um determinado tipo de dados, seja texto, data ou
hora, número ou algum outro tipo.

Tipos de Dados Mais Comuns em SGBD

Tipo de dados Armazena


Caracteres alfanuméricos
Texto Use para texto, ou texto e números que não são usados em cálculos (por exemplo,
a identificação de um produto).
Valores numéricos (valores inteiros ou fracionários).
Número Use para armazenar números a serem usados em cálculos, exceto valores
monetários (use Moeda como tipo de dados para valores monetários).
Apenas valores numéricos inteiros. (Muito usado em campos que são chave
Inteiro
primária com ou sem numeração automática).
Datas e horas.
Data/Hora Use para armazenar valores de data/hora. Observe que cada valor armazenado
inclui um componente de data e um componente de hora.
Moeda Valores monetários.

32
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Tipo de dados Armazena
Use para armazenar valores monetários (moeda).
Valores Boolean.
Sim/Não Use para campos Verdadeiro/Falso, que podem conter um de dois valores
possíveis: Sim/Não ou Verdadeiro/Falso, por exemplo.

As Chaves dos Bancos de Dados

O conceito básico de chave de um BD é que é uma ou mais colunas que distinguem uma linha das
demais dentro de uma tabela, sendo esta chamada de chave primária (PK - Primary Key) ou para
relacionar com outra tabela, chamada de chave estrangeira (FK - Foreign Key). Essas chaves é que
determinam a unicidade de cada registro dentro de uma tabela9.
Esta unicidade dos registros, determinada pela chave, é fundamental para a criação de índices.

Chave Primária10
Uma chave primária consiste num campo ou conjunto de campos da tabela que fornece ao SGBD um
identificador exclusivo para cada linha. Numa base de dados relacional, as informações são divididas em
tabelas baseadas em assuntos separados. Em seguida, as relações de tabela e as chaves primárias são
utilizadas para indicar ao SGBD como associar novamente as informações. Os SGBD utiliza os campos
de chave primária para associar rapidamente os dados a partir de várias tabelas e combinar os dados de
forma lógica.
Este método funciona, tendo em conta que, uma vez definida a chave primária, é possível utilizá-la em
outras tabelas para voltar a fazer referência à tabela utilizando a chave primária. Por exemplo, um campo
ID do Cliente na tabela Clientes também poderá ser apresentado na tabela Encomendas. Na tabela
Clientes, trata-se da chave primária. Na tabela Encomendas, denomina-se chave externa. Uma chave
externa, simplesmente indicada, trata-se da chave primária de outra tabela.

1- Chave primária e 2- chave estrangeira.

Frequentemente, um número de identificação exclusivo, como, por exemplo, um número de ID ou um


número de série ou código, funciona como chave primária numa tabela. Por exemplo, poderá ter uma
tabela Clientes, na qual cada cliente tenha um número de ID de cliente exclusivo. O campo de ID de
cliente corresponde à chave primária.
Um bom candidato a chave primária apresenta várias características. Em primeiro lugar, identifica de
forma exclusiva cada linha. Em segundo lugar, nunca está vazio nem é nulo: contém sempre um valor.
Em terceiro lugar, raramente é alterado (idealmente, nunca). O SGBD utiliza os campos de chave primária
para associar rapidamente dos dados provenientes de várias tabelas.
Um exemplo de uma escolha pouco adequada para uma chave primária seria um nome ou endereço.
Ambos contêm informações que poderiam ser alteradas com o decorrer do tempo.
Deverá especificar sempre uma chave primária para uma tabela. O SGBD cria automaticamente um
índice para a chave primária, que permite ajudar a tornar mais rápidas as consultas, bem como outras
9
https://www.diegomacedo.com.br/entendendo-as-chaves-dos-bancos-de-dados/
10
https://support.office.com/pt-pt/article/Adicionar-definir-alterar-ou-remover-a-chave-prim%C3%A1ria-07b4a84b-0063-4d56-8b00-65f2975e4379?ui=pt-
PT&rs=pt-PT&ad=PT

33
1678859 E-book gerado especialmente para DANIEL CRISTIAN
operações. Os SGBD também asseguram, que cada registro apresenta um valor no campo de chave
primária e que é sempre exclusivo.
Se não souber qual o campo ou conjunto de campos que possa constituir uma chave primária
adequada, considere a utilização de uma coluna com o tipo de dados Numeração Automática. Este tipo
de identificador não inclui fatos (não contém quaisquer informações fatuais que descrevam a linha que
representa). É recomendado utilizar identificadores que não incluam fatos, uma vez que os respectivos
valores não são alterados. Uma chave primária que contenha fatos sobre uma linha (por exemplo, um
número de telefone ou o nome de um cliente) tem maior probabilidade de ser alterada, uma vez que as
próprias informações fatuais podem ser alteradas.

Exemplo de chave primária na tabela produtos.

1. Frequentemente, uma coluna com o tipo de dados Numeração Automática constitui uma chave
primária adequada, uma vez que permite assegurar que não existem dois IDs de Produto idênticos.
Em determinados casos, poderá pretender utilizar dois ou mais campos que, em conjunto, forneçam a
chave primária de uma tabela. Por exemplo, uma tabela Detalhes da Encomenda que armazene itens de
linha para encomendas utilizaria duas colunas na respectiva chave primária: ID da Encomenda e ID do
Produto. Quando uma chave primária emprega mais do que uma coluna, também é designada por chave
composta.

Chave Estrangeira
A chave estrangeira é uma chave primária de outra tabela.

Exemplo de chave estrangeira (note que a chave estrangeira é uma chave primária em outra tabela).

Quando duas ou mais tabelas estão relacionadas há campos comuns entre elas, este campo é definido
como chave primária quando ele define o identificador exclusivo de uma tabela, e chave estrangeira
quando o campo traz informações que estão armazenadas em outra tabela. Há casos em que uma chave
primária também pode ser uma chave estrangeira (ao mesmo tempo), isso ocorre quando há um
relacionamento N:N.
A coluna Código do Fornecedor da tabela Produtos é uma chave estrangeira porque é também a chave
primária da tabela Fornecedores.

Relacionamento11

O que são Relações Tabela?


Em um banco de dados relacional, as relações permitem que você evite dados redundantes. Por
exemplo, se você estiver desenvolvendo um banco de dados que irá rastrear informações sobre livros,
será necessário ter uma tabela chamada Títulos, que irá armazenar as informações sobre cada livro, tais
como o título do livro, a data de publicação e o editor. Também há informações sobre o editor que talvez
você deseje armazenar, tais como o número de telefone, o endereço e o código postal do editor. Se você
quisesse armazenar todas essas informações na tabela de títulos, o número de telefone do editor seria
duplicado para cada título que ele publicasse.

11
https://support.microsoft.com/en-us/kb/304466/pt-br

34
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A melhor solução é armazenar as informações do editor somente uma vez em uma tabela separada,
Editores. Assim, você poderá colocar um ponteiro na tabela Títulos para referir a uma entrada na tabela
Editores.
Para certificar-se de que seus dados não estão fora de sincronismo, é possível reforçar a integridade
referencial entre as tabelas Títulos e Editores. As relações de integridade referencial ajudam a garantir
que as informações em uma tabela correspondem às informações em outra tabela. Por exemplo, cada
título na tabela Títulos deve estar associado a um editor específico na tabela Editores. Um título não pode
ser adicionado ao banco de dados para um editor que não existe no banco de dados.
Um dos objetivos de um bom design de banco de dados é remover a redundância de dados (dados
duplicados). Para alcançar esse objetivo, divida os dados em várias tabelas baseadas em tópicos para
que cada fato seja representado apenas uma vez. Depois, forneça ao SGBD os meios para reunir as
informações divididas — faça isso colocando campos em comum em tabelas que sejam relacionadas.
Para realizar essa etapa corretamente, entretanto, você deve primeiro compreender as relações entre as
tabelas e depois especificar essas relações no banco de dados.
Depois de criar uma tabela para cada assunto em seu banco de dados, você deverá fornecer ao SGBD
os meios pelos quais ele reunirá novamente essas informações quando for necessário. Isso é feito
colocando-se campos em comum nas tabelas que são relacionadas e definindo-se relações entre as
tabelas. Em seguida, você pode criar consultas, formulários e relatórios que exibam informações de várias
tabelas de uma só vez. Por exemplo, o formulário mostrado aqui inclui informações retiradas de várias
tabelas:

Tela de pedidos exibindo de uma só vez informações de quatro tabelas.

1. As informações desse formulário são originárias da tabela Clientes...


2. ...da tabela Pedidos...
3. ...da tabela Produtos...
4. ...e da tabela Detalhes do Pedido.

O nome do cliente na caixa Cobrar é recuperado da tabela Clientes; os valores de Código do Pedido
e Data do Pedido vêm da tabela Pedidos; o nome do Produto vem da tabela Produtos; os valores de
Preço Unitário e Quantidade vêm da tabela Detalhes do Pedido. Essas tabelas são vinculadas umas às
outras de várias formas para trazer as informações de todas para o formulário.
No exemplo anterior, os campos das tabelas precisam estar coordenados de forma que mostrem
informações sobre o mesmo pedido. Tal coordenação é realizada usando-se relações de tabelas. Uma
relação de tabela funciona pela correspondência de dados em campos chave - geralmente um campo
com o mesmo nome em ambas as tabelas. Na maioria dos casos, esses campos correspondentes são a
chave primária de uma tabela, que fornece um identificador exclusivo para cada registro, e uma chave
estrangeira da outra tabela. Pode-se, por exemplo, associar funcionários aos pedidos pelos quais são
responsáveis criando-se uma relação de tabela entre os campos CódigoDoFuncionário nas tabelas
Funcionários e Pedidos.

35
1678859 E-book gerado especialmente para DANIEL CRISTIAN
IDFuncionario usado como chave primária na tabela Funcionários e como chave estrangeira na tabela pedidos.

1. CódigoDoFuncionário aparece em ambas as tabelas — como uma chave primária ...


2. ...e como uma chave estrangeira.

Tipos de Relações Tabela


Uma relação trabalha correspondendo dados nas colunas chave, geralmente as colunas com o mesmo
nome em ambas as tabelas. Na maioria dos casos, as relações correspondem a chave primária de uma
tabela, que fornece um identificador exclusivo para cada linha, à entrada na chave estrangeira da outra
tabela. Por exemplo, as vendas podem ser associadas aos títulos específicos vendidos criando uma
relação entre a coluna título_id na tabela Títulos (a chave primária) e a coluna título_id na tabela Vendas
(a chave estrangeira).
Há três tipos de relações de tabela:

Uma Relação Um-para-Muitos


Considere um banco de dados de controle de pedidos que inclua uma tabela Clientes e uma tabela
Pedidos. Um cliente pode fazer qualquer número de pedidos. Assim, para qualquer cliente representado
na tabela Clientes, pode haver vários pedidos representados na tabela Pedidos. A relação entre a tabela
Clientes e a tabela Pedidos é, portanto, uma relação um-para-muitos.
Para representar uma relação um-para-muitos em um design de banco de dados, tome a chave
primária do lado "um" da relação e adicione-a como um campo ou campos adicionais à tabela no lado
"muitos" da relação. Nesse caso, por exemplo, adicione um novo campo — o campo Código da tabela
Clientes — à tabela Pedidos e nomeie-o como Código do Cliente. O SGBD poderá então usar o número
do Código do Cliente na tabela Pedidos para localizar o cliente correto para cada pedido.

Uma Relação Muitos-para-Muitos


Considere a relação entre uma tabela Produtos e uma tabela Pedidos. Um único pedido pode incluir
mais de um produto. Por outro lado, um único produto pode constar em vários pedidos. Assim, para todos
os registros da tabela Pedidos, pode haver vários registros na tabela Produtos. Além disso, para cada
registro na tabela Produtos, pode haver muitos registros na tabela Pedidos. Esse tipo de relação é
denominado relação muitos-para-muitos porque com relação a todos os produtos pode haver vários
pedidos, e para todos os pedidos pode haver vários produtos. Observe que para detectar relações muitos-
para-muitos existentes entre as tabelas é importante considerar ambos os lados da relação.
Para representar uma relação muitos-para-muitos, você deve criar uma terceira tabela, geralmente
chamada de tabela de associação, que divide a relação muitos-para-muitos em duas relações um-para-
muitos. Insira a chave primária de cada uma das duas tabelas na terceira tabela. Como resultado, a
terceira tabela registra cada ocorrência, ou instância, da relação. Por exemplo, a tabela Pedidos e a tabela
Produtos possuem uma relação muitos-para-muitos que é definida criando-se duas relações um-para-
muitos com a tabela Detalhes do Pedido. Um pedido pode ter muitos produtos, e cada produto pode
aparecer em muitos pedidos.

Uma Relação Um-para-Um


Em uma relação um-para-um, cada registro na primeira tabela pode ter somente um registro
correspondente na segunda tabela, e cada registro na segunda tabela pode ter somente um registro
correspondente na primeira tabela. Esse tipo de relação não é comum porque, geralmente, as
informações relacionadas dessa maneira são armazenadas na mesma tabela. A utilização de uma relação
um-para-um é recomendada para dividir uma tabela com muitos campos, isolar parte de uma tabela por
motivos de segurança ou armazenar informações que se apliquem somente a um subconjunto da tabela
principal. Quando você identificar esse tipo de relação, ambas as tabelas devem compartilhar um campo
em comum.

36
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Por que Criar Relações de Tabela?
Você pode criar relações de tabela explicitamente usando a janela Relações ou arrastando um campo
do painel Lista de Campos. O Office Access 2007 usa relações de tabelas para que elas possam ser
unidas quando for necessário utilizá-las em um objeto de banco de dados. Há várias razões que justificam
a criação de relações de tabelas antes da criação de outros objetos do banco de dados, como formulários,
consultas e relatórios.

As Relações de Tabela Informam os Designs de Consulta


Para trabalhar com registros de mais de uma tabela, você geralmente deve criar uma consulta que
associe as tabelas. A consulta faz a correspondência dos valores no campo de chave primária da primeira
tabela com um campo de chave estrangeira na segunda tabela. Por exemplo, para retornar linhas que
listem todos os pedidos de cada cliente, construa uma consulta que faça a associação da tabela Clientes
com a tabela Pedidos com base no campo Código do Cliente. Na janela Relações, você pode especificar
manualmente os campos a serem associados. Entretanto, se você já tiver uma relação definida entre as
tabelas, o SGBD fornece a associação padrão, com base na relação de tabela existente. Além disso, se
você usar um dos assistentes de consulta, o SGBD usa as informações coletadas das relações da tabela
já definidas para apresentá-lo opções informadas e para pré-definir configurações de propriedade com os
valores padrão adequados.

As Relações de Tabela Informam os Designs de Formulário e Relatório


Quando você cria um formulário ou relatório, o SGBD usa as informações coletadas das relações de
tabela já definidas para apresentá-lo opções informadas e para pré-definir configurações de propriedade
com os valores padrão adequados.

As relações de tabelas são a base através da qual você pode garantir a integridade referencial para
ajudar a evitar registros órfãos no banco de dados. Um registro órfão é um registro com uma referência a
outro registro que não existe — por exemplo, um registro de pedido que faça referência a um registro de
cliente que não existe.

Quando você cria um banco de dados, divide as informações em tabelas, cada uma com uma chave
primária. Depois, você adiciona chaves estrangeiras a tabelas relacionadas que façam referência a essas
chaves primárias. Esses pares chave estrangeira-chave primária formam a base das relações de tabelas
e consultas de várias tabelas. É importante, entretanto, que essas referências chave estrangeira-chave
primária permaneçam sincronizadas. A integridade referencial ajuda a garantir que as referências
permaneçam sincronizadas e depende das relações de tabelas.

Noções Básicas sobre Integridade Referencial

Quando você cria um banco de dados, divide as informações em várias tabelas baseadas em tópicos
para minimizar a redundância de dados. Forneça, então, ao SGBD os meios necessários para reunir os
dados novamente colocando campos em comum em tabelas relacionadas. Por exemplo, para representar
uma relação um-para-muitos, tome a chave primária da tabela "um" e adicione-a como campo adicional
na tabela "muitos". Para reunir os dados novamente, o SGBD pega o valor na tabela "muitos" e procura
o valor correspondente na tabela "um". Dessa forma, os valores na tabela "muitos" fazem referência aos
valores correspondentes na tabela "um".
Suponha que você tenha uma relação um-para-muitos entre Expedidores e Pedidos e você deseje
excluir um Expedidor. Se o expedidor que você deseja excluir tiver pedidos na tabela Pedidos, esses
pedidos se tornarão "órfãos" quando você excluir o registro Expedidor. Os pedidos ainda conterão um
código de expedidor, mas ele não será mais válido, porque o registro ao qual ele faz referência não existe
mais.
O objetivo da integridade referencial é evitar órfãos e manter as referências em sincronia, para que
esta situação hipotética nunca ocorra.
Imponha a integridade referencial habilitando-a para uma relação de tabela (consulte Impor a
integridade referencial para obter instruções passo a passo). Depois que ela é imposta, o SGBD rejeita
qualquer operação que possa violar a integridade referencial dessa relação de tabela. Isso significa que
o SGBD rejeitará ambas atualizações que alterem o destino de uma referência e exclusões que removam
o destino de uma referência. Entretanto, é possível que você venha a ter uma necessidade perfeitamente
válida de alterar a chave primária de um expedidor que possua pedidos na tabela Pedidos. Nesses casos,
o que você realmente precisa é que o SGBD atualize automaticamente todas as linhas afetadas como

37
1678859 E-book gerado especialmente para DANIEL CRISTIAN
parte de uma única operação. Dessa forma, o SGBD garante que a atualização seja totalmente realizada,
de forma que o banco de dados não fique inconsistente, com algumas linhas atualizadas e outras não.
Por esse motivo, o SGBD oferece suporte à opção Propagar Atualização dos Campos Relacionados.
Quando você impõe a integridade referencial e escolhe a opção Propagar Atualização dos Campos
Relacionados, e depois atualiza uma chave primária, o SGBD atualiza automaticamente todos os campos
que fazem referência à chave primária.
É possível também que você venha a ter a necessidade válida de excluir uma linha e todos os registros
relacionados — por exemplo, um registro Expedidor e todos os pedidos relacionados a ele. Por isso, o
SGBD oferece suporte à opção Propagar Exclusão dos Registros Relacionados. Quando você impõe a
integridade referencial e escolhe a opção Propagar Exclusão dos Registros Relacionados e depois exclui
um registro no lado da chave primária da relação, o SGBD automaticamente exclui todos os registros que
fazem referência à chave primária.

Formulários
Às vezes refere-se aos formulários como "telas de entrada de dados". Eles são as interfaces usadas
para trabalhar com os dados e frequentemente contêm botões de comando que executam vários
comandos. Você pode criar um banco de dados sem usar formulários, simplesmente editando seus dados
nas folhas de dados da tabela. Contudo, a maioria dos usuários de bancos de dados prefere usar
formulários para exibir, inserir e editar dados nas tabelas.
Os formulários fornecem um formato fácil de usar para trabalhar com os dados, e você também pode
adicionar elementos funcionais, como botões de comando. Você pode programar os botões para
determinar quais dados aparecem no formulário, abrir outros formulários ou relatórios, ou executar várias
outras tarefas. Por exemplo, você pode ter um formulário chamado "Formulário do Cliente" no qual você
trabalha com dados de clientes. O formulário do cliente pode ter um botão que abre um formulário de
pedido no qual você pode inserir um novo pedido para esse cliente.
Os formulários também permitem controlar como outros usuários interagem com os dados no banco
de dados. Por exemplo, você pode criar um formulário que mostra apenas determinados campos e
permite que apenas determinadas operações sejam executadas. Isso ajuda a proteger dados e a garantir
que os dados sejam inseridos corretamente.

Relatórios
Os relatórios são usados para resumir e apresentar os dados nas tabelas. Geralmente, um relatório
responde a uma pergunta específica, como "Que valor recebemos de cada cliente este ano?" ou "Em
quais cidades nossos clientes estão localizados?" Cada relatório pode ser formatado para apresentar as
informações de maneira a melhor facilitar a leitura.
Um relatório pode ser executado a qualquer momento e sempre refletirá os dados atuais no banco de
dados. De maneira geral, os relatórios são formatados para serem impressos, mas também podem ser
exibidos na tela, exportados para outro programa ou enviados como uma mensagem de e-mail.

Consultas
É nas consultas que reside o trabalho real de um banco de dados, e elas podem executar várias
funções diferentes. Sua função mais comum é recuperar dados específicos das tabelas. Geralmente, os
dados que você deseja ver estão espalhados em várias tabelas, e as consultas permitem exibi-los em
uma única folha de dados. Além disso, como geralmente você não deseja ver todos os registros de uma
vez, as consultas permitem adicionar critérios para "filtrar" os dados até obter somente os registros
desejados. Frequentemente as consultas servem como fonte de registros para formulários e relatórios.
Certas consultas são "atualizáveis", o que significa que você pode editar os dados nas tabelas base
por meio da folha de dados da consulta. Se estiver trabalhando em uma consulta atualizável, lembre-se
de que as alterações estão realmente sendo feitas nas tabelas e não apenas na folha de dados da
consulta.
As consultas têm duas variedades básicas: consultas seleção e consultas ação. Uma consulta seleção
simplesmente recupera os dados e os disponibiliza para uso. Você pode exibir os resultados da consulta
na tela, imprimi-los ou copiá-los para a área de transferência. Ou você pode usar a saída da consulta
como fonte de registros para um formulário ou relatório.
Uma consulta ação, como o nome indica, executa uma tarefa com os dados. As consultas ação podem
ser usadas para criar novas tabelas, adicionar dados a tabelas existentes, atualizar ou excluir dados.

38
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Design de Banco de Dados12

Certos princípios guiam o processo de design do banco de dados. O primeiro princípio é que
informações duplicadas (também denominadas dados redundantes) são ruins porque consomem espaço
e aumentam a possibilidade de erros e inconsistências. O segundo princípio é que a correção e
completitude das informações é importante. Se o banco de dados contiver informações incorretas, todos
os relatórios que empregam informações do banco de dados também conterão informações incorretas.
Como resultado, todas as decisões tomadas a partir desses relatórios serão errôneas.
Um bom design de banco de dados, portanto, é um que:
- Divide as informações em tabelas baseadas em tópicos, visando reduzir a redundância de dados.
- Fornece ao SGBD os dados essenciais à reunião de informações nas tabelas, conforme necessário.
- Ajuda a oferecer suporte e assegurar a precisão e a integridade das informações.
- Atende suas necessidades de processamento de dados e de relatórios.

O Processo do Design
O processo do design consiste nas seguintes etapas:

Determinar a Finalidade do seu Banco de Dados


Recomenda-se anotar a finalidade do banco de dados em um papel — sua finalidade, como se espera
usá-lo e quem o usará. Com relação a um banco de dados pequeno e de empresa caseira, por exemplo,
você escreveria algo como "O banco de dados cliente mantém uma lista de informações de clientes com
a finalidade de produzir malas diretas e relatórios". Se o banco de dados for mais complexo ou usado por
diversas pessoas, como ocorre frequentemente em um ambiente empresarial, a finalidade poderia ser
contida em um simples parágrafo ou mais, devendo incluir quando e como cada pessoa irá usar o banco
de dados. A ideia é ter uma declaração de missão bem desenvolvida, à qual se possa referir durante todo
o processo de design. Ter uma declaração como essa ajuda a focar nas metas no momento da tomada
de decisões.

Localizar e Organizar as Informações Necessárias


Reunir todos os tipos de informações que você possa desejar gravar no banco de dados, como nome
de produto e número de pedido.
Para localizar e organizar as informações requeridas, comece pelas informações existentes. Por
exemplo, é possível registrar os pedidos de compra em um livro razão ou manter as informações do
cliente em formulários de papel em um gabinete de arquivos. Reúna esses documentos e liste cada um
dos tipos de informações mostrados (por exemplo, cada caixa preenchida em um formulário). Se não
houver formulários, imagine que será necessário criar um formulário para registrar as informações do
cliente. Que informações você colocaria no formulário? Que caixas de preenchimento você criaria?
Identifique e liste todos esses itens. Por exemplo, supondo que você atualmente mantém a lista de clientes
em cartões de índice. O exame desses cartões demonstra que cada cartão contém um nome, endereço,
cidade, estado, CEP e número de telefone do cliente. Cada um desses itens representa uma coluna
potencial em uma tabela.
À medida que prepara a lista, não se preocupe em conseguir uma lista perfeita na primeira tentativa.
Em vez disso, liste todos os itens que lhe vierem à mente. Se outras pessoas usarem o banco de dados,
peça sugestões também. Você poderá refinar posteriormente a lista.
Em seguida, considere os tipos de relatórios ou de listas de distribuição a serem produzidos com o
banco de dados. Por exemplo, você poderá gerar um relatório de vendas de produto que apresente as
vendas por região, ou um relatório de resumo de inventário mostrando os níveis de inventário do produto.
É igualmente possível gerar cartas modelo para enviar aos clientes, que divulguem um evento de vendas
ou que ofereçam um brinde. Crie o relatório ideal e imagine sua aparência. Que informações você
colocaria no relatório? Liste todos os itens. Faça o mesmo com a carta formulário e com todos os relatórios
cuja criação você antevê.

12
https://support.office.com/pt-BR/article/Fundamentos-do-design-de-banco-de-dados-1EADE2BF-E3A0-41B5-AEE6-D2331F158280

39
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Pensar nos relatórios e listas de distribuição a serem criados ajuda na identificação dos itens
necessários ao banco de dados. Por exemplo, supondo que você dê aos clientes a chance de optar por
(ou não) por atualizações periódicas via e-mail, e que deseja imprimir uma listagem dos que optaram pela
opção. Para registrar essas informações, adicione uma coluna "Enviar e-mail" à tabela do cliente. É
possível configurar o campo como Sim ou Não com relação a todos os clientes.
A necessidade de enviar mensagens de e-mail aos clientes sugere que um outro item seja registrado.
Após saber se o cliente deseja receber mensagens de e-mail, será também necessário saber o endereço
de e-mail para os quais as mensagens serão enviadas. Portanto, é necessário registrar um endereço de
e-mail para cada um dos clientes.
Há sentido em construir um protótipo de cada relatório ou listagem de saída, e pensar nos itens
necessários à produção do relatório. Por exemplo, ao examinar uma carta formulário, algumas coisas
podem vir à sua mente. Para incluir uma saudação adequada — por exemplo, a sequência de caracteres
"Sr.", "Srta." ou "Sra.", que inicia uma saudação, será preciso criar um item de saudação. Da mesma
forma, é possível começar normalmente uma carta com “Prezado Sr. Silva”, em vez de “Prezado
Edmundo Silva”. Isso quer dizer que você deseja armazenar regularmente o sobrenome em separado do
nome.
Um ponto essencial a ser lembrado é que todas as partes da informação devem ser quebradas em
suas partes úteis menores. No caso de nome, para disponibilizar prontamente o sobrenome, quebre-o em
duas partes — Nome e Sobrenome. Para classificar um relatório pelo último nome, por exemplo, é útil
armazenar separadamente o sobrenome do cliente. Em geral, quando se deseja classificar, pesquisar,
calcular ou criar um relatório com base em um item de informações, deve-se inserir esse item em seu
próprio campo.
Pense nas perguntas que você deseja que o banco de dados responda. Por exemplo, quantas vendas
do produto em destaque foram fechadas mês passado? Onde vivem seus melhores clientes? Quem é o
fornecedor de seu produto de maior vendagem? A antecipação dessas perguntas ajuda a que você se
concentre nos outros itens a serem registrados.

Dividir as Informações em Tabelas


Dividir os itens de informações em entidades ou tópicos principais, como Produtos ou Pedidos. Cada
tópico torna-se então uma tabela.
Para dividir as informações em tabelas, escolha as maiores entidades ou tópicos. Por exemplo, após
localizar e organizar informações relativas a um banco de dados de venda de produto, a lista preliminar
deve ter a seguinte aparência:

Itens de informações manuscritas e agrupados em assuntos.

40
1678859 E-book gerado especialmente para DANIEL CRISTIAN
As maiores entidades mostradas aqui são os produtos, os fornecedores, os clientes e os pedidos.
Assim, há sentido em começar pelas seguintes tabelas: uma de fatos sobre produtos, uma de fatos sobre
fornecedores, uma de fatos sobre clientes, e uma de fatos sobre pedidos. Embora essas tabelas não
completem a lista, são um ponto de partida. Continue e refinar essa lista até ter um design que funcione
bem.
Ao examinar pela primeira vez a lista de itens, você talvez se sinta tentado a colocá-los todos em uma
única tabela, em vez de ter quatro, como mostrado na ilustração anterior. Você saberá aqui por que isso
não é recomendado. Pense um momento na tabela mostrada a seguir:

Exemplo de tabela de produtos e fornecedores.

Nesse caso, cada linha contém informações sobre o produto e seu fornecedor. Como pode haver
vários produtos de um mesmo fornecedor, o nome e o endereço do fornecedor deverão ser repetidos
inúmeras vezes. Isso consome espaço em disco. Gravar as informações do fornecedor uma única vez
em uma tabela Fornecedores separada e, em seguida, vincular essa tabela à tabela Produtos, é uma
solução muito melhor.
Um segundo problema com esse design advém da necessidade de se modificarem as informações
sobre o fornecedor. Por exemplo, supondo seja necessário alterar o endereço de um fornecedor. Como
o endereço aparece em vários lugares, você talvez altere acidentalmente o endereço em um local, porém
se esqueça de alterá-lo nos outros. Gravar o endereço do fornecedor em apenas um local resolve esse
problema.
Ao criar seu banco de dados, tente sempre registrar cada fato apenas uma vez. Quando se pegar
repetindo a mesma informação em mais de um local, como o endereço de determinado fornecedor,
coloque a informação em uma tabela separada.
Finalmente, suponhamos que haja apenas um produto fornecido pela Coho Winery, e que você deseje
excluir o produto, retendo, porém, o nome do fornecedor e as informações de endereço. Como excluir o
registro do produto sem também perder as informações do fornecedor? Isso não é possível. Como cada
registro contém fatos sobre um produto, assim como fatos sobre o fornecedor, não é possível excluir um
sem eliminar o outro. Para manter esses fatos de maneira distinta, separe essa tabela em duas: uma
tabela de informações sobre o produto, e uma outra tabela de informações sobre o fornecedor. A exclusão
do registro do produto deve eliminar apenas os fatos sobre o produto, não os fatos sobre o fornecedor.
Após a seleção do tópico a ser representado na tabela, as colunas da tabela só devem armazenar
fatos sobre esse tópico. Por exemplo, a tabela de produto só deve armazenar fatos sobre produtos. Como
o endereço do fornecedor é um fato sobre o fornecedor, e não um fato sobre o produto, pertence à tabela
do fornecedor.

Transformar Informações em Colunas


Opte pelas informações que deseja armazenar em cada uma das tabelas. Cada item torna-se um
campo que é exibido como coluna da tabela. Por exemplo, uma tabela Funcionários pode incluir campos
como Sobrenome e Data de Contratação.
Para determinar as colunas de uma tabela, opte pelas informações que você necessita controlar sobre
o tópico registrado na tabela. Por exemplo, com relação à tabela Cliente, Nome, Endereço, Cidade-
Estado-CEP, E-mail para envio de correspondência, Saudação e Endereço de e-mail constituem uma
lista de colunas que é um bom começo. Cada registro da tabela contém o mesmo conjunto de colunas,
de modo que é possível armazenar informações sobre o Nome, Endereço, Cidade-Estado-CEP, E-mail
para envio de correspondência, Saudação e endereço de e-mail de todos os registros. Por exemplo, a
coluna de endereço contém os endereços dos clientes. Cada registro contém dados sobre um cliente, e
o campo de endereço contém o endereço do cliente.
Após determinar o conjunto inicial de colunas de cada tabela, você poderá refinar as colunas. Por
exemplo, é recomendado armazenar o nome do cliente em duas colunas separadas: o nome e o
sobrenome, de modo que se possa classificar, pesquisar e indexar apenas nessas duas colunas. Da
mesma forma, o endereço na verdade consiste em cinco componentes distintos: endereço, cidade,

41
1678859 E-book gerado especialmente para DANIEL CRISTIAN
estado, CEP e país/região. É igualmente recomendado armazená-los em colunas diferentes. Se você
deseja realizar uma operação de pesquisa, filtro ou classificação por estado, por exemplo, será necessário
armazenar a informação de estado em uma coluna separada.
Verifique se o banco de dados conterá somente informações de origem doméstica, ou se, além disso,
conterá informações internacionais. Por exemplo, se você planeja armazenar endereços internacionais,
deve haver uma coluna de Região em vez de Estado, porque essa coluna aceita os estados domésticos
e as regiões de outros países/outras regiões. Semelhantemente, o código de endereçamento postal tem
mais sentido que o CEP se você pretende armazenar endereços internacionais.
A lista a seguir mostra algumas dicas sobre a determinação de colunas:

Não Inclusão de Dados Calculados


Na maior parte dos casos, o resultado de cálculos não deve ser armazenado em tabelas. Em vez disso,
é possível fazer com que o SGBD realize cálculos quando se deseja exibir o resultado. Por exemplo,
supondo que haja um relatório de Produtos do Pedido que exiba o subtotal de unidades do pedido por
categoria de produto no banco de dados. Contudo, não há coluna de subtotal de Unidades no Pedido em
nenhuma tabela. Em vez disso, a tabela de Produtos inclui uma coluna Unidades do Pedido que armazena
as unidades do pedido com relação a cada um dos produtos. Usando esses dados, o SGBD calcula o
subtotal cada vez que o relatório é impresso. O próprio subtotal não pode ser armazenado na tabela.

Armazenar as Menores Partes Lógicas das Informações


Você pode ficar tentado a ter um único campo para nomes completos, ou para nomes de produtos
juntamente com descrições de produto. Se você combinar mais de um tipo de informação em um só
campo, será difícil recuperar posteriormente os fatos individuais. Tente quebrar as informações em partes
lógicas, por exemplo, criando campos distintos para nome e sobrenome, ou por nome, categoria e
descrição de produto.

Lista de itens de informações durante o processo de Design.

Após refinar as colunas de dados de cada tabela, você estará pronto para escolher a chave primária
de cada tabela.

Especificar as Chaves Primárias


Escolher a chave primária de todas as tabelas. A chave primária é uma coluna usada unicamente para
identificar cada linha. Um exemplo pode ser Código de Produto ou Código de Pedido.
Toda tabela deve incluir uma coluna ou conjunto de colunas que identifica com exclusividade cada
linha armazenada na tabela. Trata-se, em geral, de um número de identificação exclusivo, como o número
de identificação de um funcionário ou um número de série. Na terminologia de banco de dados, essas
informações são denominadas chave primária da tabela. O SGBD usa os campos de chave primária para
associar rapidamente os dados de várias tabelas e passar a você as informações consolidadas.
Se já houver um identificador exclusivo para a tabela, como um número de produto que identifica
exclusivamente cada produto do seu catálogo, você poderá usar esse identificador como chave primária
da tabela - porém, apenas se os valores da coluna forem sempre diferentes com relação a todos os
registros. Não é possível duplicar valores em uma chave primária. Por exemplo, não use nomes de
pessoas como chave primária, porque nomes não são exclusivos. É fácil encontrar duas pessoas com o
mesmo nome em uma mesma tabela.

42
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A chave primária deve sempre conter um valor. Se o valor de uma coluna pode se tornar sem alocação
ou desconhecido (valor faltante) em dado momento, não poderá ser usado como componente de chave
primária.
Escolha sempre uma chave primária cujo valor não se altere. Em um banco de dados que utiliza mais
de uma tabela, a chave primária de uma tabela pode ser usada como referência em outras tabelas. Se a
chave primária se alterar, a alteração precisa também ser aplicada a todos os locais em que a chave é
citada. Usar uma chave primária que não se altera reduz a possibilidade de que a chave primária fique
fora de sincronia com outras tabelas que fazem referência a ela.
Em geral, um número exclusivo arbitrário é usado como chave primária. Por exemplo, é possível
atribuir um número exclusivo de pedido a todos os pedidos. A finalidade do número de pedido é identificá-
lo. Após atribuído, ele não é mais alterado.
Se você não estiver visando a uma coluna ou conjunto de colunas que possam consistir em chave
primária adequada, pense em usar uma coluna que tenha um tipo de dados de Auto Incremento (Onde
os números de cada registro são gerados automaticamente). Quando o tipo de dados de Auto Incremento
é usado, o SGBD atribui automaticamente um valor a você. Esse identificador é isento de fatos; não
contém informações factuais que descrevam a linha que representam. Os identificadores isentos de fatos
são ideais para se usar como chave primária porque não se alteram. Uma chave primária que contenha
fatos a respeito de uma linha — número de telefone ou nome de cliente, por exemplo — tem mais
probabilidade de se alterar porque as próprias informações factuais podem se modificar.

Mostrando a tabela produtos em um campo de chave primária.

1. Um conjunto de colunas para o tipo de dados de Auto Incremento consiste, em geral, em uma chave
primária adequada. Jamais dois códigos de produto são idênticos.
Em alguns casos, é preferível usar dois ou mais campos que, juntos, forneçam a chave primária para
uma tabela. Por exemplo, uma tabela de Detalhes do Pedido que armazene itens de linha de pedidos
usaria duas colunas em sua chave primária: Código de Pedido e Código de Produto. Quando uma chave
primária emprega mais de uma coluna, é também denominada chave composta.
Com relação ao banco de dados de vendas de produto, é possível criar uma coluna de Auto Incremento
para cada uma das tabelas, para servir como chave primária: CódigoDoProduto para a tabela Produtos,
CódigoDoPedido para as tabelas Pedidos, CódigoDoCliente para a tabela Clientes e
CódigoDoFornecedor para a tabela Fornecedores.

Itens e informações durante o processo de design.

Configurar as Relações de Tabelas


Observar cada uma das tabelas e decidir como os dados em determinada tabela estão relacionados
aos dados de outras tabelas. Adicionar campos a tabelas ou criar novas tabelas para esclarecer as
relações, se necessário.

43
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Agora que as informações estão divididas em tabelas, há necessidade de uma forma de reunir as
informações novamente, com um sentido. Por exemplo, o formulário a seguir engloba informações de
várias tabelas.

Formulário de pedido.

1. As informações desse formulário são originárias da tabela Clientes


2. ...da tabela Funcionários...
3. ...da tabela Pedidos...
4. ...da tabela Produtos...
5. ...e da tabela Detalhes do Pedido.

Os SGBD são um sistemas de gerenciamento de banco de dados relacionais. Em um banco de dados


relacional, as informações são divididas em tabelas distintas, baseadas em tópicos. São então utilizadas
as relações de tabelas para reunir informações, à medida que se tornem necessárias.

Criando uma Relação Um-para-Muitos


Examine este exemplo: as tabelas Fornecedores e Produtos do banco de dados de pedidos de produto.
Um fornecedor pode fornecer qualquer número de produtos. Consequentemente, para qualquer
fornecedor representado na tabela Fornecedores pode haver vários produtos representados na tabela
Produtos. A relação entre a tabela Fornecedores e a tabela Produtos é, portanto, uma relação um-para-
muitos.

Conceito um para muitos.

Para representar uma relação um-para-muitos em um design de banco de dados, tome a chave
primária do lado "um" da relação e adicione-a como coluna ou colunas adicionais à tabela do lado "muitos"
da relação. Nesse caso, por exemplo, a coluna Código do Fornecedor da tabela Fornecedores é
adicionada à tabela Produtos. O SGBD pode, em seguida, usar o número do código do fornecedor da
tabela Produtos para localizar o fornecedor correto de todos os produtos.
A coluna Código do Fornecedor da tabela Produtos é denominada chave estrangeira. A chave
estrangeira é uma chave primária de outra tabela. A coluna Código do Fornecedor da tabela Produtos é
uma chave estrangeira porque é também a chave primária da tabela Fornecedores.

44
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Lista de itens e informações durante o processo de design.

As bases para a associação de tabelas relacionadas são fornecidas pelo estabelecimento da união de
chaves primárias com chaves estrangeiras. Quando não se está certo sobre quais tabelas devem
compartilhar uma coluna comum, identificar uma relação um-para-muitos assegura que as duas tabelas
envolvidas exigirão verdadeiramente uma coluna compartilhada.

Criando uma Relação Muitos-para-Muitos


Examine a relação entre a tabela Produtos e a Tabela Pedido.
Um único pedido pode incluir mais de um produto. Por outro lado, um único produto pode constar em
vários pedidos. Assim, para todos os registros da tabela Pedidos pode haver vários registros na tabela
Produtos. E para cada registro na tabela Produtos pode haver registros na tabela Pedidos. Esse tipo de
relação é denominado relação muitos-para-muitos porque com relação a todos os produtos pode haver
vários pedidos, e para todos os pedidos pode haver vários produtos. Observe que para detectar relações
muitos-para-muitos entre as tabelas é importante considerar ambos os lados da relação.
Os tópicos das duas tabelas — pedidos e produtos — têm uma relação muitos-para-muitos. Isso
representa um problema. Para entender o problema, imagine o que aconteceria se você tentasse criar a
relação entre duas tabelas adicionando o campo Código do Produto à tabela Pedidos. Para ter mais de
um produto por pedido, é necessário mais de um registro na tabela Pedidos por pedido. Você repetiria as
informações do pedido em cada uma das linhas relativas a um único pedido — o que resultaria em um
design ineficaz que poderia resultar em dados imprecisos. O mesmo problema é enfrentado quando se
coloca o campo Código do Pedido na tabela Produtos — haveria mais de um registro na tabela Produtos
para cada produto. Como resolver esse problema?
A solução é criar uma terceira tabela, em geral denominada tabela de junção, que divide as diversas
relações muitos-para-muitos em duas relações um-para-muitos. Insira a chave primária de cada uma das
duas tabelas em uma terceira tabela. Consequentemente, a terceira tabela registra todas as ocorrências
ou instâncias da relação.

Relacionamento muitos para muitos.

45
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Cada registro da tabela de Detalhes do Pedido representa um item de linha do pedido. A chave primária
da tabela Detalhes do Pedido consiste em dois campos — as chaves estrangeiras das tabelas Pedidos e
Produtos. Usar somente o campo Código do Pedido não funciona como chave primária dessa tabela,
porque um único pedido pode conter vários itens de linha. O Código do Pedido repete-se em cada item
de linha em um pedido, de modo que o campo não possa conter valores únicos. Usar apenas o campo
Código do Produto não funciona também, porque um mesmo produto pode surgir em diversos pedidos
diferentes. Em conjunto, porém, os dois campos podem sempre produzir um valor único para cada
registro.
No banco de dados de vendas de produto a tabela Pedidos e a tabela Produtos não estão relacionadas
entre si de forma direta. Em vez disso, são relacionadas indiretamente através da tabela Detalhes do
Pedido. A relação muitos-para-muitos entre pedidos e produtos é representada no banco de dados por
meio de duas relações um-para-muito:
A tabela Pedidos e a tabela Detalhes tem uma relação um-para-muitos. Todos os pedidos podem ter
mais de um item de linha, porém todo item de linha é conectado a apenas um pedido.
A tabela Produtos e a tabela Pedidos tem uma relação um-para-muitos. Cada produto pode ter vários
itens de linha associados a ele, mas cada item de linha se refere a apenas um produto.
Da tabela de Detalhes do Pedido, é possível determinar todos os produtos em um pedido particular. É
possível também determinar que todos os pedidos de um produto particular.
Após incorporar a tabela de Detalhes do Pedido, a lista de tabelas e campos pode ter a seguinte
aparência:

Lista de itens e informações durante o processo de design.

Criando uma Relação Um-para-Um


Um outro tipo de relação é a relação um-para-um. Por exemplo, suponhamos que haja necessidade
de registrar algumas informações especiais e complementares de um produto, que serão raramente
usadas ou que se só aplicam a uns poucos produtos. Como essas informações não são exigidas com
frequência, e como armazenar informações na tabela Produtos resultaria em espaço vazio para todos os
produtos aos quais elas não se aplicam, coloque essas informações em uma tabela distinta. Assim como
a tabela Produtos, utilize o CódigoDoProduto como chave primária. A relação entre essa tabela
complementar e a tabela Produto é uma relação um-para-um. Para cada registro da tabela Produto, existe
apenas um registro correspondente na tabela complementar. Quando essa relação é identificada, ambas
as tabelas devem compartilhar um campo comum.
Quando a necessidade de uma relação um-para-um é detectada no banco de dados, considere colocar
as informações das duas tabelas juntas em uma só tabela. Se houver um motivo para não o fazer, talvez
porque isso resulte em uma série de espaços vazios, a lista a seguir mostra como representar a relação
no design:
Se as duas tabelas tiverem o mesmo tópico, você poderá provavelmente configurar a relação por meio
da mesma chave primária em ambas as tabelas.
Se as duas tabelas tiverem tópicos diferentes com chaves primárias diversas, escolha uma das tabelas
(qualquer uma) e insira a chave primária na outra tabela como chave estrangeira.
A determinação das relações entre tabelas ajuda a assegurar que se tenham as tabelas e colunas
corretas. Quando existe uma relação um-para-um ou um-para-muitos, as tabelas envolvidas exigem o

46
1678859 E-book gerado especialmente para DANIEL CRISTIAN
compartilhamento de uma coluna ou colunas comuns. Quando existe uma relação muitos-a-muitos, uma
terceira tabela é necessária para representar a relação.

Refinar o Design
Analisar o design com relação aos erros. Criar as tabelas e adicionar alguns novos registros de dados
de exemplo. Observe se os resultados esperados das tabelas são obtidos. Faça ajustes no design,
conforme necessário.
Após ter as tabelas, campos e relações necessários, crie e preencha as tabelas com dados de exemplo
e tente trabalhar com as informações: criação de consultas, adição de novos registros, entre outros. Fazer
isso ajuda a levantar os problemas potenciais — por exemplo, pode ser necessário adicionar uma coluna
que se esqueceu de inserir durante a fase de design, ou pode haver uma tabela que deva ser dividida em
duas tabelas para remover duplicação.
Confirme se é possível usar o banco de dados para obter as respostas desejadas. Crie rascunhos dos
seus formulários e relatórios, e veja se eles apresentam os dados esperados. Procure por duplicações de
dados desnecessárias e, quando encontrar alguma, altere o design para eliminá-la.
À medida que você faz experiências com o banco de dados inicial, provavelmente descobrirá espaço
para melhoramentos. Seguem algumas coisas a serem verificadas:
- Você esqueceu alguma coluna? Se esqueceu, as informações pertenciam a tabelas existentes? Se
se trata de informações sobre alguma outra coisa, será necessário criar uma outra tabela. Crie uma coluna
para cada item de informação que necessita de controle. Se as informações não podem ser calculadas a
partir de outras colunas, é provável que exijam uma outra coluna.
- Há alguma coluna desnecessária porque pode ser calculada a partir de campos existentes? Se um
item de informação puder ser calculado de outras colunas existentes — um preço descontado calculado
do preço de varejo, por exemplo — em geral é melhor fazer exatamente isso e evitar criar uma nova
coluna.
- Informações duplicadas são repetidamente inseridas em uma das tabelas? Em caso afirmativo, talvez
seja necessário dividir a tabela em duas tabelas que tenham a relação um-para-muitos.
- Existem tabelas com vários campos, um número limitado de registros e vários campos vazios em
registros individuais? Se for o caso, pense em criar novamente a tabela de modo que passe a ter menos
campos e mais registros.
- Todos os itens de informações foram quebrados em partes úteis menores? Se for necessário criar
relatório, classificar, pesquisar ou calcular um item de informação, coloque esse item em sua própria
coluna.
- Cada coluna contém um fato sobre o tópico da tabela? Quando uma coluna não contém informações
sobre o tópico da tabela é porque pertence a uma tabela diferente.
- Todas as relações entre tabelas são representadas tanto por campos comuns como por uma terceira
tabela? As relações um-para-um ou um-para-muitos requerem colunas comuns. As relações muitos-para-
muitos requerem uma terceira tabela.

Refinando a Tabela Produtos


Suponhamos que cada produto no banco de dados de vendas de produto incida sobre uma categoria
geral, como bebidas, condimentos ou frutos do mar. A tabela Produtos poderia incluir um campo que
apresente a categoria de cada produto.
Suponhamos que após examinar e refinar o design do banco de dados você decida armazenar uma
descrição de categoria juntamente com o nome da categoria. Quando se adiciona um campo Descrição
de Categoria à tabela Produtos, é preciso repetir todas as descrições de categoria de cada produto que
incida nessa categoria — o que não é uma boa solução.
Uma solução mais adequada é transformar Categorias em um tópico novo para controle do banco de
dados, com sua própria tabela e sua própria chave primária. Adicione então a chave primária da tabela
Categorias à tabela Produtos como chave estrangeira.
As tabelas Categorias e Produtos têm uma relação um-para-muitos: uma categoria pode incluir mais
de um produto, porém um produto só pode pertencer a uma categoria.
No momento de examinar as estruturas da tabela, preste atenção a grupos repetidos. Por exemplo,
considere uma tabela contendo as seguintes colunas:
- Código do Produto
- Nome
- Código de Produto1
- Nome1
- Código de Produto2

47
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Nome2
- Código de Produto3
- Nome3

Aqui, cada produto é um grupo separado de colunas que diferem entre si apenas pela adição de um
algarismo ao final do nome da coluna. Quando colunas numeradas dessa forma aparecerem, reexamine
o design.
Esse tipo de design tem várias falhas. Com relação aos iniciantes, força a colocação de um limite
superior no número de produtos. Tão logo você exceda esse limite, será preciso adicionar um novo grupo
de colunas à estrutura da tabela, o que é uma tarefa administrativa essencial.
Um outro problema é que os fornecedores com número de produtos inferior ao máximo irão desperdiçar
espaço, uma vez que as colunas adicionais estarão vazias. A maior falha com relação a esse design é
que isso torna várias tarefas difíceis de desempenhar, como a classificação ou indexação da tabela por
código ou nome de produto.
Sempre que forem exibidos grupos repetidos, examine detalhadamente o design, visando a dividir a
tabela em dois. No exemplo acima é melhor usar duas tabelas, uma para fornecedores e uma para
produtos, vinculadas por código de fornecedor.

Aplicar as Regras de Normalização


Aplicar as regras de normalização de dados para examinar se as tabelas estão corretamente
estruturadas. Faça ajustes nas tabelas, conforme necessário.
É possível aplicar as regras de normalização de dados (também chamadas simplesmente regras de
normalização) como próxima etapa do design. Use essas regras para ver se as tabelas estão
corretamente estruturadas. O processo de aplicação de regras ao design do banco de dados é
denominado normalização de banco de dados, ou apenas normalização.
A normalização é muito mais útil após a representação de todos os itens de informações e da obtenção
de um design preliminar. A ideia é ajudar a assegurar que você distribua os itens de informações pelas
tabelas corretas. O que a normalização não pode fazer é assegurar que se tenham todos os itens de
dados corretos de início.
Aplique as regras em sequência, assegurando a cada etapa que o seu design chegue ao que é
conhecido como "formas normalizadas". Cinco formas normalizadas são amplamente aceitas — da
primeira forma normalizada à quinta forma normalizada. Este artigo se expande nas três primeiras, porque
elas são tudo o que se exige para a maior parte dos bancos de dados.

- Primeira Forma Normalizada


A primeira forma normalizada declara que a cada interseção de linha e coluna da tabela existe um
valor único e nunca uma lista de valores. Por exemplo, não é possível ter um campo denominado Preço,
em que se insira mais de um Preço. Quando se entende cada interseção de linhas e colunas como uma
célula, cada célula poderá manter apenas um valor.

- Segunda Forma Normalizada


A segunda forma normalizada requer que cada coluna não-chave seja totalmente dependente de toda
a chave primária, não apenas de parte da chave. Essa regra aplica-se quando se tem uma chave primária
que consiste em mais de uma coluna. Por exemplo, supondo que haja uma tabela contendo as colunas
a seguir, onde Código do Pedido e Código do Produto formam a chave primária:
- Código do Pedido (chave primária)
- Código de Produto (chave primária)
- Nome de Produto

Esse design desrespeita a segunda forma normalizada, uma vez que o Nome do Produto é dependente
do Código do Produto, mas não do Código do Pedido, portanto, não depende de toda a chave primária.
É preciso remover o Nome do Produto da tabela. Ele pertence a uma tabela diferente (Produtos).

- Terceira Forma Normalizada


A terceira forma normalizada exige que não apenas todas as colunas não-chave sejam dependentes
de toda a chave primária, mas que as colunas não-chave sejam independentes entre si.
Uma outra forma de dizer isso é que cada coluna não-chave seja dependente da chave primária e
somente da chave primária. Por exemplo, na hipótese de haver uma tabela contendo as seguintes
colunas:

48
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- CódigoDeProduto (chave primária)
- Nome
- SRP
- Desconto

Suponha que Desconto dependa do SRP (suggested retail price, preço a varejo sugerido). Essa tabela
desrespeita a terceira forma normalizada, porque uma coluna não chave, Desconto, depende de uma
outra comuna não-chave, SRP. A independência da coluna significa que é possível alterar todas as
colunas não-chave sem afetar nenhuma outra coluna. Se você alterar um valor do campo SRP, Desconto
seria pertinentemente alterada, o que infringiria a regra. Nesse caso, Desconto seria movida para uma
outra tabela chaveada em SRP.

Modelos de Dados13

Modelo de banco de dados é uma descrição dos tipos de informações que estão armazenadas em um
banco de dados.
Por exemplo, pode informar que o banco armazena informações sobre produtos e que, para cada
produto, são armazenados seu código, preço e descrição.
O modelo não informa QUAIS produtos estão armazenados, apenas que tipo de informações contém.
Para construir um modelo de dados, usa-se uma linguagem de modelagem de dados.
Existem linguagens textuais e linguagens gráficas.
É possível descrever os modelos em diferentes níveis de abstração e com diferentes objetivos.
Cada descrição recebe o nome de esquema de banco de dados.

Modelo Conceitual
É uma descrição de banco de dados de forma independente de implementação num sistema de
gerenciamento.
Registra QUE dados podem aparecer no banco, mas não registra COMO estes dados estão
armazenados no SGBD.
Exemplo de um modelo conceitual textual:
1) Cadastro de Clientes
Dados necessários: nome completo, tipo de pessoa (física ou jurídica), endereço, bairro, cidade,
estado, telefone, e-mail, nome de contato.
2) Pedido
Dados necessários: código do produto, quantidade, código do cliente, código do vendedor.

Modelo Lógico
Compreende uma descrição das estruturas que serão armazenadas no banco e que resulta numa
representação gráfica dos dados de uma maneira lógica, inclusive nomeando os componentes e ações
que exercem uns sobre os outros.
Exemplo de um modelo lógico:

O modelo lógico também pode ser representado assim:


TipoDeProduto (CodTipoProd, DescrTipoProd)

13
http://docente.ifrn.edu.br/abrahaolopes/semestre-2013.1/3.2411.1v-prog-bd/modelos-de-bd-entidade-relacionamento-cardinalidade

49
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Produto (CodProd, DescrProd, PrecoProd, CodTipoProd)
CodTipoProd referencia TipoDeProduto

A técnica de modelagem mais difundida é a abordagem entidade-relacionamento (ER). Nesta técnica,


um modelo conceitual é usualmente representado através de um diagrama, chamado diagrama entidade-
relacionamento (DER).
Por enquanto iremos estudar apenas o modelo relacional, no qual os dados estão organizados em
forma de tabelas.

Modelo Físico
É uma descrição de um banco de dados no nível de abstração visto pelo usuário do SGBD. Assim,
esse modelo depende do SGBD que está sendo usado.
Aqui são detalhados os componentes da estrutura física do banco, como tabelas, campos, tipos de
valores, índices, etc.
Nesse estágio estamos prontos para criar o banco de dados propriamente dito, usando o SGBD
preferido.
Exemplo de tabelas em um BD Relacional.

Modelo Entidade-Relacionamento
- Entidade: é um objeto ou evento do mundo real sobre o qual desejamos manter um registro.
Ex.: Aluno, Carro, Produto, Vendedor, etc.
- Atributo: é uma propriedade ou característica que descreve uma entidade. Também é chamado de
campo. Ex.: Atributos da entidade ALUNO: nome, data de nascimento, telefone, endereço, etc.
João Silva – Homem - 28 anos - Vendedor
- Atributo Chave: é um atributo que deve possuir um valor único em todo o conjunto de entidades.
Este atributo é usado para identificar unicamente um registro da tabela.
Ex.: Matrícula, CPF, código, Renavam, Chassi...
Diferenciamos um atributo chave dos demais atributos colocando um * (asterisco) antes do nome do
atributo ou sublinhando este.

50
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Representamos uma entidade nos diagramas E-R através de um retângulo.

Relacionamentos
No mundo real as entidades nunca estão sozinhas; normalmente estão associadas entre si.
Reconhecer e registrar as associações entre entidades fornece uma descrição muito mais rica do
ambiente.
- Relacionamento: é uma relação entre uma, duas ou várias entidades. Geralmente associamos
através da ação (verbo) entre as entidades.
Ex.: Pai – possui – Filho
Cliente – realiza – Pedido
Vendedor – vende – Produto

- Grau do Relacionamento: é a quantidade de entidades que estão ligadas ao relacionamento.


Relacionamento unário (grau 1) – uma entidade se relaciona com ela mesma.

- Relacionamento Binário (Grau 2): é um relacionamento que liga dois tipos diferentes de entidades.
É o mais comum dos tipos de relacionamentos.

- Relacionamento Ternário (Grau 3): é um relacionamento em que três entidades estão interligadas
por um mesmo relacionamento.

51
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Outros graus de relacionamentos também podem ser usados (quaternário, grau 5, etc...).

- Cardinalidade (Máxima): define a quantidade de ocorrências de uma entidade que poderá estar
associada a outra entidade.
Ex.: Um vendedor pode vender apenas um tipo de produto? Ou dois? Ou três?
Um produto pode ser vendido por apenas um vendedor, ou por todos?
- Relacionamento Binário Um-para-Um (1:1): indica que uma ocorrência da entidade A pode se
relacionar exclusivamente com uma ocorrência da entidade B e vice versa.
Ex.: Um vendedor ocupa um único escritório e um escritório pode ser ocupado por um único vendedor.

- Relacionamento Binário Um- para-Muitos (1:n): uma ocorrência da entidade A pode se relacionar
com várias ocorrências da entidade B, porém o inverso não é permitido.
Ex. Um vendedor atende muitos clientes. Porém, cada cliente tem um vendedor específico.

- Relacionamento Binário Muitos-para-Muitos (n:m): uma ocorrência da entidade A pode se


relacionar com muitas ocorrências da unidade B e vice versa.
Ex.: Um vendedor atende muitos clientes, e um cliente pode ser atendido por diversos vendedores.
Na prática, o relacionamento n:m é dividido em duas relações 1:n e uma nova entidade é criada para
representar o relacionamento.

52
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Cardinalidade (Mínima): define o número mínimo de ocorrências de entidade que precisam estar
associadas a outra entidade (em caráter obrigatório).
Só consideramos duas cardinalidades mínimas: 0 e 1.
Escreve-se: 0..1 1..1 0..n 1..n 0..* 1 ..* etc...
Ex.: Um vendedor ocupa um único escritório, porém é obrigatório que ele tenha um escritório. (Lê-se
no mínimo Um, no máximo Um).
Um escritório pode ser ocupado por um único vendedor, porém pode ser que a sala esteja vazia, ainda
sem vendedor.
(Lê-se no mínimo Zero, no máximo Um).

Formas de Representação
Existe uma variedade enorme de representações gráficas para o modelo entidade relacionamento.

E diversas outras. Escolha a forma que preferir.

MySQL Workbench

Ferramenta gratuita para modelagem e manipulação de bancos de dados MySQL (e compatíveis).

53
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Usaremos a ferramenta de modelagem ER para criar nosso primeiro projeto.
Duplo clique em Add Diagram para abrir a tela de edição de diagramas.
As setas na cor cinza abrem ou fecham outras opções que não usaremos por enquanto.

O ambiente de modelagem de diagramas.

54
1678859 E-book gerado especialmente para DANIEL CRISTIAN
As ferramentas:

Novo, Abrir, Salvar, Salvar Como, Desfazer, Refazer,

Exibir grade, Encaixar na grade.

Os Diferentes Modelos de Bancos de Dados14

Os bancos de dados apareceram no fim dos anos 60, numa época em que a necessidade de um
sistema de gestão da informação flexível se fazia sentir15. Existem cinco modelos de SGBD, diferenciados
de acordo com a representação dos dados que contêm16:

14
http://br.ccm.net/contents/66-os-modelos-de-sgbd
15
http://br.ccm.net/contents/66-os-modelos-de-sgbd
16
http://www.fsma.edu.br/si/edicao3/banco_de_dados_orientado_a_objetos.pdf

55
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- O Modelo Hierárquico: os dados são classificados hierarquicamente, de acordo com uma
arborescência descendente. Este modelo utiliza apontadores entre os diferentes registos. Trata-se do
primeiro modelo de SGBD.

O banco de dados hierárquico foi o primeiro modelo a ser conhecido como modelo de dados. Sua
estrutura é do tipo árvore e sua formação se dá através de registros e links, onde cada registro é uma
coleção de dados e o link é uma associação entre dois registros. Os registros que precedem outros são
chamados de registro pai os demais são chamados de registros filhos. Cada registro tem suas ligações,
o registro pai pode ter vários filhos (1:N), o registro filho só pode ter um pai. Caso um determinado registro
filho tenha a necessidade de ter dois pais é necessário replicar o registro filho. A replicação possui duas
grandes desvantagens: pode causar inconsistência de dados quando houver atualização, e o desperdício
de espaço é inevitável.
Para acessar registros em um modelo hierárquico deve obedecer aos padrões desse modelo. A
navegação deve começar no topo da árvore e da esquerda para direita. Esse modelo foi muito importante
no sistema de banco de dados IMS (Information Management System – é o sistema de banco de dados
mais antigo) da IBM. É importante ressaltar que esse modelo era superior a outros modelos da época o
que o tornou bem utilizado. Apesar desse, ser o melhor da época ele tem algumas desvantagens como:
complexidade dos diagramas de estrutura de árvores, limitações das ligações entre registros - ex.: um
filho só pode ter um pai, a navegação é feita através de ponteiros, complexidade na hora das consultas,
ausência de facilidades de consultas declarativas, só trabalha com dados primitivos.
- O Modelo Rede: como o modelo hierárquico, este modelo utiliza apontadores para os registos.
Contudo, a estrutura já não é necessariamente arborescente no sentido descendente

O modelo em rede surgiu para suprir algumas deficiências do modelo hierárquico. O conceito de
hierarquia foi abolido nesse novo modelo, o que permitiu que um registro estivesse envolvido em várias
associações, ou seja, esse modelo aceitar relacionamentos (N:M), um filho pode ter mais de um pai, ele
só trabalha com dados primitivos. Uma outra característica que difere esse modelo do hierárquico é que
ele utiliza grafos ao invés de árvores.
A Data Base Task Group da CODASYL estabeleceu uma norma para esse modelo, uma linguagem
própria para definição e manipulação de dados. As ferramentas geradoras de relatório da CODASYL
padronizaram dois aspectos importantes dos sistemas gerenciadores de dados: concorrência e
segurança. Com isso, o mecanismo de segurança já permitiu que uma determinada área de banco de
dados fosse bloqueada para evitar acessos simultâneos, quando necessário.
No modelo em rede qualquer nó pode ser acessado sem precisar passar pelo nó raiz. O sistema mais
conhecido dessa implementação é o IDMS da Computer Associates. O diagrama para essa estrutura é
formado por registros e links.
- O Modelo Relacional (SGBDR, Sistema de Gestão de Bancos de Dados Relacionais): os dados
são registados em quadros a duas dimensões (linhas e colunas). A manipulação destes dados faz-se de
acordo com a teoria matemática das relações.

56
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O modelo relacional surgiu com o propósito de aumentar a independência dos dados nos sistemas
gerenciadores de banco de dados; disponibilizar um conjunto de funções apoiadas em álgebra relacional
para armazenar e recuperar dados; permitir processamento ad hoc.
A representação do banco de dados desse modelo é feito através de coleções de tabelas. Então
quando parte para essa visão, é possível ter tabelas de valores, onde cada tabela tem um nome, e dentro
de cada tabela temos as tuplas que são as linhas da tabela, e em cada tabela temos um domínio que é
valor atômico, ou seja, são valores indivisíveis no que diz respeito ao modelo relacional. Cada domínio
possui um formato de dados.
O modelo relacional também tem algumas restrições: restrições inerentes ao modelo de dados (em
uma relação não pode ter tuplas repetidas), restrições baseadas em esquema – são especificações em
DDL (data definition language), que são restrições de domínio, de chave, restrições em null, restrições de
integridade de entidade e restrições de integridade referencial, e restrições baseadas em aplicação.
- O Modelo Dedutivo: os dados são representados sob a forma de tabela, mas a sua manipulação
faz-se por cálculo de predicados
- O Modelo Objeto (SGBDO, Sistema de Gestão de Bancos de Dados Objeto): os dados são
armazenados sob a forma de objetos, quer dizer, de estruturas chamadas classes que apresentam dados
membros. Os campos são instâncias destas classes.

No fim dos anos 90, as bases relacionais são os bancos de dados mais comuns (cerca de três quartos
das bases de dados).
- Modelo Relacional-OO: o modelo relacional OO é a junção do modelo relacional com o modelo OO.
Segue o padrão SQL 1999 e estendem a SQL para incorporar o suporte para o modelo de dados
relacional-objeto, gerencia transações, processamento e otimização de consultas. Como por exemplo,
ele passou a ter construtores de tipos para especificar objetos complexos, passou a ter tuplas e array. Os
construtores set, list, bag ainda não foram adicionados ao modelo. Nesse Modelo passou a ter identidade
de objeto (reference type), encapsulamento de operações e foram adicionados mecanismo de herança e
polimorfismo.
Mesmo com todas essas características a implementação fisicamente continua sendo feita através de
tabelas, ou seja, como um modelo relacional. A semântica da aplicação é modelada e representada
através de objetos, enquanto sua implementação física é feita na forma relacional.
As principais extensões ao modelo relacional que caracterizam os modelos relacionais-objeto são:
definição de novos sistemas de tipos de dados, mais ricos, incluindo tipos de dados complexos;
incorporação de novas funcionalidades ao SGBD para manipular este novos tipos complexos de dados,
suporte a herança, possibilidade de manipulação de objetos diretamente por parte do usuário, extensões
feitas na linguagem SQL, para possibilitar manipular e consultar objetos.
- Modelos Navegacionais: no modelo navegacional, os dados são organizados em registros, que são
coleções de itens de dados, e podem ser armazenados ou recuperados de um banco de dados de forma
conjunta. É possível que um registro possua uma estrutura interna, e elementos (itens de dados)
contínuos podem ser agrupados, que também podem formar outros grupos. Dessa forma, um registro
pode ter uma construção hierárquica. Os registros com a mesma estrutura formam um tipo de registro,
que podem ser considerados equivalentes a uma tabela fora da primeira forma normal, ou ainda a um
objeto complexo. Os tipos de registro possíveis em um banco de dados são definidos em seu esquema.
A principal característica do modelo em redes é permitir a navegação entre os registros, por meio de
Conjuntos de Dados, que possui um registro proprietário e registros membros, implementados por meio

57
1678859 E-book gerado especialmente para DANIEL CRISTIAN
de ponteiros. Basicamente, registros equivalem a entidades e conjuntos de dados equivalem a descrição
dos relacionamentos. Como não há limitação na topologia criada pelos registros e conjuntos, o modelo
permite a criação de redes, de onde ganhou o nome.
Um subconjunto particular do modelo de rede, o modelo hierárquico, limita os relacionamentos a uma
estrutura de árvore, ao contrário da estrutura aplicada pelo modelo de rede completo.
O modelo em redes foi definido formalmente em 1971, pela Conference on Data Systems Languages
(CODASYL), de onde ganhou seu outro nome: modelo CODASYL17.

Questões

01. (CFM - Assistente de Tecnologia da Informação - IADES/2018) Em relação aos bancos de


dados, quais são os modelos de dados utilizados pelos Sistemas Gerenciadores de Banco de Dados
(SGBD)?

(A) Modelo hierárquico, modelo cliente-servidor, modelo plano e modelo orientado a objetos.
(B) Modelo não hierárquico, modelo cliente-servidor, modelo plano e modelo orientado a objetos.
(C) Modelo hierárquico, modelo cliente-servidor, modelo vertical e o modelo orientado a objetos.
(D) Modelo não hierárquico, modelo cliente-servidor, modelo relacional e modelo orientado a objetos.
(E) Modelo hierárquico, modelo em redes, modelo relacional e modelo orientado a objetos.

02. (TER/GO - Técnico Judiciário - CESPE) Julgue o seguinte item, a respeito da modelagem de
dados.
Considere a seguinte situação hipotética.
Em um banco de dados referente a um curso, um aluno pode estar em mais de um curso ao mesmo
tempo. Além disso, na tabela de cursos realizados por aluno, estão presentes as chaves estrangeiras
aluno e curso.
Nessa situação, tanto o código do curso como o código do aluno são chaves primárias nas tabelas
curso e aluno, respectivamente.
( ) Certo ( ) Errado

03. (TER/GO - Técnico Judiciário - CESPE) Acerca de bancos de dados, julgue os seguinte item.
Nas organizações, o emprego de sistemas gerenciadores de banco de dados promove a segurança e
a consistência dos dados, favorecendo a redundância e garantindo a integridade dos dados.
( ) Certo ( ) Errado

04. (IF/CE - Assistente em Administração - IF/CE) Banco de dados pode ser definido como:
(A) uma disposição de dados desordenados.
(B) uma coleção de dados relacionados referentes a um mesmo assunto e organizados de maneira
útil, com o propósito de servir de base, para que o usuário recupere informações.
(C) um conjunto de dados integrados que tem por objetivo impedir acessos indevidos a dados
armazenados.
(D) um conjunto de dados integrados que tem por objetivo atender a requisitos do sistema operacional.
(E) um conjunto de aplicações desenvolvidas especialmente para esta tarefa de criar e manter
informações.

05. (TRT - 17ª Região/ES - Técnico Judiciário - CESPE) Com relação aos conceitos de modelo de
entidade e relacionamento e de modelo conceitual, lógico e físico, julgue os itens subsecutivos.

Chave estrangeira é o atributo ou conjunto de atributos que se refere ou é relacionado com alguma
chave primária ou única de uma tabela, podendo ser inclusive da mesma tabela.
( ) Certo ( ) Errado

06. (PC/SC - Investigador de Polícia - ACAFE) Em relação à Banco de Dados, marque V ou F,


conforme as afirmações a seguir sejam verdadeiras ou falsas.
( ) Banco de Dados é um conjunto de informações organizadas de forma estruturada, como por
exemplo, uma lista telefônica.

17
https://pt.wikiversity.org/wiki/Introdução_à_Ciência_da_Computação/Introdução_aos_Bancos_de_Dados

58
1678859 E-book gerado especialmente para DANIEL CRISTIAN
( ) O Sistema Gerenciador de Banco de Dados, conhecido também como SGBD, é um sistema
informatizado que gerencia um banco de dados.
( ) Empresas de grande porte mantêm suas informações organizadas em Bancos de Dados de forma
que todos os seus empregados tenham acesso a elas.
( ) O Windows Server e o Linux são Gerenciadores de Banco de Dados que utilizam a linguagem de
manipulação de dados SQL.

A sequência correta, de cima para baixo, é:


(A) F - F - V - V
(B) V - F - V - F
(C) F - V - F - F
(D) V - V - F – F

Gabarito

01.E / 02.Certo / 03.Errado / 04.B / 05.Certo

Comentários:

01. Resposta: E
Os modelos de dados utilizados pelos Sistemas Gerenciadores de Banco de Dados são: Modelo
hierárquico, modelo em redes, modelo relacional e modelo orientado a objetos.

02. Resposta: Certo


Toda tabela pode conter uma única e exclusiva chave primária e também poderá conter uma ou MAIS
chaves estrangeiras, portanto a afirmação do texto são chaves primárias nas tabelas curso e aluno.

03. Resposta: Errado


A utilização de um SGBD não favorece a redundância dos dados, uma vez que os dados estarão, ou
tendem a estar, armazenados no mesmo lugar. Um dos princípios de projeto de bancos de dados é a
normalização que objetiva justamente a eliminação de redundâncias e inconsistências nos bancos de
dados.

04. Resposta: B
Um banco de dados é uma coleção de dados inter-relacionados, representando informações sobre um
domínio específico, ou seja, sempre que for possível agrupar informações que se relacionam e tratam de
um mesmo assunto, posso dizer que tenho um banco de dados.

05. Resposta: Certo


A chave estrangeira é uma chave primária de outra tabela.

Exemplo de chave estrangeira (note que a chave estrangeira é uma chave primária em outra tabela).

Quando duas ou mais tabelas estão relacionadas há campos comuns entre elas, este campo é definido
como chave primária quando ele define o identificador exclusivo de uma tabela, e chave estrangeira
quando o campo traz informações que estão armazenadas em outra tabela. Há casos em que uma chave
primária também pode ser uma chave estrangeira (ao mesmo tempo), isso ocorre quando há um
relacionamento N:N.
A coluna Código do Fornecedor da tabela Produtos é uma chave estrangeira porque é também a chave
primária da tabela Fornecedores.

59
1678859 E-book gerado especialmente para DANIEL CRISTIAN
06. Resposta: E
O terceiro item está errado, pois o acesso é restrito aos usuários autorizados. O último item está errado,
pois Windows Server e Linux são sistemas operacionais, e Postgre, Oracle, MySQL, seriam opções para
manipulação de banco de dados.

Data Warehouse (modelagem conceitual para data warehouses, dados


multidimensionais); Modelagem conceitual de dados (a abordagem entidade-
relacionamento);

DATA WAREHOUSE18

Segundo Date (2004) “Data Warehouse (que no português significa, literalmente armazém de dados) é
um deposito de dados orientado por assunto, integrado, não volátil, variável com o tempo, para apoiar as
decisões gerenciais”.

Orientado por Assunto

Refere-se aos sistemas transacionais organizados em uma determinada aplicação de uma empresa.
A orientação por assunto é uma característica importante, pois toda a modelagem do DW é orientada a
partir dos principais assuntos da empresa. Por exemplo uma empresa de arrecadação de impostos, onde
os principais assuntos são os cadastros de contribuintes, impostos a recolher.

Integrado

Essa é a característica mais importante do DW, pois trata da integração, que é feita do ambiente
operacional para as aplicações do DW. A integração é realizada visando padronizar os dados dos diversos
sistemas em uma única representação, para serem transferidos para a base de dados única do DW.

Não Volátil

Nos sistemas transacionais os dados sofrem diversas alterações como, por exemplo, a inclusão,
alteração e exclusão de dados. No ambiente do Data Warehouse os dados, antes de serem carregados,
são filtrados e limpos “gerando informação”. Após esta etapa esses dados sofrem somente operações de
consulta e exclusão, sem que possam ser alterados, e esta característica representa a não-volatilidade.

Variável com o Tempo

A variação em relação ao tempo consiste na manutenção de um histórico de dados em relação ao


período de tempo maior que dos sistemas comuns, isto significa que as técnicas de mineração de dados
não são aplicadas em tempo real, de forma a não comprometer o desempenho dos bancos transacionais
OLTP. Ao analisarmos um dado de um DW, o mesmo sempre estará relacionado a um período
determinado de tempo, pois terá uma chave de tempo que irá indicar o dia no qual esses dados foram
extraídos.
O DW consiste a junção de diversas tecnologias que permitem fazer consulta no banco de dados, para
posterior análise das informações. Devido ao ambiente projetado, o DW tem uma única fonte de dados,
que facilita muito o trabalho do analista, porque os dados que lá existem estão tratados e não há
redundância das informações.
Elementos básicos do DW
Abaixo podemos visualizar os elementos básicos que compõem as arquiteturas de um Data
Warehouse.

18
http://www.devmedia.com.br/data-warehouse/12609

60
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Figura 1 - Elementos DW.

Fonte de Dados

Sistemas transacionais da empresa, pode ser composto por diversas formas de dados.

Data Stage

Composta por uma área de armazenagem e um conjunto de processos. Sua função é extrair os dados
dos sistemas transacionais, proceder a limpeza, a transformação, combinação, de duplicação e
preparação dos dados para o uso no DW. Estes dados não são apresentados ao usuário final.

Servidor de Apresentação

Ambiente onde os dados são organizados e armazenados para consulta direta pelos usuários finais.
Normalmente os dados estão disponíveis nestes servidores em bancos de dados relacionais, mas
também podem estar armazenados em tecnologia OLAP (OnLine Analytical Processing ) já que
muitos data marts trabalham apenas com dados no modelo dimensional.

Data Mart

É um pequeno Data Warehouse, abrangendo uma determinada área de assunto e oferecendo


informações mais detalhadas sobre o mercado (ou departamento) em questão19.
Um Data Mart pode ser criado de duas maneiras:
- Capturando dados diretamente de sistemas transacionais, cada Data Mart buscando as informações
relevantes para o seu mercado;
- Capturando dados de todos os sistemas transacionais em um Data Warehouse central, que por sua
vez alimenta todos os Data Marts.

A primeira opção irá fornecer um Data Mart de forma mais rápida, porém sem levar em consideração
o cruzamento de informações entre as demais áreas de assunto. A segunda opção tende a ser mais
eficiente, porém demandará mais tempo para apresentar resultados.
Tudo depende de como seu negócio funciona e para auxiliar ou criar uma arquitetura dessas, é
imprescindível um Profissional de BI para entender a regra do negócio e a organização da empresa para
montar o projeto com o Data Warehouse e seus Data Marts20.
Exemplo:
Uma empresa possui 500 funcionários separados por departamentos: Vendas, Compras, Estoque,
Marketing, Financeiro e RH.

19
https://www.devmedia.com.br/data-warehouse-ou-data-mart-por-onde-comecar/6996
20
https://www.techtem.com.br/voce-sabe-o-que-sao-data-marts/

61
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O Analista de Business Intelligence foi contratado para montar uma arquitetura no Banco de Dados
para centralizar todas as informações de vários sistemas que a empresa utiliza no Data Warehouse assim
ele pode analisar todos os dados e enviar para Diretoria da empresa as informações relevantes.
Porém cada departamento gera muitas informações, então o analista criou pequenos Data Warehouse
em cada departamento para centralizar e filtrar essas informações antes de enviar para o Data Warehouse
Central. Esses pequenos Data Warehouse são chamados de Data Marts.

Data Mining

Também conhecido como mineração de dados, o Data Mining trabalha em grandes massas de dados
onde existem muitas correlações entre os dados que não são perceptíveis facilmente. Os Data
Warehouses são constituídos, normalmente, de imensa quantidade de dados, há necessidade de uma
ferramenta para varrer automaticamente o DW a fim de pesquisar tendências e padrões através de regras
pré-definidas que dificilmente seriam encontrados em uma pesquisa comum.

Ferramentas de Acesso aos Dados

Maneira em que os dados são extraídos e integrados com cada processo distinto do DW. As funções
para a transformação dos dados são:
- Extração: retirada dos dados dos sistemas transacionais e armazenagem na área de data stage;
- Carga de dimensões processadas: realimentação do processo para garantir a representação correta
dos dados em novo formato.
- Carga, Replicação e Recuperação: quando pronto, o dado é carregado no data mart correspondente
e são criados (ou atualizados) índices para melhorar a performance das consulta.
- Alimentação: apresenta as visões do data mart de acordo com as necessidades dos usuários.
- Carga dos resultados dos modelos: serve para realimentar possíveis modificações no data mart, caso
este não esteja adequado a aplicação que o utiliza.

Arquitetura do DW

Arquitetura do DW pode variar conforme o tipo de assunto abordado, isso ocorre devido as
necessidades que variam de empresa para empresa.

Arquitetura Genérica

A arquitetura genérica compreende a camada dos dados operacionais que serão acessados pela
camada de acesso a dados. As camadas de gerenciamento de processos, transporte e data warehouse
são responsáveis por distribuir os dados e estão no centro da arquitetura. A camada de acesso à
informação possibilita a extração das informações do DW utilizando um conjunto de ferramentas.

Figura 2 - Arquitetura genérica DW.

62
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Arquitetura de Duas Camadas

A arquitetura de duas camadas utiliza um servidor, juntamente com aplicações front end, que são
ferramentas que realizam operações sobre os dados consultados e os transformam em informações úteis
para os usuários, os componentes back end são ferramentas responsáveis pela extração, limpeza e
cargas dos dados, mais conhecidas como ETL também são utilizadas neste tipo de arquitetura.

Figura 3 - Arquitetura duas camadas DW.

Arquitetura de Três Camadas

A arquitetura de três camadas suporta vários usuários e serviços devido a sua flexibilidade, as
informações ficam armazenadas em várias camadas. Na primeira camada estão as interfaces que
trabalham com o usuário, onde geralmente são gráficas. Na segunda camada estão os servidores de
banco de dados e aplicações e, por isso, têm a necessidade de ter um acesso eficiente e veloz aos dados
compartilhados, e na última ficam armazenadas as fontes de dados. A arquitetura de três camadas é a
mais utilizada pelos analistas.

Figura 4 - Arquitetura três camadas DW.

63
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Questão

(TRT - 1ª REGIÃO/RJ - Analista Judiciário - FCC) Ao nível de sumarização dos elementos e de


detalhes disponíveis nos dados em um DW dá-se o nome de:

(A) relacionamento.
(B) capacidade.
(C) granularidade.
(D) integridade.
(E) arquitetura.

Gabarito

Comentários

Resposta: C
Granularidade diz respeito ao nível de detalhe ou de resumo contido nas unidades de dados existentes
no data warehouse. Quanto maior o nível de detalhes, menor o nível de granularidade. O nível de
granularidade afeta diretamente o volume de dados armazenado no data warehouse e ao mesmo tempo
o tipo de consulta que pode ser respondida.
Quando se tem um nível de granularidade muito alto o espaço em disco e o número de índices
necessários se tornam bem menores, porém há uma correspondente diminuição da possibilidade de
utilização dos dados para atender a consultas detalhadas.

Modelo relacional de dados (conceitos básicos, normalização);

CONCEITOS DO MODELO RELACIONAL21

O modelo relacional foi criado por Edgar F. Codd, nos anos 70, e começou a ser usado com o advento
dos bancos de dados relacionais, nos anos 80. A ideia de modelo relacional se baseia no princípio de que
as informações em uma base de dados podem ser consideradas como relações matemáticas e que
podem ser representadas, de maneira uniforme, através do uso de tabelas onde as linhas representam
as ocorrências de uma entidade e as colunas representam os atributos de uma entidade do modelo
conceitual.
As relações no modelo relacional são conjuntos de dados vistos como tabelas cujas operações são
baseadas na álgebra relacional (projeção, produto cartesiano, seleção, junção, união e subtração) e que
manipulam conjuntos de dados ao invés de um único registro, isto é, cada operação realizada afeta um
conjunto de linhas e não apenas uma única linha, ainda que algumas operações possam afetar uma única
linha (conjunto com um único elemento).
Da mesma forma, a resposta das operações de consulta são sempre na forma de uma tabela. As
operações da álgebra relacional são implementadas por linguagens não procedurais de alto nível, sendo
a SQL a linguagem padrão para os bancos de dados relacionais e universalmente usada, tendo sido
padronizada pelo ANSI (American National Standard Institute).

Principais Vantagens do Modelo Relacional

Entre as principais vantagens do modelo relacional podemos citar:


- Independência total dos dados;
- Visão múltipla dos dados;
- Redução acentuada na atividade de desenvolvimento. Particularmente para extração de dados para
relatórios e consultas específicas do usuário;
- Maior segurança no acesso aos dados;

21
https://sites.google.com/site/uniplibancodedados1/aulas/modelo-relacional

64
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Maior agilidade para consulta/atualização;
- Qualidade dos dados garantida por restrições de integridade (Identidade, referencial e de domínio).

As 12 Regras de Codd

Ao definir o modelo relacional, Codd estabeleceu 12 regras para determinação de um banco de dados
relacional.
Estas regras são usadas portanto para se verificar a fidelidade de um banco de dados ao modelo
relacional.
Na prática são poucos os gerenciadores de banco de dados que atendem a todas as 12 regras.
Na maior parte dos casos são atendidas no máximo 10 regras.

1. Toda informação num banco de dados relacional é apresentada a nível lógico na forma de tabelas;
2. Todo dado em um banco de dados relacional tem a garantia de ser logicamente acessível,
recorrendo-se a uma combinação do nome da tabela, um valor de chave e o nome da coluna;
3. Tratamento sistemático de valores nulos; (ausência de informação);
4. O dicionário de dados, catálogo, do banco de dados é baseado no modelo relacional;
5. Há uma linguagem não procedural para a definição, manipulação e controle dos dados;
6. Tratamento das atualizações de visões dos dados;
7. Tratamento de alto nível para inserção, atualização e eliminação de dados;
8. Independência física dos dados; (mudança na memória e no método de acesso, criação de um novo
índice, criação de uma nova coluna);
9. Independência lógica dos dados; (mudança no tamanho de uma coluna);
10. Restrição de Integridade; (Identidade, Referencial e Domínio);
11. Independência de Distribuição dos dados;
12. Não subversão das regras de integridade ou restrições quando se usa uma linguagem hospedeira.

O Conceito de Chave no Modelo Relacional

Chaves e Índices
Chave: o conceito de chave designa um item de busca, ou seja, um dado que será usado para efetuar
uma consulta no banco de dados. É um conceito lógico que só faz sentido para a aplicação e não existe
fisicamente no banco de dados.
Índice: o conceito de índice está associado a um recurso físico usado para otimizar uma consulta no
banco de dados. É um recurso físico, ou seja, um índice é uma estrutura de dados, (endereços), que
existe fisicamente no banco de dados.

Existem diferentes tipos de chave em um modelo relacional. Vamos ver cada um dos tipos de chave
abaixo:
Chave Primária: a chave primária é usada para identificar univocamente uma linha em uma tabela. A
chave primária pode ser composta, ter vários atributos, ou simples, um único atributo. Por exemplo, o
atributo CPF pode ser usado como chave primária para a tabela CLIENTES pois identifica um único cliente
considerando que não existe mais de um cliente com o mesmo CPF.
Chave Secundária: a chave secundária é usada para acessar um conjunto de informações. Pode ser
formada por um único atributo ou mais de um atributo que identifica(m) um subconjunto de dados em uma
tabela. Normalmente, se cria um índice para uma chave secundária como forma de otimizar a consulta
feita por aquela chave ao banco de dados. Por exemplo, podemos ter uma chave secundária formada
pelo CEP para a tabela de CLIENTES pois esta chave identifica um subconjunto de clientes que residem
em uma rua.
Chave Candidata: a chave candidata é formada por um atributo que identifica uma única linha na
tabela. Como uma tabela pode possuir mais de um atributo identificador único podemos ter várias chaves
candidatas em uma única tabela, sendo que apenas uma das chaves candidatas pode ser escolhida para
ser a chave primária da tabela. As demais chaves permanecem como chaves candidatas na tabela. Por
exemplo, podemos ter uma chave candidata formada pela coluna NIT (PISPASEP) na tabela
FUNCIONARIOS que possui como chave primária a coluna MATRICULA. Ambas identificam
univocamente um linha na tabela FUNCIONARIOS, porem a chave NIT é candidata e a chave
MATRICULA é a chave primária.
Chave Estrangeira: a chave estrangeira é formada por atributos que são chave primária em outra
tabela, servindo assim para estabelecer relacionamentos entre as tabelas de um banco de dados. Assim,

65
1678859 E-book gerado especialmente para DANIEL CRISTIAN
quando dizemos que duas tabelas estão relacionadas através de uma coluna devemos observar que em
uma tabela esta coluna será chave primária e na outra tabela ela será uma chave estrangeira que fará a
ligação entre as duas tabelas, estabelecendo o relacionamento. Por exemplo, podemos ter na tabela
FUNCIONARIOS uma chave estrangeira COD_DEPTO que estabelece um relacionamento entre a tabela
FUNCIONARIOS e a tabela DEPTOS, sendo que na tabela DEPTOS a coluna COD_DEPTO é a chave
primária.

Regras de Integridade do Modelo Relacional

O modelo relacional possui duas regras de integridade descritas a seguir.


Integridade de Identidade: a chave primária não pode conter valores nulos. Como toda informação
em um banco de dados relacionam precisa ter uma identidade exclusiva, a chave primária deve ser
obrigatoriamente preenchida. Além disso, a chave primária não deve ter valores repetidos em um tabela,
de forma a garantir que exista apenas uma linha para cada valor definido para a chave primária.
Integridade Referencial: se uma determinada tabela A possui uma chave estrangeira que estabelece
relacionamento com uma tabela B, então o valor da chave estrangeira da tabela A deve ser igual a um
valor de chave primária na tabela B. Esta regra garante que as referências de uma tabela para outra
tabela sejam válidas, de forma que os relacionamentos sejam consistentes e não ocorra inconsistência
nos dados, como haver um funcionário alocado em um departamento que não existe. Assim, para todo
valor de uma coluna que é chave estrangeira em uma tabela, deve haver um valor correspondente na
coluna que é chave primária da tabela com a qual a chave estrangeira faz referência.
Como nem sempre o relacionamento entre tabelas é obrigatório uma chave estrangeira pode possuir
valor nulo.
É importante ressaltar que em um modelo relacional estas regras de integridade são garantidas pelo
gerenciador de banco de dados de forma automática, sem a necessidade de se tratar estas regras no
código da aplicação. Ou seja, o programador não precisa programar validações no sistema para garantir
que estas regras sejam atendidas pois o próprio gerenciador de banco de dados cuida disso.
Integridade de Domínio - restringe o conjunto de valores que podem ser gravados em uma coluna de
uma tabela. Desta forma, somente os valores que pertencem ao domínio podem ser gravados na coluna
da tabela. Outros valores não são permitidos e a atualização é desfeita pelo gerenciador de banco de
dados. O domínio define um conjunto de valores. Quando este domínio é associado a uma coluna de uma
tabela, somente os valores definidos para o domínio podem ser gravados na coluna. Este tipo de restrição
garante a qualidade de dados na base de dados.

Características do Modelo Relacional

- Uma tabela deve ser acessível por qualquer coluna, mesmo que não tenha sido definida como chave;
- O relacionamento entre duas tabelas não existe fisicamente, pois o relacionamento é lógico e
representado através de chaves estrangeiras;
- Uso de linguagens não-procedurais e autocontidas; (SQL);
- Um otimizador de consultas para definição do melhor plano de acesso aos dados.

Regras para Derivação do Modelo Conceitual para o Modelo Relacional

Nesta etapa é feita a transformação das entidades e relacionamentos do modelo E-R para o modelo
relacional, no qual os dados são representados por tabelas. Para tanto, foram definidas regras para esta
transformação de forma a atender às características do modelo relacional.
Estas regras garantem que o modelo relacional estará adequado, alinhado com o modelo conceitual e
sem inconsistências. O resultado desta etapa é um diagrama de tabelas, contendo as tabelas, chaves
primárias, chaves estrangeiras e restrições de integridade, formando assim o modelo lógico que servirá
de base para o projeto físico do Banco de Dados.
Mapeamento das Entidades: toda entidade torna-se uma tabela levando todos os atributos definidos
na entidade que tornam-se colunas na tabela criada. O identificador da entidade torna-se a chave primária
da tabela que não permitirá repetição de valores e nem valores nulos.
Mapeamento de Atributos: os atributos das entidades e dos relacionamentos devem ser gerados de
forma que minimizem o consumo de espaço de armazenamento e torne mais eficiente a consulta de
dados. Devem ser consideradas as características do gerenciador de banco de dados que será utilizado
para implementar o banco de dados físico. Devem ser escolhidos o tipo de dado e tamanho adequados
para cada coluna criada na tabela.

66
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Mapeamento de Relacionamentos: o mapeamento dos relacionamentos implica na transformação
de atributos das entidades em colunas nas tabelas e, em casos específicos, implica também na criação
de novas tabelas a partir de relacionamentos e entidades associativas. Existem diferentes abordagens
dependendo da cardinalidade do relacionamento:
- Relacionamentos que Possuem Atributos: estes relacionamentos se tornam tabelas no caso de
relacionamentos n:n. No caso de relacionamentos 1:n os atributos do relacionamento são transferidos
para a tabela que possui cardinalidade n;
- Relacionamentos São Representados por Chaves Estrangeiras (Foreign Key): todo atributo
correspondente à chave primária de outra relação, base para a integridade referencial, é definido como
uma chave estrangeira);
- Relacionamento 1 para 1 (1:1): uma das entidades envolvidas no relacionamento carrega o atributo
identificador que deve ser definido com chave estrangeira na tabela criada para a entidade fazendo
referência à chave primária da tabela criada para a outra entidade. O Critério para escolher qual tabela
receberá a chave estrangeira depende do negócio que está sendo modelado, sendo necessária análise
caso a caso. Porém em geral se escolhe a tabela onde faz mais sentido colocar o atributo, entidade mais
importante para o negócio.
- Relacionamento 1 para Muitos (1:N): a entidade cuja cardinalidade é N recebe o atributo
identificador da entidade com cardinalidade 1 que será mapeado como uma chave estrangeira na tabela
criada para a entidade com cardinalidade N. Além disso, recebe os atributos do relacionamento se houve.
Caso seja a entidade com cardinalidade N seja uma entidade fraca, então ela recebe o atributo
identificador da entidade com cardinalidade 1 que deve ser mapeado de forma a compor a chave primária
da tabela criada para a entidade com cardinalidade N, como forma de manter a dependência funcional
em relação à entidade com cardinalidade 1.
- Relacionamento Muitos para Muitos (M:N): deve ser criada uma tabela que recebe os atributos
identificadores das entidades que participam do relacionamento, sendo criada a chave primária composta
pelas colunas derivadas dos atributos identificadores. Além disso, a tabela recebe todos os atributos do
relacionamento, se existirem. Este é o único caso em que um relacionamento se torna uma tabela, em
todos os demais casos, são criadas chaves estrangeiras nas tabelas a fim de estabelecer os
relacionamentos.
- Relacionamentos Múltiplos (Ternário, Quaternário, etc.): deve ser criada uma tabela que recebe
tantos atributos identificadores quantas foram as entidades que participam do relacionamento. A chave
primária desta tabela é composta por todos os atributos identificadores. É o caso de relacionamentos
ternário, quaternários, etc.
- Relacionamento Auto-relacionamento: incluir a chave primária da entidade na própria entidade
como chave estrangeira, gerando uma estrutura de acesso a partir dessa chave.
Mapeamento de Generalização/Especialização: deve ser criada uma tabela para a entidade pai e
uma tabela para cada entidade filha. Os atributos comuns às entidades filhas devem ser mapeados na
tabela criada para a entidade pai. As tabelas criadas para cada entidade filha devem receber o atributo
identificador da entidade pai na composição da chave primária e receber também os atributos específicos
da entidade filha correspondente. A entidade pai e a entidade filha também podem ser mapeadas para
uma única tabela.
Mapeamento de Agregações: normalmente gera uma nova tabela que representa a agregação. Esta
tabela normalmente faz relacionamento com uma tabela associativa;

O Modelo Relacional22

No modelo relacional a principal construção para representação dos dados é a relação, uma tabela
com linhas não ordenadas e colunas. Uma relação consiste de um esquema e de uma instância. O
esquema especifica o nome da relação e o nome e o domínio de cada coluna, também denominada
atributo ou campo da relação. O domínio do atributo é referenciado no esquema por seu nome e serve
para restringir os valores que este atributo pode assumir. O esquema de uma relação é invariável ao
longo do tempo, sendo modificado apenas por comandos específicos. Um exemplo de esquema de
relação é:

Students (sid: string, name: string, login: string, age: integer, gpa: real)

22
Fonte: www.ic.unicamp.br/~geovane/mo410-091/

67
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Neste caso está sendo definida a relação de nome Students, com atributos sid, name, login, age e gpa,
cujos domínios são respectivamente string, string, string, integer e real.
A instância de uma relação é o conjunto de linhas, também denominadas tuplas ou registros, distintas
entre si, que compõem a relação em um dado momento. Ela é variável, já que o número de tuplas e o
conteúdo de seus atributos podem variar ao longo do tempo. A instância de uma relação deve seguir
sempre o seu respectivo esquema, respeitando o número de atributos definidos, bem como os seus
domínios. Esta restrição, denominada restrição de domínio, é muito importante. O modelo relacional
somente considera relações que satisfaçam esta restrição. Um exemplo de uma instância para o esquema
Students é ilustrado na Figura 1.

Sid Name Login age gpa


53666 Jones jones@cs 18 3.4
53688 Smith smith@eecs 18 3.2
53650 Smith smith@math 19 3.8
Figura 1 – Exemplo de instância da relação Students.

O número de tuplas (cada linha formada por uma lista ordenada de colunas) que uma dada instância
possui denomina-se cardinalidade da relação e o número de atributos é o seu grau. A instância de relação
da Figura 1 tem cardinalidade 3 e grau 5. Note que a cardinalidade é variável, mas o grau não.
Um banco de dados relacional é um conjunto de uma ou mais relações com nomes distintos. O
esquema do banco de dados relacional é a coleção dos esquemas de cada relação que compõe o banco
de dados.

Criando e Modificando Relações em SQL

A linguagem SQL padrão usa a palavra TABLE para referenciar uma relação. Um subconjunto desta
linguagem forma a Linguagem de Definição de Dados (DDL) que compreende comandos básicos para a
criação, a remoção e a modificação de relações.
A criação de relações em SQL é feita usando-se o comando CREATE TABLE, com a especificação do
respectivo esquema. Por exemplo, para criar a relação Students citada anteriormente tem-se:

CREATE TABLE Students (sid: CHAR(20), name: CHAR(20), login: CHAR(10), age: INTEGER, gpa:
REAL)

Observe que com a execução deste comando está sendo criada apenas a relação, sem que sejam
atribuídos quaisquer valores aos seus atributos.
Para a remoção de uma relação do banco de dados usa-se o comando DROP TABLE. Assim, para
remover a mesma relação Students tem-se:

DROP TABLE Students

Este comando remove a relação especificada, removendo a informação sobre o seu esquema e
também as tuplas da instância atual.
O comando ALTER TABLE é usado para alteração do esquema de uma relação. Ainda considerando
a relação Students para alterar o seu esquema com a adição de um novo campo firstYear cujo domínio é
inteiro usa-se o comando:

ALTER TABLE Students ADD COLUMN firstYear: integer;

A execução deste comando faz com que o esquema da relação seja alterado e com que para cada
tupla da instância corrente seja criado um novo atributo de nome firstYear, atribuindo a ele o valor null.
Um outro subconjunto da linguagem SQL forma a Linguagem de Manipulação de Dados (DDL), que
compreende comandos básicos para a modificação e a recuperação de dados.
O comando INSERT INTO é usado para adicionar novas tuplas a uma relação.
Por exemplo, para inserir uma tupla na relação Students, tem-se:

INSERT INTO Students (sid, name, login, age, gpa) VALUES (53688, ‘Smith’, ‘smith@ee’, 18, 3.2)

Os valores descritos por VALUES, correspondem ao valor que cada atributo terá na nova tupla.

68
1678859 E-book gerado especialmente para DANIEL CRISTIAN
As tuplas de uma relação são removidas por meio do comando DELETE FROM. Pode-se remover
todas as tuplas de uma relação ou apenas aquelas que satisfaçam uma dada condição. Para remover as
tuplas de estudantes cujo nome (name) é Smith na relação Students executa-se o comando:

DELETE FROM Students S WHERE S.name = ‘Smith’

A alteração do valor de atributos que compõem as tuplas é feita usando-se o comando UPDATE
FROM. De forma semelhante ao comando DELETE FROM, podese modificar uma tupla específica ou
várias delas por meio de um único comando. Por exemplo:

UPDATE FROM Students S


SET S.age = S.age + 1, S.gpa = S.gpa – 1
WHERE S.sid = 53688

Este comando altera apenas a tupla da relação Students cujo atributo sid tenha valor igual a 53688.
Já o comando a seguir altera todas as tuplas cujo atributo gpa tenha valor maior ou igual a 3.2.

UPDATE FROM Students S SET S.gpa = S.gpa + 0.5 WHERE S.gpa >= 3.2

Restrições de Integridade sobre Relações

Um bom SGBD deve evitar a entrada de informação incorreta ou inconsistente em sua base de dados,
garantindo, com isso, a qualidade da informação inserida. Uma restrição de integridade (RI) é uma
condição especificada no esquema da base de dados para restringir a informação a ser armazenada. Ou
seja, a RI é uma condição definida que deve ser verdadeira para qualquer instância da base de dados.
Se uma instância da base de dados satisfaz todas as RIs especificadas, então ela é uma instância válida.
Um bom SGBD garante as RIs, não permitindo a existência de instâncias inválidas.
As RI são especificadas e conferidas em 2 momentos diferentes:

Especificação da RI: se dá na definição do esquema da base de dados pelo usuário ou pelo


administrador da base de dados (DBA);
Conferência das RIs: é feita pelo banco de dados toda vez que uma relação é modificada por uma
aplicação sendo executada.

O modelo relacional permite a especificação de vários tipos de RIs. Um deles é a restrição de domínio
citada anteriormente. Outros tipos serão vistos a seguir.

Restrições de Chaves

A restrição de chave serve para garantir que as tuplas de uma relação sejam únicas. Para isso,
identifica um conjunto mínimo de atributos que devem ter valores diferentes em todas as tuplas de uma
instância da relação. Este conjunto de atributos denomina-se chave candidata da relação e deve
satisfazer os seguintes requisitos:
- Não podem existir duas tuplas diferentes com os mesmos valores para estes atributos, ou seja, a
chave identifica unicamente qualquer tupla da relação válida;
- Ao retirar-se qualquer atributo componente da chave, ela deixa de identificar unicamente as tuplas.

Se o segundo requisito for violado, então a chave candidata é uma super-chave. Por exemplo temos
que sid é uma chave para a relação Student, pois identifica cada estudantes. Já o conjunto {sid, gpa} é
uma super-chave da relação, pois ao retirar-se o atributo gpa, o atributo sid continua identificando
unicamente as tuplas. O conjunto de todos os atributos de uma relação formam sempre uma super-chave
desta relação.
Pela definição de relação, é sempre garantida a existência de uma chave. Entretanto, cada relação
pode conter várias chaves candidatas. Cabe ao DBA escolher dentre elas aquela que será a chave
primária, a ser usada pelo banco de dados em operações de otimização. A escolha desta chave é muito
importante e deve ser feita visando garantir a qualidade dos dados. Por exemplo, na relação Students, se
name fosse escolhido como chave primária, não seria possível a existência de estudantes homônimos, o
que talvez não refletisse corretamente os requisitos do sistema. A chave primária não pode assumir valor
null.

69
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Especificando Restrições de Chaves em SQL
A especificação de uma chave e de uma chave primária em SQL é feita respectivamente, pelos
comandos UNIQUE e PRIMARY KEY.
Na execução do comando a seguir está sendo criada a relação Enrolled, cuja chave primária é
composta pelos atributos stdid e cid

CREATE TABLE Enrolled (stdid CHAR(20), cid CHAR(20), grade CHAR(2), PRIMARY KEY (stdid,cid)
)

Caso fosse desejado que esta mesma relação tivesse uma chave composta pelos atributos cid e grade,
sendo sid a chave primária da relação, deveria ser executado o comando:

CREATE TABLE Enrolled (stdid CHAR(20), cid CHAR(20), grade CHAR(2),


UNIQUE (cid, grade), CONSTRAINT EnrolledKey PRIMARY KEY (cid) )

O uso de CONSTRAINT no comando serve para nomear uma restrição, facilitando sua identificação
para impressão de mensagens de erro numa eventual ocorrência de violação.
Note que neste segundo exemplo cada estudante pode cursar apenas um curso e receber uma única
nota para este curso. E ainda que dois estudantes de um mesmo curso não recebem a mesma nota.
Percebe-se, então, que quando usada de forma displicente, uma restrição de integridade pode impedir o
armazenamento de instâncias de base de dados que surgem na prática.

Restrições de Chave Estrangeira


No modelo relacional é comum que a informação de uma relação esteja ligada à informação de outra
relação. Se uma delas é modificada a outra também deve ser checada e modificada, se for o caso, de
maneira a garantir a consistência da informação. Para que o banco de dados possa fazer esta checagem,
é especificada uma restrição envolvendo ambas as relações. Esta restrição denomina-se restrição de
chave estrangeira.
A chave estrangeira é um conjunto de atributos de uma relação que é usado para fazer referência a
uma tupla de outra relação, correspondendo à chave primária da relação referenciada. A chave
estrangeira deve conter o mesmo número de atributos da chave primária da outra relação, e seus
respectivos domínios, mas não necessariamente os mesmos nomes. Além disso, diferente da chave
primária, pode assumir valor null. Por fim pode referenciar a relação que a contém, se necessário.
Considerando a relação Enrolled apresentada anteriormente, queremos garantir que apenas
estudantes com registro válido (sid) podem ser matriculados nos cursos. Para isso, cria-se uma chave
estrangeira em Enrolled que corresponde à chave primária da relação Students, conforme ilustra a Figura
2:

Figura 2 – Exemplo de chave estrangeira.

Neste caso, se a aplicação tentar inserir em Enrolled uma tupla de um estudante de stdid 53673, o
sistema de banco de dados irá rejeitar a operação, pois não existe um estudante em Students com este
sid. Da mesma forma, ao tentar excluir de Students o estudante com sid 53650, o sistema não deve
permitir a exclusão, pois ele está sendo referenciado por Enrolled. Uma outra ação possível para este
caso seria a exclusão de todas as tuplas de Enrolled que façam referência a este estudante.
Se todas as restrições de chave estrangeiras definidas no banco de dados são garantidas, a sua
integridade referencial é alcançada, ou seja, garante-se que não há referências pendentes.

70
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Especificando Restrições de Chave Estrangeira em SQL
O comando FOREIGN KEY identifica a chave estrangeira na criação da relação. Assim, para definir
esta restrição para o exemplo da Figura 2 usa-se o comando:

CREATE TABLE Enrolled (stdid CHAR(20), cid CHAR(20), grade CHAR(2),


PRIMARY KEY (stdid,cid) , FOREIGN KEY (stdid) REFERENCES Students )

Com este comando garante-se que somente estudantes registrados em Students possam ser
matriculados em cursos. Outra restrição colocada por este comando, por meio da chave primária, é que
cada estudante pode ter apenas uma nota por curso.

Restrições Gerais
Restrições de domínio, de chave primária e de chave estrangeiras são consideradas como parte
fundamental do modelo de dados relacional. Entretanto, elas não são suficientes para especificar outras
restrições mais genéricas, como, por exemplo, a definição de intervalos de valores para determinados
atributos.
Para estes outros casos são usadas restrições de tabelas e de assertivas. As restrições de tabelas são
associadas a um única tabela e são checadas sempre que a tabela é alterada. Já as restrições de
assertivas são associadas a várias tabelas e são checadas sempre que uma destas tabelas é modificada.

Garantindo as Restrições de Integridade

Como já visto anteriormente, as RIs são especificadas quando uma relação é criada e são checadas
sempre que uma relação é modificada. O impacto de RIs de domínio, de chave primária e de chave
estrangeiras é direto. Ou seja, sempre que um comando de inserção, exclusão ou atualização causa uma
violação de RI, ele é rejeitado.
Considerando as relações Students e Enrolled já definidas, sendo stdid uma chave estrangeira em
Enrolled que faz referência a Students. O que deveria ser feito se uma tupla de Enrolled com um id de
estudante não existente fosse inserida? Ou então uma tupla de Students com sid nulo (null)? O sistema
deveria apenas rejeitá-las. Mas o que deveria ser feito se uma tupla de Student referenciada por Enrolled
fosse removida? Neste caso com certeza o sistema estaria violando as restrições de integridade
referencial. Para evitar isso, a linguagem SQL provê alternativas de tratamento para estas violações. São
elas:
- Rejeitar a remoção da tupla de Students que é referenciada por Enrolled;
- Remover também todas as tuplas de Enrolled que referenciam a tupla de Students a ser removida;
- Atribuir um valor padrão válido ao stdid das tuplas de Enrolled que referenciam a tupla de Students a
ser removida;
- Atribuir o valor null ao stdid das tuplas de Enrolled que referenciam a tupla de Students removida,
denotando ‘desconhecido’ ou ‘não aplicável’; entretanto, como neste exemplo stdid é parte da chave
primária de Enrolled , esta alternativa estaria violando a RI de chave primária e portanto não poderia ser
aplicada.

Estas opções são válidas também para o caso de atualizações na relação Students.
Os comandos em SQL para implementar estas ações são:

NO ACTION, que é a opção padrão e que rejeita a operação sendo executada;


CASCADE, que remove a tupla da relação referenciada e todas as tuplas que fazem referência à ela;
SET NULL / SET DEFAULT , que atribui um valor à chave estrangeira da tupla referenciada.

A seleção da alternativa a ser utilizada é feita no momento da especificação da RI. Para o exemplo
utilizado, supondo-se que no caso de exclusão seja escolhida a segunda alternativa e no caso de
atualização a primeira delas, a criação da tabela Enrolled se daria pelo comando:

CREATE TABLE Enrolled (stdid CHAR(20), cid CHAR(20), grade CHAR(2),


PRIMARY KEY (stdid,cid), FOREIGN
KEY (stdid) R EFERENCES Students
ON DELETE CASCADE ON UPDATE NO ACTION)

71
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Transações e Restrições

Uma transação é um programa que é executado pelo banco de dados e que pode conter vários
comandos de acesso à base de dados, como consultas, inserções, atualizações, etc. Caso um destes
comandos da transação viole algumas das restrições de integridade especificadas o tratamento padrão é
rejeitar a sua execução. Entretanto, esta abordagem algumas vezes pode ser muito inflexível, devendo
ser dado outro tratamento à situação.
Um exemplo seria, considerando as relações Enrolled e Students, a situação em que para o estudante
ser registrado, ou seja, ter seu id, ele deve estar matriculado em um curso. Entretanto, para o curso existir,
deve haver pelo menos um estudante matriculado. Percebe-se pelas restrições existentes neste caso,
que ao tentar-se inserir a primeira tupla de qualquer das relações, ocorrerá violação e, portanto, as
operações não serão completadas. A única maneira de conseguir realizar a primeira inserção em alguma
delas seria postergando a checagem da restrição, que normalmente ocorreria ao final da execução do
comando INSERT.
A linguagem SQL permite então que a checagem de uma restrição possa ser feita em modo imediato
(IMMEDIATE) ou postergado (DEFERRED). Para isso usa-se o comando SET, indicando a restrição e o
seu modo. Por exemplo o comando SET CONSTRAINT EnrolledKey DEFERRED, faz com que a restrição
de nome EnrolledKey definida anteriormente seja checada somente no momento de efetivação (commit )
da transação.

Consultando Dados Relacionais

Uma consulta em uma base de dados relacionais, normalmente referenciada como query, é uma
questão sobre os dados da base, cuja resposta consiste em uma nova relação que contém o resultado
na forma de tuplas. Por exemplo, usando as relações Students e Enrolled, pode-se querer saber quantos
estudantes são maiores de 18 anos ou quantos estudantes estão matriculados em um determinado curso.
O comando em SQL usado para realização de consultas é o SELECT. Para encontrar todos os
estudantes com 18 anos na instância de relação da Figura 1, executasse o comando SELECT * FROM
Students S WHERE S.age=18. A relação resposta é apresentada na Figura 3:

Sid Name Login age gpa


53666 Jones jones@cs 18 3.4
53688 Smith smith@eecs 18 3.2

Figura 3 – Relação resultante de uma consulta.

O símbolo ‘*’ na consulta indica que a relação resultante deve conter todos os atributos existentes na
relação consultada. Caso fosse desejado obter apenas o nome e a senha destes estudantes, o comando
a ser executado seria o descrito abaixo que produziria o resultado ilustrado na Figura 4.
SELECT name, login FROM Students S WHERE S.age=18

Name Login
Jones jones@cs
Smith smith@eecs

Figura 4 – Relação resultante de uma consulta.

Projeto Lógico da Base de Dados: indo do Modelo ER para o Modelo Relacional

O Modelo Entidade Relacionamento (MER) é adequado para representar um projeto de banco de


dados em alto nível. Uma vez pronto o esquema da base de dados em MER, é preciso converter este
esquema para o esquema de banco de dados a ser usado, geralmente no modelo relacional. Para isso,
existe um conjunto de regras que direcionam o usuário nesta atividade, tratando, inclusive, restrições que
não foram cobertas no esquema conceitual.

72
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Conjuntos Entidade

Um conjunto entidade (CE) é mapeado no modelo relacional com uma relação. Os atributos desta
relação serão os mesmos do CE, bem como seus respectivos domínios. Da mesma forma, a chave
primária também é mantida.
Como exemplo, considere o CE Employees ilustrado na Figura 5, cujos atributos são ssn, name e lot,
sendo ssn a chave primária.

Figura 5 – CE Employess

O comando SQL para criar a relação correspondente é:

CREATE TABLE Employees (ssn CHAR(11), name CHAR(20), lot INTEGER, PRIMARY KEY (ssn)).

Conjuntos Relacionamento

O mapeamento de conjuntos relacionamento (CR) para o modelo relacional pode ser feito de duas
maneiras, abordadas a seguir.

Conjuntos Relacionamento sem Restrições


Um conjunto relacionamento (CR) sem restrições é mapeado numa relação de maneira semelhante
ao conjunto entidade. Entretanto, a chave primária de cada entidade envolvida irá compor os atributos da
nova relação como chaves estrangeiras, juntamente com os atributos descritivos. A chave primária desta
relação será composta pelas chaves estrangeiras.

Figura 6 – CR Works_In

Para o exemplo do CR Works_In ilustrado na Figura 6, o seguinte comando SQL será usado no
mapeamento:

CREATE TABLE Works_In( ssn CHAR(11), did INTEGER, since DATE,


PRIMARY KEY (ssn, did),
FOREIGN KEY (ssn) REFERENCES Employees,
FOREIGN KEY (did) REFERENCES Departments)

No caso de auto relacionamento a relação a ser criada conterá 2 ocorrências da chave primária da
entidade envolvida, as quais comporão a chave primária da relação.

73
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Figura 7 – CR Reports_To

Assim, o mapeamento do conjunto auto-relacionamento Reports_To ilustrado na Figura 7 para o


modelo relacional é feito pelo comando:

CREATE TABLE Reports_To(supervisor_ssn CHAR(11), subordinate_ssn CHAR(11) ,


PRIMARY KEY (supervisor_ssn, subordinate_ssn),
FOREIGN KEY (supervisor_ssn) REFERENCES Employees (ssn),
FOREIGN KEY (subordinate_ssn) REFERENCES Departments(ssn))

Conjuntos Relacionamento com Restrições de Chave


Existem 2 opções para o mapeamento de CR com restrição de chave para o modelo relacional. A
primeira delas é semelhante àquela já apresentada, criando uma relação para representar o CR, com
chaves estrangeiras para as relações envolvidas. Esta abordagem nem sempre é interessante, pois faz
com que seja necessária a combinação de relações para responder às consultas do usuário, podendo se
tornar uma atividade lenta.
A segunda opção é embutir o CR em qualquer das relações referentes às entidades envolvidas, usando
uma chave estrangeira para referenciar a outra entidade. Os atributos descritivos também passam a
integrar a relação que recebeu o CR. Esta solução é mais vantajosa por permitir a obtenção mais rápida
de uma resposta. A única ressalva é que podem ocorrer tuplas nas quais os atributos referentes ao CR
estejam vazios.

Figura 8 – CR Manages.

Considerando a segunda opção, o mapeamento do CR Manages da Figura 8 para o modelo relacional


é feito pelo comando:

CREATE TABLE Dept_Mgr(did INTEGER, dname CHAR(20), budget REAL, ssn CHAR(11), since
DATE,
PRIMARY KEY (did),
FOREIGN KEY (ssn) REFERENCES Employees))

Esta abordagem pode ser aplicada a CR que envolvam mais de 2 CE. Em geral, se o CR envolve N
CE e algumas delas tem restrição de chave, o CR pode ser embutido nesta CE.

Conjuntos Relacionamento com Restrições de Participação


A abordagem é a mesma que no caso anterior, mas com algumas particularidades. Considerando que
no exemplo da Figura 8 existisse uma restrição de participação relacionada ao gerente: todo
departamento tem de ter sempre um gerente. O comando a ser utilizado seria:

74
1678859 E-book gerado especialmente para DANIEL CRISTIAN
CREATE TABLE Dept_Mgr(did INTEGER, dname CHAR(20), budget REAL, ssn CHAR(11) NOT
NULL, since DATE,
PRIMARY KEY (did),
FOREIGN KEY (ssn) REFERENCES Employees ON DELETE NO ACTION)

Neste caso a restrição de participação é garantida pelo NOT NULL usado no atributo ssn indicando
que ele não pode assumir valores null. Ou seja, que ele tem sempre um valor associado. Já o comando
NO ACTION, que é padrão, garante que o empregado não pode ser excluído da relação de empregados,
se ele estiver como gerente do departamento.

Conjuntos de Entidades Fracas


O conjunto de entidades fracas tem sempre participação binária do tipo 1:N, bem como uma restrição
de chave e uma de participação total. Levando-se isso em conta, a abordagem utilizada nos casos
anteriores é ideal. Entretanto, por se tratar de uma entidade dependente de outra, tem chave do tipo
parcial.

Figura 9 – Conjunto Entidade Fraca.

Para o exemplo de conjunto entidade fraca ilustrado na Figura 9 o mapeamento para o modelo
relacional seria feito por meio do comando:

CREATE TABLE Dept_Policy(pname CHAR(20), age INTEGER, cost REAL, ssn CHAR(11),
PRIMARY KEY (pname, ssn),
FOREIGN KEY (ssn) REFERENCES Employees ON DELETE CASCADE)

Note que neste caso, a chave da relação passou a ser composta pela chave primária da entidade fraca
(pname) e pela chave primária da relação que a contém (ssn, em Employees), sendo esta última também
uma chave estrangeira. O comando CASCADE, garante que se o empregado for excluído da relação
Employees, todos os seus dependentes também o serão.

Traduzindo Hierarquias de Classe

Semelhante ao que ocorre com os CR, existem 2 abordagens para tratar as hierarquias ISA. Na
primeira delas, deve ser criada, além da relação referente à super entidade, uma relação para cada
especialização do CE, mantendo seus atributos e acrescentando uma chave estrangeira que referencia
a super entidade. A chave estrangeira desta relação é também a sua chave primária. Caso a relação
correspondente à super entidade seja removida, as relações das entidades especializadas também
devem ser removidas, usando para isso o comando CASCADE.
A segunda abordagem sugere criar uma relação para cada especialização do CE, mas não para a
super entidade. Neste caso cada relação criada conterá, além dos seus atributos, os atributos da super
entidade.
A primeira abordagem é mais genérica e também a mais usada. Sua principal vantagem é permitir que
a relação referente à super classe seja utilizada independente das relações referentes às entidades
especializadas. Já a segunda abordagem obriga que a informação seja sempre obtida a partir de uma
das entidades especializadas, não permitindo a existência da super classe.

Traduzindo Diagramas ER com Agregação

Neste casos o mapeamento é simples e semelhante àqueles discutidos anteriormente: o CR da


agregação é traduzido em uma nova relação, sendo sua chave primária é composta pela chave primária

75
1678859 E-book gerado especialmente para DANIEL CRISTIAN
dos CE envolvidos. Da mesma forma, o CR que envolve esta agregação também é traduzido em uma
nova relação, que terá dentre seus atributos a chave primária da relação que representa a agregação.

Visões

A visão é uma tabela cujas linhas não são explicitamente armazenadas na base de dados, mas sim
computadas, quando necessário, a partir de uma definição em termos de tabelas da base de dados,
denominada tabelas base. O comando em SQL usado para isso é o CREATE VIEW.
Considere as relações Students e Enrolled discutidas anteriormente. Suponha que exista um interesse
constante em recuperar o nome e o identificador dos estudantes que tem nota B em algum curso e
também o identificador deste curso. Uma visão pode ser utilizada neste caso, sem que seja necessária a
criação de uma tabela para isso. Então para o exemplo descrito poderia ser usado o comando:
CREATE VIEW B-Students (name, sid, course) AS SELECT S.sname, S.sid, E.cid FROM Students S,
Enrolled E WHERE S.sid = E.sid AND E.grade = 'B'
A visão definida neste caso é composta por 3 campos (name, sid e course) cujos domínios
correspondem aos mesmos das relações Students (name e sid) e Enrolled (course).

Visões, Independência de Dados e Segurança


O mecanismo de visões provê uma independência lógica dos dados no modelo relacional. Ela pode
ser usada para definir relações em um esquema externo que mascare mudanças ocorridas no esquema
conceitual da base de dados das aplicações. Por exemplo, se o esquema de uma dada relação for
alterado, pode ser definida uma visão que represente o esquema antigo, permitindo que aplicações
continuem executando normalmente sem grandes modificações.
Visões também são muito úteis no contexto de segurança. Com elas é possível permitir que
determinados grupos de usuários acessem somente os dados que eles tem permissão para uso. Por
exemplo, permitir que estudantes vejam apenas o nome e login de outros estudantes, mas não sua senha
ou sua nota em um curso.

Atualizações em Visões
Uma das vantagens do uso de visões é permitir o gerenciamento da apresentação do dados aos
usuários, sem que eles tenham de se preocupar com a maneira como eles estão fisicamente
armazenados na base de dados. Isto normalmente funciona adequadamente, já que a visão pode ser
usada exatamente como uma relação, permitindo a definição de consultas sobre os dados que a
compõem. Esta vantagem acaba levando a uma restrição, pois como se também se trata de uma relação,
é natural o desejo de atualização os seus dados. Entretanto, como a visão é uma tabela virtual, a sua
atualização deve incidir sobre a tabela base, o que nem sempre é possível, devido a problemas como
ambiguidade e restrições de integridade.

Necessidade de Restringir Atualizações de Visões


É possível atualizar ou inserir uma linha em uma visão. No entanto, nem sempre esta atualização fará
parte dela. Isto ocorre porque na verdade a execução destes comandos promovem uma atualização ou
inserção nas tabelas base e dependendo da condição estabelecida na definição da visão, a linha pode
não ser selecionada para compor a tabela virtual, não refletindo a alteração feita.
Assim, por questões práticas, a linguagem SQL permite a atualização de visões apenas em casos
muito específicos, não permitindo, por exemplo, a atualização de colunas calculadas, de visões
compostas por agregados de linhas ou geradas a partir de junções.

Destruindo e Alterando Visões


Uma visão, de maneira semelhante ao que ocorre com as relações, também pode ser removida do
banco de dados. O comando em SQL utilizado para isso é o DROP VIEW.
Note que a remoção de uma relação também pode levar à remoção de uma visão caso tenha sido
usado algum comando que estabeleça esta restrição, como o CASCADE. Já o comando RESTRICT evita
que uma relação seja excluída quando da existência de uma visão associada a ela.

Controles de Acesso e Autenticação

Os controles de acesso, físicos ou lógicos, têm como objetivo proteger equipamentos, aplicativos e
arquivos de dados contra perda, modificação ou divulgação não autorizada. Os sistemas computacionais,

76
1678859 E-book gerado especialmente para DANIEL CRISTIAN
bem diferentes de outros tipos de recursos, não podem ser facilmente controlados apenas com
dispositivos físicos, como cadeados, alarmes ou guardas de segurança.

O Que São Controles de Acesso Lógico?


Os controles de acesso lógico são um conjunto de procedimentos e medidas com o objetivo de proteger
dados, programas e sistemas contra tentativas de acesso não autorizadas feitas por pessoas ou por
outros programas de computador.
O controle de acesso lógico pode ser encarado de duas formas diferentes: a partir do recurso
computacional que se quer proteger e a partir do usuário a quem serão concedidos certos privilégios e
acessos aos recursos.
A proteção aos recursos computacionais baseia-se nas necessidades de acesso de cada usuário,
enquanto que a identificação e autenticação do usuário (confirmação de que o usuário realmente é quem
ele diz ser) é feita normalmente por meio de um identificador de usuário (ID) e por uma senha durante o
processo de logon no sistema.

Que Recursos Devem Ser Protegidos?


A proteção aos recursos computacionais inclui desde aplicativos e arquivos de dados até utilitários e
o próprio sistema operacional. Abaixo serão apresentados os motivos pelos quais esses recursos devem
ser protegidos.

Aplicativos (Programas Fonte e Objeto)


O acesso não autorizado ao código fonte dos aplicativos pode ser usado para alterar suas funções
e a lógica do programa. Por exemplo, em um aplicativo bancário, pode-se zerar os centavos de todas as
contas-correntes e transferir o total dos centavos para uma determinada conta, beneficiando ilegalmente
esse correntista.

Arquivos de Dados
Bases de dados, arquivos ou transações de bancos de dados devem ser protegidos para evitar que os
dados sejam apagados ou alterados sem autorização, como, por exemplo, arquivos com a configuração
do sistema, dados da folha de pagamento, dados estratégicos da empresa.

Utilitários e Sistema Operacional


O acesso a utilitários, como editores, compiladores, softwares de manutenção, monitoração e
diagnóstico deve ser restrito, já que essas ferramentas podem ser usadas para alterar aplicativos,
arquivos de dados e de configuração do sistema operacional, por exemplo.
O sistema operacional é sempre um alvo bastante visado, pois sua configuração é o ponto-chave de
todo o esquema de segurança. A fragilidade do sistema operacional compromete a segurança de todo o
conjunto de aplicativos, utilitários e arquivos.

Arquivos de Senha
A falta de proteção adequada aos arquivos que armazenam as senhas pode comprometer todo o
sistema, pois uma pessoa não autorizada, ao obter identificador (ID) e senha de um usuário privilegiado,
pode, intencionalmente, causar danos ao sistema. Essa pessoa dificilmente será barrada por qualquer
controle de segurança instalado, já que se faz passar por um usuário autorizado.

Arquivos de Log
Os arquivos de log são usados para registrar ações dos usuários, constituindo-se em ótimas fontes de
informação para auditorias futuras. Os logs registram quem acessou os recursos computacionais,
aplicativos, arquivos de dados e utilitários, quando foi feito o acesso e que tipo de operações foram
efetuadas.
Um invasor ou usuário não autorizado pode tentar acessar o sistema, apagar ou alterar dados, acessar
aplicativos, alterar a configuração do sistema operacional para facilitar futuras invasões, e depois alterar
os arquivos de log para que suas ações não possam ser identificadas. Dessa forma, o administrador do
sistema não ficará sabendo que houve uma invasão.

O que os Controles de Acesso Lógico Pretendem Garantir em Relação à Segurança de


Informações?
Os controles de acesso lógico são implantados com o objetivo de garantir que:
- Apenas usuários autorizados tenham acesso aos recursos;

77
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Os usuários tenham acesso apenas aos recursos realmente necessários para a execução de suas
tarefas;
- O acesso a recursos críticos seja bem monitorado e restrito a poucas pessoas;
- Os usuários estejam impedidos de executar transações incompatíveis com sua função ou além de
suas responsabilidades.

O controle de acesso pode ser traduzido, então, em termos de funções de identificação e autenticação
de usuários; alocação, gerência e monitoramento de privilégios; limitação, monitoramento e desabilitação
de acessos; e prevenção de acessos não autorizados.

Como os Usuários São Identificados e Autenticados?


Os usuários dos sistemas computacionais são identificados e autenticados durante um processo,
chamado logon. Os processos de logon são usados para conceder acesso aos dados e aplicativos em
um sistema computacional, e orientam os usuários durante sua identificação e autenticação.
Normalmente esse processo envolve a entrada de um ID (identificação do usuário) e de uma senha
(autenticação do usuário). A identificação define para o computador quem é o usuário e a senha é um
autenticador, isto é, ela prova ao computador que o usuário é realmente quem ele diz ser.

Como Deve Ser Projetado um Processo de Logon para Ser Considerado Eficiente?
O procedimento de logon deve divulgar o mínimo de informações sobre o sistema, evitando fornecer
a um usuário não autorizado informações detalhadas. Um procedimento de logon eficiente deve:
- Informar que o computador só deve ser acessado por pessoas autorizadas;
- Evitar identificar o sistema ou suas aplicações até que o processo de logon esteja completamente
concluído;
- Durante o processo de logon, evitar o fornecimento de mensagens de ajuda que poderiam auxiliar
um usuário não autorizado a completar esse procedimento;
- Validar a informação de logon apenas quando todos os dados de entrada estiverem completos. Caso
ocorra algum erro, o sistema não deve indicar qual parte do dado de entrada está correta ou incorreta,
como, por exemplo, ID ou senha;
- Limitar o número de tentativas de logon sem sucesso (é recomendado um máximo de três tentativas),
e ainda:
a) registrar as tentativas de acesso inválidas;
b) forçar um tempo de espera antes de permitir novas tentativas de entrada no sistema ou rejeitar
qualquer tentativa posterior de acesso sem autorização específica;
c) encerrar as conexões com o computador.
- Limitar o tempo máximo para o procedimento de logon. Se excedido, o sistema deverá encerrar o
procedimento;
- Mostrar as seguintes informações, quando o procedimento de logon no sistema finalizar com êxito:
a) data e hora do último logon com sucesso;
b) detalhes de qualquer tentativa de logon sem sucesso, desde o último procedimento realizado com
sucesso.

O que é Identificação do Usuário?


A identificação do usuário, ou ID, deve ser única, isto é, cada usuário deve ter uma identificação
própria. Todos os usuários autorizados devem ter um ID, quer seja um código de caracteres, cartão
inteligente ou qualquer outro meio de identificação. Essa unicidade de identificação permite um controle
das ações praticadas pelos usuários através dos logs.
No caso de identificação a partir de caracteres, é comum estabelecer certas regras de composição,
como, por exemplo, quantidade mínima e máxima de caracteres, misturando letras, números e símbolos.

O que é Autenticação do Usuário?


Após a identificação do usuário, deve-se proceder à sua autenticação, isto é, o sistema deve confirmar
se o usuário é realmente quem ele diz ser. Os sistemas de autenticação são uma combinação de
hardware, software e de procedimentos que permitem o acesso de usuários aos recursos computacionais.
Na autenticação, o usuário deve apresentar algo que só ele saiba ou possua, podendo até envolver a
verificação de características físicas pessoais. A maioria dos sistemas atuais solicita uma senha (algo
que, supostamente, só o usuário conhece), mas já existem sistemas mais modernos utilizando cartões
inteligentes (algo que o usuário possui) ou ainda características físicas (algo intrínseco ao usuário), como
o formato da mão, da retina ou do rosto, impressão digital e reconhecimento de voz.

78
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Como Orientar os Usuários em Relação às Senhas?
Para que os controles de senha funcionem, os usuários devem ter pleno conhecimento das políticas
de senha da organização, e devem ser orientados e estimulados a segui-las fielmente. Todos os usuários
devem ser solicitados a:
- Manter a confidencialidade das senhas;
- Não compartilhar senhas;
- Evitar registrar as senhas em papel;
- Selecionar senhas de boa qualidade, evitando o uso de senhas muito curtas ou muito longas, que os
obriguem a escrevê-las em um pedaço de papel para não serem esquecidas (recomenda-se tamanho
entre seis e oito caracteres);
- Alterar a senha sempre que existir qualquer indicação de possível comprometimento do sistema ou
da própria senha;
- Alterar a senha em intervalos regulares ou com base no número de acessos (senhas para usuários
privilegiados devem ser alteradas com maior frequência que senhas normais);
- Evitar reutilizar as mesmas senhas;
- Alterar senhas temporárias no primeiro acesso ao sistema;
- Não incluir senhas em processos automáticos de acesso ao sistema (por exemplo, armazenadas em
macros).

Vale lembrar também que utilizar a mesma senha para vários sistemas não é uma boa prática, pois a
primeira atitude de um invasor, quando descobre a senha de um usuário em um sistema vulnerável, é
tentar a mesma senha em outros sistemas a que o usuário tem acesso.

Que Tipos de Senhas Devem ser Evitadas?


Os usuários devem evitar senhas compostas de elementos facilmente identificáveis por possíveis
invasores, como por exemplo:
- Nome do usuário;
- Identificador do usuário (ID), mesmo que seus caracteres estejam embaralhados;
- Nome de membros de sua família ou de amigos íntimos;
- Nomes de pessoas ou lugares em geral;
- Nome do sistema operacional ou da máquina que está sendo utilizada;
- Nomes próprios;
- Datas;
- Números de telefone, de cartão de crédito, de carteira de identidade ou de outros documentos
pessoais;
- Placas ou marcas de carro;
- Palavras que constam de dicionários em qualquer idioma;
- Letras ou números repetidos;
- letras seguidas do teclado do computador (ASDFG, YUIOP);
- Objetos ou locais que podem ser vistos a partir da mesa do usuário (nome de um livro na estante,
nome de uma loja vista pela janela);
- Qualquer senha com menos de seis caracteres.

Alguns softwares são capazes de identificar senhas frágeis, como algumas dessas citadas acima, a
partir de bases de dados de nomes e sequências de caracteres mais comuns, e ainda bloquear a escolha
destas senhas por parte do usuário. Essas bases de dados normalmente fazem parte do pacote de
software de segurança, e podem ser atualizadas pelo gerente de segurança com novas inclusões.

Como Escolher uma Boa Senha?


Geralmente são consideradas boas senhas aquelas que incluem, em sua composição, letras
(maiúsculas e minúsculas), números e símbolos embaralhados, totalizando mais de seis caracteres.
Porém, para ser boa mesmo, a senha tem de ser difícil de ser adivinhada por outra pessoa, mas de fácil
memorização, para que não seja necessário anotá-la em algum lugar. Também é conveniente escolher
senhas que possam ser digitadas rapidamente, dificultando que outras pessoas, a uma certa distância ou
por cima de seus ombros, possam identificar a sequência de caracteres.
Um método bastante difundido é selecionar uma frase significativa para o usuário e utilizar os primeiros
caracteres de cada palavra que a compõe, inserindo símbolos entre eles. É também recomendável não
utilizar a mesma senha para vários sistemas. Se um deles não for devidamente protegido, a senha poderá
ser descoberta e utilizada nos sistemas que, a priori, estariam seguros. Outro conselho: adquira o hábito

79
1678859 E-book gerado especialmente para DANIEL CRISTIAN
de trocar sua senha com frequência. Trocá-la a cada sessenta, noventa dias é considerada uma boa
prática.
Se você realmente não conseguir memorizar sua senha e tiver que escrevê-la em algum pedaço de
papel, tenha pelo menos o cuidado de não identificá-la como sendo uma senha. Não pregue esse pedaço
de papel no próprio computador, não guarde a senha junto com a sua identificação de usuário, e nunca
a envie por e-mail ou a armazene em arquivos do computador.

Como Deve Ser Feita a Concessão de Senhas aos Usuários?


A concessão de senhas deve ser feita de maneira formal, considerando os seguintes pontos:

- Solicitar aos usuários a assinatura de uma declaração, a fim de manter a confidencialidade de sua
senha pessoal (isso pode estar incluso nos termos e condições do contrato de trabalho do usuário);
- Garantir, aos usuários, que estão sendo fornecidas senhas iniciais seguras e temporárias, forçando-
os a alterá-las imediatamente no primeiro logon. O fornecimento de senhas temporárias, nos casos de
esquecimento por parte dos usuários, deve ser efetuado somente após a identificação positiva do
respectivo usuário;
- Fornecer as senhas temporárias aos usuários de forma segura. O uso de terceiros ou de mensagens
de correio eletrônico desprotegidas (não criptografadas) deve ser evitado.

O Que a Instituição Pode Fazer para Proteger e Controlar as Senhas de Acesso a Seus
Sistemas?
O sistema de controle de senhas deve ser configurado para proteger as senhas armazenadas contra
uso não autorizado, sem apresentá-las na tela do computador, mantendo-as em arquivos criptografados
e estipulando datas de expiração (normalmente se recomenda a troca de senhas após 60 ou 90 dias).
Alguns sistemas, além de criptografar as senhas, ainda guardam essas informações em arquivos
escondidos que não podem ser vistos por usuários, dificultando, assim, a ação dos hackers.
Para evitar o uso frequente das mesmas senhas, o sistema de controle de senhas deve manter um
histórico das últimas senhas utilizadas por cada usuário. Deve-se ressaltar, entretanto, que a troca muito
frequente de senhas também pode confundir o usuário, que poderá passar a escrever a senha em algum
lugar visível ou escolher uma senha mais fácil, comprometendo, assim, sua segurança.
O gerente de segurança deve desabilitar contas inativas, sem senhas ou com senhas padronizadas.
Até mesmo a senha temporária fornecida ao usuário pela gerência de segurança deve ser gerada de
forma que já entre expirada no sistema, exigindo uma nova senha para os próximos logons. Portanto,
deve haver um procedimento que force a troca de senha imediatamente após a primeira autenticação,
quando o usuário poderá escolher a senha que será utilizada dali por diante.
Ex-funcionários devem ter suas senhas bloqueadas. Para isso, devem existir procedimentos
administrativos eficientes que informem o gerente de segurança, ou o administrador dos sistemas, da
ocorrência de demissões ou de desligamentos de funcionários. Esses procedimentos, na prática, nem
sempre são seguidos, expondo a organização a riscos indesejáveis.
Também devem ser bloqueadas contas de usuários após um determinado número de tentativas de
acesso sem sucesso. Esse procedimento diminui os riscos de alguém tentar adivinhar as senhas. Atingido
esse limite, só o administrador do sistema poderá desbloquear a conta do usuário, por exemplo.

Existem Outras Formas de Autenticação do Usuário, Além do Uso de Senhas?


Sim. A autenticação dos usuários pode ser feita a partir de tokens, ou ainda, de sistemas biométricos.

O Que São Tokens?


A ideia de fornecer tokens aos usuários como forma de identificá-los é bastante antiga. No nosso dia-
a-dia, estamos frequentemente utilizando tokens para acessar alguma coisa. As chaves que abrem a
porta da sua residência ou seu cartão com tarja magnética para utilizar o caixa eletrônico do banco são
exemplos de tokens. O cartão magnético é ainda uma token especial, pois guarda outras informações,
como, por exemplo, a sua conta bancária.
Token pode ser definida, então, como um objeto que o usuário possui, que o diferencia das outras
pessoas e o habilita a acessar algum objeto. A desvantagem das tokens em relação às senhas é que
essas, por serem objetos, podem ser perdidas, roubadas ou reproduzidas com maior facilidade.

80
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O Que São Cartões Magnéticos Inteligentes?
Os cartões inteligentes são tokens que contêm microprocessadores e capacidade de memória
suficiente para armazenar dados, a fim de dificultar sua utilização por outras pessoas que não seus
proprietários legítimos.
O primeiro cartão inteligente, patenteado em 1975, foi o de Roland Moreno, considerado o pai do cartão
inteligente. Comparado ao cartão magnético, que é um simples dispositivo de memória, o cartão
inteligente não só pode armazenar informações para serem lidas, mas também é capaz de processar
informações. Sua clonagem é mais difícil e a maioria dos cartões inteligentes ainda oferece criptografia.
Normalmente o usuário de cartão inteligente precisa fornecer uma senha à leitora de cartão para que
o acesso seja permitido, como uma medida de proteção a mais contra o roubo de cartões.
As instituições bancárias, financeiras e governamentais são os principais usuários dessa tecnologia,
em função de seus benefícios em relação à segurança de informações e pela possibilidade de redução
de custos de instalações e de pessoal, como, por exemplo, a substituição dos guichês de atendimento ao
público nos bancos por caixas eletrônicos. Os cartões inteligentes têm sido usados em diversas
aplicações: cartões bancários, telefônicos e de crédito, dinheiro eletrônico, segurança de acesso, carteiras
de identidade.

O Que São Sistemas Biométricos?


Os sistemas biométricos são sistemas automáticos de verificação de identidade baseados em
características físicas do usuário. Esses sistemas têm como objetivo suprir deficiências de segurança das
senhas, que podem ser reveladas ou descobertas, e das tokens, que podem ser perdidas ou roubadas.
Os sistemas biométricos automáticos são uma evolução natural dos sistemas manuais de
reconhecimento amplamente difundidos há muito tempo, como a análise grafológica de assinaturas, a
análise de impressões digitais e o reconhecimento de voz. Hoje já existem sistemas ainda mais
sofisticados, como os sistemas de análise da conformação dos vasos sanguíneos na retina.

Que Características Humanas Podem Ser Verificadas por Sistemas Biométricos?


Teoricamente, qualquer característica humana pode ser usada como base para a identificação
biométrica. Na prática, entretanto, existem algumas limitações. A tecnologia deve ser capaz de medir
determinada característica de tal forma que o indivíduo seja realmente único, distinguindo inclusive
gêmeos, porém não deve ser invasiva ou ferir os direitos dos indivíduos.
Um dos problemas enfrentados pelos sistemas biométricos atuais é sua alta taxa de erro, em função
da mudança das características de uma pessoa com o passar dos anos, ou devido a problemas de saúde
ou o próprio nervosismo, por exemplo. A tolerância a erros deve ser estabelecida com precisão, de forma
a não ser grande o suficiente para admitir impostores, nem pequena demais a ponto de negar acesso a
usuários legítimos. Abaixo serão apresentadas algumas características humanas verificadas por sistemas
biométricos existentes:
- Impressões Digitais: são características únicas e consistentes. Nos sistemas biométricos que
utilizam essa opção, são armazenados de 40 a 60 pontos para verificar uma identidade. O sistema
compara a impressão lida com impressões digitais de pessoas autorizadas, armazenadas em sua base
de dados. Atualmente, estão sendo utilizadas impressões digitais em alguns sistemas governamentais,
como, por exemplo, o sistema de previdência social na Espanha e o de registro de eleitores na Costa
Rica;
- Voz: os sistemas de reconhecimento de voz são usados para controle de acesso, porém não são tão
confiáveis como as impressões digitais, em função dos erros causados por ruídos do ambiente e de
problemas de garganta ou nas cordas vocais das pessoas a eles submetidas;
- Geometria da Mão: também é usada em sistemas de controle de acesso, porém essa característica
pode ser alterada por aumento ou diminuição de peso ou pela artrite;
- Configuração da Íris e da Retina: os sistemas que utilizam essas características se propõem a
efetuar identificação mais confiável do que os sistemas que verificam impressões digitais. Entretanto, são
sistemas invasivos, pois direcionam feixes de luz aos olhos das pessoas que se submetem à sua
identificação;
- Reconhecimento Facial Através De Termogramas: o termograma facial é uma imagem captada
por uma câmera infravermelha que mostra os padrões térmicos de uma face. Essa imagem é única e,
combinada com algoritmos sofisticados de comparação de diferentes níveis de temperatura distribuídos
pela face, constitui-se em uma técnica não-invasiva, altamente confiável, não sendo afetada por
alterações de saúde, idade ou temperatura do corpo. São armazenados ao todo 19.000 pontos de
identificação, podendo distinguir gêmeos idênticos, mesmo no escuro. O desenvolvimento dessa

81
1678859 E-book gerado especialmente para DANIEL CRISTIAN
tecnologia tem como um de seus objetivos baratear seu custo para que possa ser usada em um número
maior de aplicações de identificação e de autenticação.

Como Restringir o Acesso aos Recursos Informacionais?


O fato de um usuário ter sido identificado e autenticado não quer dizer que ele poderá acessar qualquer
informação ou aplicativo sem qualquer restrição. Deve-se implementar um controle específico,
restringindo o acesso dos usuários apenas às aplicações, arquivos e utilitários imprescindíveis para
desempenhar suas funções na organização. Esse controle pode ser feito por menus, funções ou arquivos.

Para que Servem os Controles de Menu?


Os controles de menu podem ser usados para restringir o acesso de diferentes categorias de usuários
apenas àqueles aplicativos ou utilitários indispensáveis a cada categoria.
Por exemplo, em um sistema de folha de pagamento, poderá ser apresentado um menu inicial com
três opções diferentes: funcionário, gerente e setor de recursos humanos. Nesse caso, o administrador
do sistema deverá conceder acesso a cada uma das opções de acordo com a função desempenhada
pelo usuário. Portanto, o funcionário só terá acesso a dados da sua folha de pagamento pessoal,
enquanto que o gerente poderá ter acesso a algumas informações da folha de seus funcionários. O setor
de recursos humanos, para poder alimentar a base de dados de pagamento, obterá um nível diferente de
acesso e sua interação com o sistema será feita a partir de menus próprios para a administração de
pessoal. Os menus apresentados após a seleção de uma das opções (funcionário, gerente ou setor de
recursos humanos) serão, portanto, diferentes.

Para que Servem os Controles de Funções de Aplicativos?


No que diz respeito às funções internas dos aplicativos, os respectivos proprietários deverão definir
quem poderá acessá-las e como, por meio de autorização para uso de funções específicas ou para
restrição de acesso às funções de acordo com o usuário (menus de acesso predefinidos), horário ou tipo
de recursos (impressoras, fitas backup).

Como Proteger Arquivos?


A maioria dos sistemas operacionais possui mecanismos de controle de acesso que definem as
permissões e os privilégios de acesso para cada recurso ou arquivo no sistema. Quando um usuário tenta
acessar um recurso, o sistema operacional verifica se as definições de acesso desse usuário e do recurso
desejado conferem. O usuário só conseguirá o acesso se essa verificação for positiva.
Para garantir a segurança lógica, pode-se especificar dois tipos de controle, sob óticas diferentes:
- O que um sujeito pode fazer; ou
- O que pode ser feito com um objeto.

O que São Direitos e Permissões de Acesso?


Definir direitos de acesso individualmente para cada sujeito e objeto pode ser uma maneira um tanto
trabalhosa quando estiverem envolvidas grandes quantidades de sujeitos e objetos. A forma mais comum
de definição de direitos de acesso, nesse caso, é a matriz de controle de acesso. Nessa matriz pode-se
fazer duas análises: uma em relação aos sujeitos; outra, em relação aos objetos.
Na primeira abordagem, cada sujeito recebe uma permissão (ou capacidade) que define todos os seus
direitos de acesso. As permissões de acesso são, então, atributos, associados a um sujeito ou objeto,
que definem o que ele pode ou não fazer com outros objetos. Essa abordagem, no entanto, é pouco
utilizada, já que, na prática, com grandes quantidades de sujeitos e objetos, a visualização exata de quem
tem acesso a um determinado objeto não é tão clara, comprometendo, assim, a gerência de controle de
acesso.
Na segunda abordagem, os direitos de acesso são armazenados com o próprio objeto formando a
chamada lista de controle de acesso (Access Control List (ACL)).

O que São Listas de Controle de Acesso?


Enquanto a permissão de acesso define o que um objeto pode ou não fazer com outros, a lista de
controle de acesso define o que os outros objetos ou sujeitos podem fazer com o objeto a ela associado.
As listas de controle de acesso nada mais são do que bases de dados, associadas a um objeto, que
descrevem os relacionamentos entre aquele objeto e outros, constituindo-se em um mecanismo de
garantia de confidencialidade e integridade de dados.
A definição das listas de controle de acesso deve ser sempre feita pelos proprietários dos recursos, os
quais determinam o tipo de proteção adequada a cada recurso e quem efetivamente terá acesso a eles.

82
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A gerência das listas de controle de acesso, na prática, também é complicada. Para reduzir os
problemas de gerenciamento dessas listas e o espaço de memória ou disco por elas ocupado, costuma-
se agrupar os sujeitos com características semelhantes ou direitos de acesso iguais. Dessa forma, os
direitos de acesso são associados a grupos, e não a sujeitos individualizados. Vale ressaltar que um
sujeito pode pertencer a um ou mais grupos, de acordo com o objeto a ser acessado.

Como Monitorar o Acesso aos Recursos Informacionais?


O monitoramento dos sistemas de informação é feito, normalmente, pelos registros de log, trilhas de
auditoria ou outros mecanismos capazes de detectar invasões. Esse monitoramento é essencial à equipe
de segurança de informações, já que é praticamente impossível eliminar por completo todos os riscos de
invasão por meio da identificação e autenticação de usuários.
Na ocorrência de uma invasão, falha do sistema ou atividade não autorizada, é imprescindível reunir
evidências suficientes para que possam ser tomadas medidas corretivas necessárias ao restabelecimento
do sistema às suas condições normais, assim como medidas administrativas e/ ou judiciais para investigar
e punir os invasores.
A forma mais simples de monitoramento é a coleta de informações, sobre determinados eventos, em
arquivos históricos, mais conhecidos como logs. Com essas informações, a equipe de segurança é capaz
de registrar eventos e de detectar tentativas de acesso e atividades não autorizadas após sua ocorrência.

O que São Logs?


Os logs são registros cronológicos de atividades do sistema que possibilitam a reconstrução, revisão
e análise dos ambientes e das atividades relativas a uma operação, procedimento ou evento,
acompanhados do início ao fim.
Os logs são utilizados como medidas de detecção e monitoramento, registrando atividades, falhas de
acesso (tentativas frustradas de logon ou de acesso a recursos protegidos) ou uso do sistema
operacional, utilitários e aplicativos, e detalhando o que foi acessado, por quem e quando. Com os dados
dos logs, pode-se identificar e corrigir falhas da estratégia de segurança. Por conterem informações
essenciais para a detecção de acesso não autorizado, os arquivos de log devem ser protegidos contra
alteração ou destruição por usuários ou invasores que queiram encobrir suas atividades.

O que Deve Ser Registrado Em Logs?


Devido à grande quantidade de dados armazenada em logs, deve-se levar em consideração que seu
uso pode degradar o desempenho dos sistemas. Sendo assim, é aconselhável balancear a necessidade
de registro de atividades críticas e os custos, em termos de desempenho global dos sistemas.
Normalmente, os registros de log incluem:
- Identificação dos usuários;
- Datas e horários de entrada (logon) e saída do sistema (logoff);
- Identificação da estação de trabalho e, quando possível, sua localização;
- Registros das tentativas de acesso (aceitas e rejeitadas) ao sistema;
- Registros das tentativas de acesso (aceitas e rejeitadas) a outros recursos e dados.

Ao definir o que será registrado, é preciso considerar que quantidades enormes de registros podem
ser inviáveis de serem monitoradas. Nada adianta ter um log se ele não é periodicamente revisado. Para
auxiliar a gerência de segurança na árdua tarefa de análise de logs, podem ser previamente definidas
trilhas de auditoria mais simples e utilizados softwares especializados disponíveis no mercado,
específicos para cada sistema operacional.

Outros Controles de Acesso Lógico


Outro recurso de proteção bastante utilizado em alguns sistemas é o time-out automático, isto é, a
sessão é desativada após um determinado tempo sem qualquer atividade no terminal ou computador.
Para restaurá-la, o usuário é obrigado a fornecer novamente seu ID e senha. Em alguns sistemas
operacionais, o próprio usuário, após sua habilitação no processo de logon, pode ativar e desativar essa
função de time-out. Nesse sentido, os usuários devem ser orientados a:
- Encerrar as sessões ativas, a menos que elas possam ser protegidas por mecanismo de bloqueio
(por exemplo, proteção de tela com senha);
- No caso de terminal conectado ao computador de grande porte, efetuar a desconexão quando a
sessão for finalizada (não apenas desligar o terminal, mas utilizar o procedimento para desconexão).

83
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Como controle de acesso lógico, a gerência de segurança pode ainda limitar o horário de uso dos
recursos computacionais de acordo com a real necessidade de acesso aos sistemas. Pode-se, por
exemplo, desabilitar o uso dos recursos nos fins de semana ou à noite.
É usual também limitar a quantidade de sessões concorrentes, impedindo que o usuário consiga entrar
no sistema ou na rede a partir de mais de um terminal ou computador simultaneamente. Isso reduz os
riscos de acesso ao sistema por invasores, pois se o usuário autorizado já estiver conectado, o invasor
não poderá entrar no sistema. Da mesma forma, se o invasor estiver logado, o usuário autorizado, ao
tentar se conectar, identificará que sua conta já está sendo usada e poderá notificar o fato à gerência de
segurança.

Onde as Regras de Controle de Acesso São Definidas?


As regras de controle e direitos de acesso para cada usuário ou grupo devem estar claramente
definidas no documento da política de controle de acesso da instituição, o qual deverá ser fornecido aos
usuários e provedores de serviço para que tomem conhecimento dos requisitos de segurança
estabelecidos pela gerência.

O Que Considerar na Elaboração da Política de Controle de Acesso?


A política de controle de acesso deve levar em conta:
- Os requisitos de segurança de aplicações específicas do negócio da instituição;
- A identificação de toda informação referente às aplicações de negócio;
- As políticas para autorização e distribuição de informação (por exemplo, a necessidade de conhecer
os princípios e níveis de segurança, bem como a classificação da informação);
- A compatibilidade entre o controle de acesso e as políticas de classificação da informação dos
diferentes sistemas e redes;
- A legislação vigente e qualquer obrigação contratual, considerando a proteção do acesso a dados ou
serviços;
- O perfil de acesso padrão para categorias de usuários comuns;
- O gerenciamento dos direitos de acesso em todos os tipos de conexões disponíveis em um ambiente
distribuído conectado em rede.

Que Cuidados Devem Ser Tomados na Definição das Regras de Controle de Acesso?
Ao especificar as regras de controle de acesso, devem ser considerados os seguintes aspectos:
- Diferenciar regras que sempre devem ser cumpridas das regras opcionais ou condicionais;
- Estabelecer regras baseadas na premissa “Tudo deve ser proibido a menos que expressamente
permitido” ao invés da regra “Tudo é permitido a menos que expressamente proibido”;
- Diferenciar as permissões de usuários que são atribuídas automaticamente por um sistema de
informação daquelas atribuídas por um administrador;
- Priorizar regras que necessitam da aprovação de um administrador antes da liberação daquelas que
não necessitam de tal aprovação.

Que Tipo de Regras de Controle de Acesso Devem Ser Formalizadas na Política?


O acesso aos sistemas de informação deve ser controlado por um processo formal, o qual deverá
abordar, entre outros, os seguintes tópicos:
- Utilização de um identificador de usuário (ID) único, de forma que cada usuário possa ser identificado
e responsabilizado por suas ações;
- Verificação se o usuário obteve autorização do proprietário do sistema de informação ou serviço para
sua utilização;
- Verificação se o nível de acesso concedido ao usuário está adequado aos propósitos do negócio e
consistente com a política de segurança da organização;
- Fornecimento, aos usuários, de documento escrito com seus direitos de acesso. Os usuários deverão
assinar esse documento, indicando que entenderam as condições de seus direitos de acesso;
- Manutenção de um registro formal de todas as pessoas cadastradas para usar cada sistema de
informações;
- Remoção imediata dos direitos de acesso de usuários que mudarem de função ou saírem da
organização;
- Verificação periódica da lista de usuários, com intuito de remover usuários inexistentes e IDs em
duplicidade;
- Inclusão de cláusulas nos contratos de funcionários e prestadores de serviço, que especifiquem as
sanções a que estarão sujeitos em caso de tentativa de acesso não autorizado.

84
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Quem é o Responsável pelos Controles de Acesso Lógico?
A responsabilidade sobre os controles de acesso lógico pode ser tanto do gerente do ambiente
operacional como dos proprietários (ou gerentes) de aplicativos. O gerente do ambiente operacional deve
controlar o acesso à rede, ao sistema operacional e seus recursos e, ainda, aos aplicativos e arquivos de
dados. É responsável, assim, por proteger os recursos do sistema contra invasores ou funcionários não
autorizados.
Enquanto isso, os proprietários dos aplicativos são responsáveis por seu controle de acesso,
identificando quem pode acessar cada um dos sistemas e que tipo de operações pode executar. Por
conhecerem bem o sistema aplicativo sob sua responsabilidade, os proprietários são as pessoas mais
indicadas para definir privilégios de acesso de acordo com as reais necessidades dos usuários.
Dessa forma, as responsabilidades sobre segurança de acesso são segregadas entre o gerente do
ambiente operacional de informática e os gerentes de aplicativos.

Em Que os Usuários Podem Ajudar na Implantação dos Controles de Acesso Lógico?


A cooperação dos usuários autorizados é essencial para a eficácia da segurança. Os usuários devem
estar cientes de suas responsabilidades para a manutenção efetiva dos controles de acesso,
considerando, particularmente, o uso de senhas e a segurança dos equipamentos de informática que
costumam utilizar.

Análise Estática de Código Fonte23

A análise de código para fins de segurança é uma das alternativas bastante utilizadas pelo mercado
para o desenvolvimento de software seguro. Sua difusão se deve primeiro aos resultados, em termos de
localização de bugs de segurança em código e, segundo, pela facilidade de incorporação ao ciclo de
desenvolvimento, uma vez que as principais ferramentas se integram às IDEs mais usadas hoje, como o
Eclipse e o Visual Studio da Microsoft.
Todas as ferramentas comerciais existentes se fundamentam no mesmo princípio, regras e padrões
de codificação suspeitos. Isso as tornam muito dependentes da ação humana. Num primeiro momento,
para desenhar essas regras a partir de muita pesquisa e depois, para avaliar o resultado de uma análise
de código.
As ferramentas de análise de código fundamentam ao Teorema de Rice, que coloca que qualquer
questão não trivial endereçada a um programa pode ser reduzido ao Problema de Halting, isso implica
que os problemas de análise de código são insolúveis no pior caso e, por consequência, que essas
ferramentas são obrigadas a fazer aproximação, cujo resultado é algo não perfeito.
Os principais problemas das ferramentas de análise de código fonte para segurança estão
concentradas em:
Falso Negativo: o programa contém bugs não endereçados pela ferramenta. Isso dá a falsa sensação
de que não existe bugs, que na verdade significa que a ferramenta não foi capaz de encontrar mais
exemplares;
Falso Positivo: a ferramenta endereça bugs não existentes. Isso se refere a duas possibilidades: um
erro propriamente dito, onde a ferramenta localizou um bug que não existe fisicamente; ou há uma
classificação da ferramenta incoerente com as variáveis do ambiente. Por exemplo, a ferramenta poderia
encontrar um bug de SQL Injection, que na realidade, não interessa para o software investigado pelas
suas características de operação.
Vale ressaltar que as ferramentas comerciais procuram reduzir o falso positivo, assumindo o custo de
deixar passar falsos negativos.
As análises de código fonte podem ser divididas de acordo com as seguintes abordagens:
Análise Estática: a análise estática pode compreender as técnicas de busca direta a partir de lista de
strings (grep); a análise léxica, onde os tokens do código fonte são comparados com àqueles contidos
numa biblioteca de vulnerabilidades e análise semântica, onde se prevê como o programa se comportará
em tempo de execução, usando a tecnologia de compiladores (árvore sintática abstrata);
Análise de Fluxo de Controle: usada para caminhar através das condições lógicas do código. O
processo funciona como a seguir:
- Observe uma função e determine cada condição de desvio. Essas incluem loops, switchs, if's e
blocos try/catch;
- Entenda as condições sobre como cada bloco será executado;
- Vá para a próxima função e repita.

23
Fonte: http://softwareseguro.blogspot.com.br/2007/07/ferramentas-de-anlise-cdigo-fonte.html

85
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Análise de fluxo de dados: usada para seguir fluxos de dados dos pontos de entrada aos pontos de
saída. O processo funciona como descrito a seguir:
- Para cada entrada, determine o quanto você acredita na fonte de entrada. Quando em dúvida você
não deve acreditar;
- Siga o fluxo de dados para cada saída possível, registrando ao longo do percurso qualquer tentativa
de validação de dados;
- Vá para a próxima entrada e continue.
A ferramenta mais eficiente é a que consegue fazer uso dessas abordagens combinadas para reduzir
tanto o falso negativo, como o falso positivo. Alguns fornecedores estão na busca por ferramentas efetivas
em temos de análise de código para segurança. Dentre eles, vale destacar:
- Coverity;
- Fortify;
- Ounce Labs;
- Microsoft.

Vale reforçar, que para conseguir tirar o melhor proveito das ferramentas de análise de código fonte
para segurança, seu uso deve estar amparado por um ciclo de desenvolvimento seguro, como o CLASP
ou SDL.

Visão Geral de Backup (SQL Server)24

Este tópico apresenta o componente de backup do SQL Server. O backup do banco de dados do SQL
Server é essencial para proteger seus dados. Esta discussão abrange tipos de backup e restrições de
backup. O tópico também apresenta dispositivos de backup do SQL Server e mídia de backup.

Fazer Backup [Verbo]


Copia os dados ou registros de log de um banco de dados do SQL Server ou de seu log de transações
para um dispositivo de backup, como um disco, a fim de criar um backup de dados ou backup de log.

Backup [Substantivo]
Uma cópia dos dados do SQL Server que pode ser usada para restaurar e recuperar os dados após
uma falha. Um backup dos dados do SQL Server é criado no nível de um banco de dados ou de um ou
mais de seus arquivos ou grupos de arquivos. Não é possível criar backups no nível da tabela. Além dos
backups de dados, o modelo de recuperação completa requer a criação de backups do log de transações.
- modelo de recuperação
Uma propriedade de banco de dados que controla a manutenção do log de transações em um banco
de dados. Existem três modelos de recuperação: simples, completo e bulk-logged. O modelo de
recuperação de banco de dados determina seus requisitos de backup e de restauração.

Restaurar
Um processo multifase que copia todos os dados e páginas de log de um backup do SQL Server para
um banco de dados especificado e, em seguida, efetua roll-forward de todas as transações registradas
no backup, aplicando as alterações registradas para avançar os dados em tempo.

Tipos de Backups

Backup Somente Cópia


Um backup de uso especial que é independente da sequência regular dos backups do SQL Server.

Backup de Dados
Um backup de dados em um banco de dados completo (um backup de banco de dados), um banco de
dados parcial (um backup parcial) ou um conjunto de arquivos de dados ou grupos de arquivos (um
backup de arquivo).

Backup de Banco de Dados


Um backup de um banco de dados. Os backups completos de banco de dados representam todo o
banco de dados no momento em que o backup é concluído. Os backups de banco de dados diferenciais

24
Fonte: https://msdn.microsoft.com/pt-br/library/ms175477(v=sql.120).aspx

86
1678859 E-book gerado especialmente para DANIEL CRISTIAN
contêm somente alterações feitas no banco de dados desde seu backup completo de banco de dados
mais recente.

Backup Diferencial
Um backup de dados que se baseia no backup completo mais recente de um banco de dados completo
ou parcial ou um conjunto de arquivos de dados ou grupos de arquivos (a base diferencial) que contém
somente as extensões de dados alterados desde a base diferencial.
Um backup diferencial parcial registra apenas as extensões de dados que foram alteradas nos grupos
de arquivos desde o backup parcial anterior, conhecido como a base para o diferencial.

Backup Completo
Um backup de dados que contém todos os dados em um banco de dados ou em um conjunto de grupos
de arquivos ou arquivos, além de log suficiente para permitir a recuperação desses dados.

Backup de Log
Um backup de logs de transações que inclui todos os registros de log dos quais não foi feito backup
em um backup de log anterior. (Modelo de recuperação completa).

Backup de Arquivo
Um backup de um ou mais arquivos ou grupos de arquivos de banco de dados.

Backup Parcial
Contém dados apenas de alguns grupos de arquivos em um banco de dados, incluindo os dados no
grupo de arquivos primário, em cada grupo de arquivos de leitura/gravação e em qualquer arquivo
somente leitura especificado opcionalmente.

Termos e Definições de Mídia de Backup

Dispositivo de Backup
Um disco ou dispositivo de fita no qual são gravados backups do SQL Server e nos quais eles podem
ser restaurados. Os backups do SQL Server também podem ser gravados em um serviço de
armazenamento do Blob do Windows Azure. O formato de URL é usado para especificar o destino e o
nome do arquivo de backup.

Mídia de Backup
Uma ou mais fitas ou arquivos de disco nos quais um ou mais backups foram gravados.

Conjunto de Backup
O conteúdo de backup adicionado a um conjunto de mídias por uma operação de backup bem-
sucedida.

Família de Mídia
Os backups criados em um único dispositivo não espelhado ou um conjunto de dispositivos espelhados
em um conjunto de mídias

Conjunto de Mídias
Uma coleção ordenada de mídias de backup, fitas ou arquivos de disco, em que uma ou mais
operações de backup foram gravadas, usando um número e um tipo fixo de dispositivos de backup.

Conjunto de Mídias Espelhado


Várias cópias (espelhos) de um conjunto de mídias.

Fazer Backup e Restaurar Bancos de Dados do SQL Server


O componente de backup e restauração do SQL Server oferece uma proteção essencial para dados
críticos armazenados em bancos de dados do SQL Server. Para minimizar o risco de perda de dados
catastrófica, você precisa fazer backup dos bancos de dados para preservar as modificações feitas nos
dados regularmente. Uma estratégia de backup e restauração bem-planejada ajuda a proteger bancos de
dados contra perda de dados causada por várias falhas. Teste sua estratégia restaurando um conjunto

87
1678859 E-book gerado especialmente para DANIEL CRISTIAN
de backups e recuperando depois seu banco de dados para se preparar para responder com eficiência a
um desastre.
Além do armazenamento local para guardar os backups, o SQL Server também oferece suporte ao
backup e à restauração no serviço de armazenamento de Blob do Windows Azure. .

Benefícios
O backup dos bancos de dados do SQL Server, a execução de procedimentos de restauração de teste
nos backups e o armazenamento de cópias de backups em um local externo seguro evita a perda de
dados potencialmente catastrófica.
Com backups válidos de um banco de dados, você pode recuperar seus dados de muitas falhas, como:
- Falha de mídia;
- Por exemplo, erros de usuário, que removem uma tabela por engano;
- Por exemplo, problemas de hardware, uma unidade de disco danificada ou perda permanente de um
servidor;
- Desastres naturais. Ao usar o Backup do SQL Server para serviço de armazenamento de Blob do
Windows Azure, é possível criar um backup externo em uma região diferente daquela do seu local, para
usar no caso de um desastre natural afetar seu local.

Além disso, os backups de um banco de dados são úteis para fins administrativos rotineiros, como
copiar um banco de dados de um servidor para outro, configurar o espelhamento do banco de dados ou
Grupos de Disponibilidade AlwaysOn e fazer arquivamento.

Componentes e Conceitos

- Fazer Backup [Verbo]


Copia os dados ou registros de log de um banco de dados do SQL Server ou de seu log de transações
para um dispositivo de backup, como um disco, a fim de criar um backup de dados ou backup de log.

- Backup [Substantivo]
Uma cópia dos dados que podem ser usados para restaurar e recuperar os dados após uma falha. Os
backups de um banco de dados também podem ser usados para restaurar uma cópia do banco de dados
em um novo local.

- Dispositivo de Backup
Um disco ou dispositivo de fita no qual os backups do SQL Server serão gravados e nos quais eles
poderão ser restaurados. Os backups do SQL Server também podem ser gravados em um serviço de
armazenamento do Blob do Windows Azure. O formato de URL é usado para especificar o destino e o
nome do arquivo de backup.

- Mídia de Backup
Uma ou mais fitas ou arquivos de disco nos quais um ou mais backups foram gravados.

- Backup de Dados
Um backup de dados em um banco de dados completo (um backup de banco de dados), um banco de
dados parcial (um backup parcial) ou um conjunto de arquivos de dados ou grupos de arquivos (um
backup de arquivo).

- Backup de Banco de Dados


Um backup de um banco de dados. Os backups completos de banco de dados representam todo o
banco de dados no momento em que o backup é concluído. Os backups de banco de dados diferenciais
contêm somente alterações feitas no banco de dados desde seu backup completo de banco de dados
mais recente.

- Backup Diferencial
Um backup de dados que se baseia no backup completo mais recente de um banco de dados completo
ou parcial ou um conjunto de arquivos de dados ou grupos de arquivos (a base diferencial) que contém
somente os dados alterados desde essa base.

88
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Backup Completo
Um backup de dados que contém todos os dados em um banco de dados ou em um conjunto de grupos
de arquivos ou arquivos, além de log suficiente para permitir a recuperação desses dados.

- Backup de Log
Um backup de logs de transações que inclui todos os registros de log dos quais não foi feito backup
em um backup de log anterior. (Modelo de recuperação completa)

- Recuperação
Para retornar um banco de dados a um estado estável e consistente.
Uma fase de inicialização de banco de dados ou de restauração com recuperação que coloca o banco
de dados em um estado de transação consistente.

- Modelo de Recuperação
Uma propriedade de banco de dados que controla a manutenção do log de transações em um banco
de dados. Existem três modelos de recuperação: simples, completo e bulk-logged. O modelo de
recuperação de banco de dados determina seus requisitos de backup e de restauração.

- Restaurar
Um processo multifase que copia todos os dados e páginas de log de um backup do SQL Server para
um banco de dados especificado e, em seguida, efetua roll forward de todas as transações registradas
no backup, aplicando as alterações registradas para avançar os dados no tempo.

Introdução às Estratégias de Backup e Restauração


O backup e a restauração dos dados devem ser personalizados em um ambiente específico e devem
funcionar com os recursos disponíveis. Portanto, um uso confiável de backup e restauração para
recuperação requer uma estratégia de backup e restauração. Uma estratégia de backup e restauração
bem-planejada maximiza a disponibilidade dos dados e minimiza a perda de dados, considerando, ao
mesmo tempo, seus requisitos empresariais específicos.
Coloque o banco de dados e os backups em dispositivos separados. Caso contrário, se o dispositivo
que contém o banco de dados falhar, seus backups ficarão indisponíveis. Colocar os dados e backups
em dispositivos separados também aprimora o desempenho de E/S dos backups gravados e o uso de
produção do banco de dados.
Uma estratégia de backup e restauração contém uma parte de backup e uma parte de restauração. A
parte de backup da estratégia define o tipo e a frequência dos backups, a natureza e velocidade do
hardware exigido para eles, como os backups serão testados, e onde e como a mídia de backup deve ser
armazenada (incluindo considerações de segurança). A parte de restauração da estratégia define quem
é responsável pela execução da restauração e como a restauração deve ser executada para atender às
metas de disponibilidade do banco de dados e minimizar perda de dados. Recomendamos que você
documente seus procedimentos de backup e restauração e mantenha uma cópia da documentação em
seu livro de execuções.
O design de uma estratégia de backup e restauração eficaz requer planejamento, implementação e
teste cuidadosos. O teste é obrigatório. Não existirá uma estratégia de backup até que você tenha
restaurado com êxito os backups em todas as combinações incluídas na estratégia de restauração. Você
deve considerar uma variedade de fatores. Eles incluem o seguinte:
- As metas de produção de sua organização para os bancos de dados, especialmente os requisitos
para disponibilidade e proteção contra perda de dados;
- A natureza de cada um dos seus bancos de dados: o tamanho, os padrões de uso, a natureza de
seu conteúdo, os requisitos dos dados, e assim por diante;
- Restrições de recursos, como hardware, pessoal, espaço para armazenagem de mídia de backup, a
segurança física da mídia armazenada, e assim por diante.

O formato de armazenamento do SQL Server em disco é o mesmo nos ambientes de 64 bits e 32 bits.
Portanto, backup e restauração funcionam em ambientes de 32 bits e 64 bits. Um backup criado em uma
instância de servidor executada em um ambiente pode ser restaurado em uma instância de servidor
executada em outro ambiente.

89
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Impacto do Modelo de Recuperação no Backup e na Restauração
As operações de backup e restauração ocorrem dentro do contexto de um modelo de recuperação. Um
modelo de recuperação é uma propriedade de banco de dados que controla a forma de gerenciamento
do log de transações. Além disso, o modelo de recuperação de um banco de dados determina para quais
tipos de backups e cenários de restauração o banco de dados oferece suporte. Geralmente, um banco
de dados usa o modelo de recuperação simples ou o modelo de recuperação completa. O modelo de
recuperação completa pode ser suplementado alternando para o modelo de recuperação bulk-logged
antes das operações em massa.
A melhor escolha do modelo de recuperação para o banco de dados depende de seus requisitos
empresariais. Para evitar gerenciamento de log de transações e simplificar o backup e a restauração, use
o modelo de recuperação simples. Para minimizar exposição à perda de trabalho, às custas de uma
sobrecarga administrativa, use o modelo de recuperação completa.

- Planejar a Estratégia de Backup


Depois de selecionar um modelo de recuperação que satisfaça seus requisitos empresariais para um
banco de dados específico, você precisa planejar e implementar uma estratégia de backup
correspondente. A melhor estratégia de backup depende de uma série de fatores, dos quais os seguintes
são especialmente significativos:

- Quantas Horas ao Dia os Aplicativos Precisam Acessar o Banco de Dados?


Se houver um período de pouca atividade previsível, recomendamos que você agende backups de
banco de dados completos para aquele período.

- Com que Frequência as Alterações e Atualizações Deverão Ocorrer?


Se as alterações forem frequentes, considere o seguinte:
No modelo de recuperação simples, agende backups diferenciais entre os backups de banco de dados
completos. Um backup diferencial captura só as alterações desde o último backup completo do banco de
dados.
No modelo de recuperação completa, você deve agendar backups de log frequentes. O agendamento
de backups diferenciais entre backups completos pode reduzir o tempo de restauração reduzindo o
número de backups de log a serem restaurados após a restauração dos dados.

- As Alterações Ocorrem Geralmente em uma Pequena Parte do Banco de Dados ou em uma Grande
Parte do Banco de Dados?
Para um banco de dados grande no qual mudanças estão concentradas em uma parte dos arquivos
ou grupos de arquivos, backups parciais e backups de arquivo podem ser úteis.

Quanto Espaço em Disco É Necessário para um Backup Completo de Banco de Dados?

- Estimar o Tamanho de um Backup de Banco de Dados Completo


Antes de implementar uma estratégia de backup e restauração, calcule o quanto de espaço em disco
um backup de banco de dados completo usará. A operação de backup copia os dados no banco de dados
para o arquivo de backup. O backup contém só os dados reais no banco de dados e não qualquer espaço
não utilizado. Portanto, o backup é geralmente menor do que o próprio banco de dados. Você pode
estimar o tamanho de um backup de banco de dados completo usando o procedimento armazenado do
sistema sp_spaceused. Para obter mais informações, consulte sp_spaceused (Transact-SQL).

- Agendar Backups
A execução do backup tem um efeito mínimo sobre as transações em andamento; portanto, as
operações de backup podem ser realizadas durante a operação regular. Você pode executar um backup
do SQL Server com um efeito mínimo sobre as cargas de trabalho de produção.
Depois de decidir os tipos de backups necessários e a frequência de execução de cada tipo,
recomendamos que você agende backups regulares como parte de um plano de manutenção de banco
de dados para o banco de dados.

- Testar seus Backups


Não existirá uma estratégia de restauração até que você tenha testado seus backups. É muito
importante testar sua estratégia de backup completamente para cada um dos bancos de dados,

90
1678859 E-book gerado especialmente para DANIEL CRISTIAN
restaurando uma cópia do banco de dados em um sistema de teste. É necessário testar a restauração de
cada tipo de backup que você pretende usar.
Recomendamos que você mantenha um manual de operações para cada banco de dados. Esse
manual operacional deve documentar o local dos backups, os nomes do dispositivo de backup (se houver)
e o tempo necessário para restaurar os backups de teste

Questões

01. (UFMG - Analista de Tecnologia da Informação - UFMG/2018) O modelo relacional revolucionou


a área de banco de dados; e os sistemas de bancos de dados relacionais são amplamente utilizados no
mercado. Com relação ao modelo relacional de bancos de dados e a SQL, que é um padrão de linguagem
para a criação, manipulação e consulta a SGBDs (Sistemas de Gerenciamento de Bancos de Dados), é
INCORRETO afirmar que:
(A) no mapeamento de um conjunto de relacionamentos de um diagrama ER para um esquema de
bancos de dados relacional, se um conjunto de relacionamentos envolve n conjuntos de entidades e
alguns m deles são vinculados por meio de setas no diagrama ER, temos m chaves candidatas e uma
delas deve ser designada como chave primária.
(B) em uma restrição de chave estrangeira, a chave estrangeira na relação de referência deve ter o
mesmo número de colunas e tipos de dados compatíveis com a chave primária da relação referenciada,
embora os nomes das colunas possam ser diferentes.
(C) uma restrição de chave define um conjunto de campos de uma relação, sendo que algum dos
subconjuntos desse conjunto de campos em uma chave, chamado de chave candidata, é um identificador
único para uma tupla (ou registro) da relação.
(D) o mecanismo de visão fornece o suporte para independência lógica de dados no modelo relacional.
Por exemplo, se o esquema de uma relação armazenada é alterado, podemos definir uma visão com o
esquema antigo e os aplicativos que esperam ver o esquema antigo podem agora usar essa visão com o
mesmo objetivo.

02. (CFM - Analista de Tecnologia da Informação - IADES/2018) O modelo relacional representa o


banco de dados como uma coleção de relações.
ELMASRI, R.; NAVATHE, S. B. Sistemas de Bancos de Dados. 6a ed. São Paulo: Pearson Addison Wesley, 2011

Considerando-se o exposto e o modelo relacional, é correto afirmar que cada linha de uma tabela
recebe o nome de:
(A) grupo.
(B) atributo.
(C) relação.
(D) tupla.
(E) domínio.

03. (TRF - 4ª REGIÃO - Técnico Judiciário - FCC) Num banco de dados relacional,
(A) as tuplas necessitam de informações em todas as colunas.
(B) quando um campo chave de uma tabela Y é inserido como um campo na tabela X, diz-se que ele
é uma chave primária na tabela X.
(C) um registro é um atributo de uma tabela.
(D) uma coluna é uma instância de uma tabela.
(E) os registros não precisam conter informações em todas as colunas.

04. (MF - Analista de Finanças e Controle - ESAF) No Modelo Relacional de banco de dados,
(A) o cabeçalho de uma tabela contém os atributos.
(B) o modelo do atributo é o conjunto de valores permitidos.
(C) o cabeçalho de uma tabela contém instâncias.
(D) o domínio do atributo é a sua descrição.
(E) o corpo da tabela contém relacionamentos qualitativos.

05. (TCE/RO - Técnico em Informática - CESGRANRIO) A chave candidata que é escolhida pelo
projetista do banco de dados como de significado principal para a identificação de entidades, dentro de
um conjunto de entidades, é a chave:

91
1678859 E-book gerado especialmente para DANIEL CRISTIAN
(A) do sistema.
(B) do modelo.
(C) relacional.
(D) primária.
(E) biunívoca.

06. (Petrobras - Engenheiro de Equipamento Júnior - CESGRANRIO) Qual processo aplicado a um


esquema relacional tem por um de seus objetivos minimizar as redundâncias em um banco de dados
relacional?
A) Pivoteamento
B) Normalização
(C) Decomposição
(D) Agregação
(E) Associação

07. (COREN/SP - Administrador de Banco de Dados - VUNESP) Em um banco de dados relacional,


o conceito de instância representa o conjunto:
(A) de usuários válidos em determinado momento.
(B) de valores válidos de cada atributo.
(C) dos atributos que admitem valores nulos.
(D) dos dados armazenados em determinado instante.
(E) dos atributos que formam as chaves primárias de todas as tabelas.

Gabarito

01.C / 02.D / 03.E / 04.A/ 05.D / 06.B / 07.D

Comentários

01. Resposta: C
A restrição de chave serve para garantir que as tuplas de uma relação sejam únicas. Para isso,
identifica um conjunto mínimo de atributos que devem ter valores diferentes em todas as tuplas de uma
instância da relação. Este conjunto de atributos denomina-se chave candidata da relação e deve
satisfazer os seguintes requisitos:

- Não podem existir duas tuplas diferentes com os mesmos valores para estes atributos, ou seja, a
chave identifica unicamente qualquer tupla da relação válida;
- Ao retirar-se qualquer atributo componente da chave, ela deixa de identificar unicamente as tuplas.

02. Resposta: D
Na terminologia do modelo relacional:
- Uma linha é chamada de tupla;
- Um cabeçalho de coluna é chamado de atributo;
- A tabela é chamada de relação;
- O tipo de dados que descreve os tipos de valores que podem aparecer em cada coluna é chamado
de domínio;
- Um domínio é um conjunto de valores atômicos;
- A especificação de um domínio é definida por um tipo de dados do qual os valores de dados que
formam o domínio sejam retirados; e
- A especificação de um nome para este domínio de modo a ajudar na interpretação de seus valores.

03. Resposta: E
Correto! Podem existir campos nulos.

04. Resposta: A
"Na representação gráfica os nomes de atributos são representados no cabeçalho da tabela."

92
1678859 E-book gerado especialmente para DANIEL CRISTIAN
05. Resposta: D
- Chave Candidata: Atributo ou grupamento de atributos que tem a propriedade de identificar
unicamente uma ocorrência da entidade. Pode vir a ser uma chave Primária. A chave candidata que não
é chave primária também chama-se chave Alternativa. A chave candidata deve possuir, como
propriedade, a unicidade e a irredutibilidade.

06. Resposta: B
A normalização de dados é uma série de passos que se seguem no projeto de um banco de dados,
que permitem um armazenamento consistente e um eficiente acesso aos dados em bancos de dados
relacionais. Esses passos reduzem a redundância de dados e as chances dos dados se tornarem
inconsistentes.

07. Resposta: D
A instância de uma relação é o conjunto de linhas, também denominadas tuplas ou registros, distintas
entre si, que compõem a relação em um dado momento.

Postgre-SQL;

POSTGRESQL

Visão Geral da Arquitetura do PostgreSQL25

Edição de SQL e Funções no PSQL

O psql é a principal interface dos desenvolvedores com o PostgreSQL. No entanto, editar códigos no
psql pode ser uma tarefa onerosa. As consultas e funções podem ser extensas e o trabalho se tornar
cansativo e improdutivo. Existem algumas opções que podem ajudar a trabalhar melhor com os códigos,
sem precisar sair do PSQL, que abordamos resumidamente aqui.

Executando Arquivos TXT Salvos Previamente


A maneira que mais utilizo para trabalhar com funções e SQL é executar arquivos txt salvos
previamente. Gosto de ter scripts para as necessidades básicas em seus respectivos lugares, que possam
ser reutilizados, e tem sido bem útil trabalhar desta forma. Edite o seu SQL no editor que achar melhor e
salve-o. Para executa-lo, utilize a sintaxe:

25
Fonte: http://postgresqlbr.blogspot.com.br/2010/02/select-clausulas-for-update-for-share-e.html

93
1678859 E-book gerado especialmente para DANIEL CRISTIAN
\i (nome do arquivo como código SQL)

Editar Código SQL Usando o Editor Padrão


Neste caso, o sistema abre o editor padrão definido para o postgresql. Caso não exista um editor
definido, o postgres perguntará, dentre os disponíveis, qual você deseja utilizar. No meu caso, utilizo o
nano (http://www.nano-editor.org/). É um editor bem simples e fácil de usar, que apresenta boas teclas
de atalho.

Digite:

\e (ou \edit)

O sistema abre a tela do editor para inserir e editar seu texto, permitindo rolar as páginas e manter o
SQL sem problemas. É possível salvar o script para reutilização. Para apenas executar, sem salvar, basta
sair do editor. No caso do NANO, teclando CONTROL+X.

Editar Nova Função no Editor


Ao se digitar \ef, o psql abre o Editor predeterminado, apresentando um "esqueleto de função" para
edição. Basta sair teclando CONTROL+X para criar a função.

Editar Função Existente


Neste caso, utilize a sintaxe:

\ef (nome da função a editar)

94
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Se você esqueceu o nome das funções que deseja editar, pode adaptar a consulta abaixo para
descobrir.

SELECT proname, pronamespace, proowner FROM PG_PROC;

Agora é editar seus scripts, consultas e funções de dentro do psql!

OOPS! Alterando o Editor Padrão!

Já ia me esquecendo! Altere o editor padrão utilizando a sintaxe abaixo:

\set PSQL_EDITOR (caminho do editor)

Select - Cláusulas FOR UPDATE, FOR SHARE e NOWAIT


Influenciar nos mecanismos de bloqueio do banco de dados nem sempre é um processo intuitivo. No
entanto, pode ser bastante útil para garantia da confiabilidade de resultados e para ajustes de
desempenho.

A existência de bloqueios sobre os dados acessados durante o acesso concorrente não pode ser
ignorada em sistemas com grande número de transações concorrentes mesmo no Postgres que utiliza o
protocolo de bloqueios multiversão (MVCC - MultiVersion Concurrency Control).
O PostgreSQL oferece algumas cláusulas relativamente simples que permitem este tipo de controle
no caso de consultas no banco de dados: FOR UPDATE, FOR SHARE e NOWAIT.
O uso de consultas com a cláusula FOR UPDATE obriga o servidor a bloquear os registros consultados
para leitura e escrita durante o transcorrer da transação. Desta forma se garante que o que está sendo
visualizado corresponde ao que está armazenado no banco de dados.
Por sua vez, a cláusula FOR SHARE efetua bloqueio de escrita, mas permite que leituras sejam feitas
aos dados consultados.
Em resumo, a cláusula FOR UPDATE restringe os acessos aos dados consultados, enquanto que a
FOR SHARE explicitamente autoriza acessos de leitura aos dados consultados. O tipo de transação é
que determina se e quando utilizar estas cláusulas.
É importante salientar que ambas as cláusulas se referem apenas os dados que são recuperados na
consulta.
Adicionalmente, a cláusula NOWAIT pode ser utilizada tanto com FOR UPDATE quanto com FOR
SHARE, e força a ocorrência de erro caso o servidor tenha de esperar para a obtenção de bloqueios nos
dados consultados. Desta forma, sacrifica-se a transação para que não se perca tempo na fila de espera
por bloqueios.
As cláusulas UNION, INTERSECT e EXCEPT até o momento não são compatíveis com FOR UPDATE
e FOR SHARE.
Para os próximos exemplo, serão utilizadas as seguintes tabelas:

CREATE TABLE pai (codpai integer,nomepai varchar(50));


CREATE TABLE filho (codpai integer,codfilho integer,nomefilho varchar(50));

95
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplos:

1 - Sintaxe Simples Com FOR UPDATE

SELECT * FROM pai FOR UPDATE;

2 - Sintaxe Simples com FOR SHARE

EXPLAIN SELECT * FROM pai FOR SHARE;

3 - Uso de FOR SHARE em Consulta com Junção

SELECT *
FROM pai p, filho f
WHERE p.codpai = f.codpai
FOR SHARE;

4 - Uso de NOWAIT

SELECT * FROM pai FOR UPDATE NOWAIT;

5 - Uso de FOR UPDATE em Transação de Atualização

BEGIN;
SELECT * FROM pai FOR UPDATE;
UPDATE pai SET nomepai = nomepai ' Father';
COMMIT;

Comandos de Manipulação de DATA/ HORA

O PostgreSQL manipula campos tipo data e hora com muita desenvoltura. Apresenta ainda o tipo de
dados timestamp que engloba data e hora no mesmo campo. As principais funções de manipulação de
datas são:

- current_date
- current_time
- current_timestamp
- now()
- timeofday()
- date_part
- date_trunc
- extract

Estas funções podem ser utilizadas na inserção, atualizações e exclusão dos dados, como será
exemplificado nos próximos posts.

Criação de Índices com Procedures no Postgresql


Vamos ver se consigo ajudar. Esta é uma dúvida do Juliano Fischer:

"Ola, estou com um problema em uma implementação e creio que pode me ajudar.
Preciso criar um subprograma armazenado que crie um indice,se já existir, informar o usuario, cado
contratio, crie um indice.

criaIndice(campo,tabela);"

Não é tão difícil! A solução está abaixo, mas cuidado pois o meu código pode e deve ser melhorado.
Nomes de parâmetros, métodos e variáveis e bons comentários podem ser adicionados.

96
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Além disso, o exemplo só verifica um campo e exige que se tenha cuidado na nomenclatura dos
campos, pois utiliza a cláusula LIKE.

1 - Recuperar Informações dos Índices


A dica é utilizar a pg_indexes, visão de sistema mais amigável para saber se o índice existe:

select * from pg_indexes;

2 - Criar uma Função Parametrizada que Indica se os Índices Existem. Observem que Utilizei a
Linguagem SQL, e não Pl/ Pgsql. Poderia Utilizar Qualquer outra Liguagem, mas a Lógica Seria
Similar
-- Verifica se existe o índice. Retorna ZERO se o índice não for encontrado
CREATE OR REPLACE FUNCTION retindex(in tabela varchar, in campo varchar) RETURNS bigint
AS $$
select count(*) from pg_indexes where tablename = $1 and indexdef like '%' || $2 || '%'
$$
LANGUAGE SQL;

3 - Criar Função Parametrizada que Cria o Índice Caso o Mesmo não Exista, e que Utilize a
Função Anterior. Linguagem Pl/ Pgsql.
-- Verifica e cria novo índice se for o caso
CREATE OR REPLACE FUNCTION criaindex (tabela varchar, campo varchar) RETURNS VARCHAR
AS $$
DECLARE
func_cmd VARCHAR;
BEGIN
if retindex($1,$2) > 0 then
RETURN 'OK';
else
func_cmd := 'CREATE INDEX ' || $1 || '_IDX ON ' || $1 || ' (' || $2 || ')';
EXECUTE func_cmd;
RETURN func_cmd;
end if;
END;
$$ LANGUAGE plpgsql;

4 - Testando Tudo
select retindex ('teste','c1');
select criaindex ('teste', 'c1');

3. Big data: Fundamentos; Técnicas de preparação e apresentação de dados.

BIG DATA: CONCEITO, PREMISSAS E APLICAÇÃO

O Conceito de Big Data

A princípio, podemos definir o conceito de Big Data como sendo conjuntos de dados extremamente
amplos e que, por este motivo, necessitam de ferramentas especialmente preparadas para lidar com
grandes volumes, de forma que toda e qualquer informação nestes meios possa ser encontrada,
analisada e aproveitada em tempo hábil.
De maneira mais simplista, a ideia também pode ser compreendida como a análise de grandes
quantidades de dados para a geração de resultados importantes que, em volumes menores, dificilmente
seriam alcançados.

97
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Não é difícil entender o cenário em que o conceito se aplica: trocamos milhões de e-mails por dia;
milhares de transações bancárias acontecem no mundo a cada segundo; soluções sofisticadas gerenciam
a cadeia de suprimentos de várias fábricas neste exato momento; operadoras registram a todo instante
chamadas e tráfego de dados do crescente número de linhas móveis no mundo todo; sistemas
de ERP coordenam os setores de inúmeras companhias. Enfim, exemplos não faltam - se te perguntarem,
você certamente será capaz de apontar outros sem fazer esforço.
Informação é poder, logo, se uma empresa souber como utilizar os dados que tem em mãos, poderá
entender como melhorar um produto, como criar uma estratégia de marketing mais eficiente, como cortar
gastos, como produzir mais em menos tempo, como evitar o desperdício de recursos, como superar um
concorrente, como disponibilizar serviços para a um cliente especial de maneira satisfatória e assim por
diante.
Perceba, estamos falando de fatores que podem inclusive ser decisivos para o futuro de uma
companhia. Mas, Big Data é um nome relativamente recente (ou, ao menos, começou a aparecer na
mídia recentemente). Isso significa que somente nos últimos anos é que as empresas descobriram a
necessidade de fazer melhor uso de seus grandes bancos de dados?
Pode ter certeza que não. Há tempos que departamentos de TI contemplam aplicações de Data
Mining, Business Intelligence e CRM (Customer Relationship Management), por exemplo, para tratar
justamente de análise de dados, tomadas de decisões e outros aspectos relacionados ao negócio.
A proposta de uma solução de Big Data é a de oferecer uma abordagem ampla no tratamento do
aspecto cada vez mais "caótico" dos dados para tornar as referidas aplicações e todas as outras mais
eficientes e precisas. Para tanto, o conceito considera não somente grandes quantidades de dados, a
velocidade de análise e a disponibilização destes, como também a relação com e entre os volumes.

O Facebook é um exemplo de empresa que se beneficia de Big Data: as bases de dados do serviço
aumentam todo dia e são utilizadas para determinar relações, preferências e comportamentos dos
usuários

98
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Por que Big Data é Tão Importante?

Lidamos com dados desde os primórdios da humanidade. Acontece que, nos tempos atuais, os
avanços computacionais nos permitem guardar, organizar e analisar dados muito mais facilmente e com
frequência muito maior.
Este panorama está longe de deixar de ser crescente. Basta imaginar, por exemplo, que vários
dispositivos em nossas casas - geladeiras, TVs, lavadoras de roupa, cafeteiras, entre outros - deverão
estar conectados à internet em um futuro não muito distante. Esta previsão está dentro do que se conhece
como Internet das Coisas.
Se olharmos para o que temos agora, já veremos uma grande mudança em relação às décadas
anteriores: tomando como base apenas a internet, pense na quantidade de dados que são gerados
diariamente somente nas redes sociais; repare na imensa quantidade de sites na Web; perceba que você
é capaz de fazer compras on-line por meio até do seu celular, quando o máximo de informatização que
as lojas tinham em um passado não muito distante eram sistemas isolados para gerenciar os seus
estabelecimentos físicos.
As tecnologias atuais nos permitiram - e permitem - aumentar exponencialmente a quantidade de
informações no mundo e, agora, empresas, governos e outras instituições precisam saber lidar com esta
"explosão" de dados. O Big Data se propõe a ajudar nesta tarefa, uma vez que as ferramentas
computacionais usadas até então para gestão de dados, por si só, já não podem fazê-lo satisfatoriamente.
A quantidade de dados gerada e armazenada diariamente chegou a tal ponto que, hoje, uma estrutura
centralizada de processamento de dados já não faz mais sentido para a maioria absoluta das grandes
entidades. O Google, por exemplo, possui vários data centers para dar conta de suas operações, mas
trata todos de maneira integrada. Este "particionamento estrutural", é bom destacar, não é uma barreira
para o Big Data - em tempos de computação nas nuvens, nada mas trivial.

Os 'Vs' do Big Data: Volume, Velocidade, Variedade, Veracidade e Valor

No intuito de deixar a ideia de Big Data mais clara, alguns especialistas passaram a resumir o assunto
em aspectos que conseguem descrever satisfatoriamente a base do conceito: os cincos 'Vs' - volume,
velocidade e variedade, com os fatores veracidade e valor aparecendo posteriormente.
O aspecto do volume (volume) você já conhece. Estamos falando de quantidades de dados realmente
grandes, que crescem exponencialmente e que, não raramente, são subutilizados justamente por estarem
nestas condições.
Velocidade (velocity) é outro ponto que você já assimilou. Para dar conta de determinados problemas,
o tratamento dos dados (obtenção, gravação, atualização, enfim) deve ser feito em tempo hábil - muitas
vezes em tempo real. Se o tamanho do banco de dados for um fator limitante, o negócio pode ser
prejudicado: imagine, por exemplo, o transtorno que uma operadora de cartão de crédito teria - e causaria
- se demorasse horas para aprovar um transação de um cliente pelo fato de o seu sistema de segurança
não conseguir analisar rapidamente todos os dados que podem indicar uma fraude.
Variedade (variety) é outro aspecto importante. Os volume de dados que temos hoje são consequência
também da diversidade de informações. Temos dados em formato estruturados, isto é, armazenados em
bancos como PostgreSQL e Oracle, e dados não estruturados oriundos de inúmeras fontes, como
documentos, imagens, áudios, vídeos e assim por diante. É necessário saber tratar a variedade como
parte de um todo - um tipo de dado pode ser inútil se não for associado a outros.
O ponto de vista da veracidade (veracity) também pode ser considerado, pois não adianta muita coisa
lidar com a combinação "volume + velocidade + variedade" se houver dados não confiáveis. É necessário
que haja processos que garantam o máximo possível a consistência dos dados. Voltando ao exemplo da
operadora de cartão de crédito, imagine o problema que a empresa teria se o seu sistema bloqueasse
uma transação genuína por analisar dados não condizentes com a realidade.
Informação não é só poder, informação também é patrimônio. A combinação "volume + velocidade +
variedade + veracidade", além de todo e qualquer outro aspecto que caracteriza uma solução de Big Data,
se mostrará inviável se o resultado não trouxer benefícios significativos e que compensem o investimento.
Este é o ponto de vista do valor (value).
É claro que estes cinco aspectos não precisam ser tomados como a definição perfeita. Há quem
acredite, por exemplo, que a combinação "volume + velocidade + variedade" seja suficiente para transmitir
uma noção aceitável do Big Data. Sob esta óptica, os aspectos da veracidade e do valor seriam
desnecessários, porque já estão implícitos no negócio - qualquer entidade séria sabe que precisa de
dados consistentes; nenhuma entidade toma decisões e investe se não houver expectativa de retorno.

99
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O destaque para estes dois pontos talvez seja mesmo desnecessário por fazer referência ao que
parece óbvio. Por outro lado, a sua consideração pode ser relevante porque reforça os cuidados
necessários a estes aspectos: uma empresa pode estar analisando redes sociais para obter uma
avaliação da imagem que os clientes têm de seus produtos, mas será que estas informações são
confiáveis ao ponto de não ser necessário a adoção de procedimentos mais criteriosos? Será que não se
faz necessário um estudo mais profundo para diminuir os riscos de um investimento antes de efetuá-lo?
De qualquer forma, os três primeiros 'Vs' - volume, velocidade e variedade - podem até não oferecer a
melhor definição do conceito, mas não estão longe de fazê-lo. Entende-se que Big Data trata apenas de
enormes quantidades de dados, todavia, você pode ter um volume não muito grande, mas que ainda se
encaixa no contexto por causa dos fatores velocidade e variedade.

Soluções de Big Data

Além de lidar com volumes extremamente grandes de dados dos mais variados tipos, soluções de Big
Data também precisam trabalhar com distribuição de processamento e elasticidade, isto é, suportar
aplicações com volumes de dados que crescem substancialmente em pouco tempo.
O problema é que os bancos de dados "tradicionais", especialmente aqueles que exploram o modelo
relacional, como o MySQL, o PostgreSQL e o Oracle, não se mostram adequados a estes requisitos, já
que são menos flexíveis.
Isso acontece porque bancos de dados relacionais normalmente se baseiam em quatro propriedades
que tornam a sua adoção segura e eficiente, razão pela qual soluções do tipo são tão populares:
Atomicidade, Consistência, Isolamento e Durabilidade. Esta combinação é conhecida como ACID, sigla
para o uso destes termos em inglês: Atomicity, Consistency, Isolation e Durability. Vejamos uma breve
descrição de cada uma:
Atomicidade: toda transação deve ser atômica, isto é, só pode ser considerada efetivada se
executada completamente;
Consistência: todas as regras aplicadas ao banco de dados devem ser seguidas;
Isolamento: nenhuma transação pode interferir em outra que esteja em andamento ao mesmo tempo;
Durabilidade: uma vez que a transação esteja concluída, os dados consequentes não podem ser
perdidos.

O problema é que este conjunto de propriedades é por demais restritivo para uma solução de Big Data.
A elasticidade, por exemplo, pode ser inviabilizada pela atomicidade e pela consistência. É neste ponto
que entra em cena o conceito de NoSQL, denominação que muitos atribuem à expressão em inglês "Not
only SQL", que em tradução livre significa "Não apenas SQL" (SQL -Structured Query Language - é, em
poucas palavras, uma linguagem própria para se trabalhar com bancos de dados relacionais).
O NoSQL faz referência às soluções de bancos de dados que possibilitam armazenamento de diversas
formas, não se limitando ao modelo relacional tradicional. Bancos do tipo são mais flexíveis, sendo
inclusive compatíveis com um grupo de premissas que "compete" com as propriedades ACID:
a BASE (Basically Available, Soft state, Eventually consistency - Basicamente disponível, Estado Leve,
Eventualmente consistente).
Não é que bancos de dados relacionais tenham ficado ultrapassados - eles são e continuarão por muito
tempo sendo úteis a uma série de aplicações. O que acontece é que, geralmente, quanto maior um banco
de dados se torna, mais custoso e trabalhoso ele fica: é preciso otimizar, acrescentar novos servidores,
empregar mais especialistas em sua manutenção, enfim.
Via de regra, escalar (torná-lo maior) um bancos de dados NoSQL é mais fácil e menos custoso. Isso
é possível porque, além de contar com propriedades mais flexíveis, bancos do tipo já são otimizados para
trabalhar com processamento paralelo, distribuição global (vários data centers), aumento imediato de sua
capacidade e outros.
Além disso, há mais de uma categoria de banco de dados NoSQL, fazendo com que soluções do tipo
possam atender à grande variedade de dados que existe, tanto estrurados, quanto não estruturados:
bancos de dados orientados a documentos, bancos de dados chave/valor, bancos de dados de grafos,
enfim.
Exemplos de bancos de dado NoSQL são o Cassandra, o MongoDB, o HBase, o CouchDB e o Redis.
Mas, quando o assunto é Big Data, apenas um banco de dados do tipo não basta. É necessário também
contar com ferramentas que permitam o tratamento dos volumes. Neste ponto, o Hadoop é, de longe, a
principal referência.

100
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplos de bancos de dados noSQL: Cassandra, MongoDB, HBase, CouchDB e Redis.

O que é Hadoop?
O Hadoop é uma plataforma open source desenvolvida especialmente para processamento e análise
de grandes volumes de dados, sejam eles estruturados ou não estruturados. O projeto é mantido pela
Apache Foundation, mas conta com a colaboração de várias empresas, como Yahoo!, Facebook, Google
e IBM.
Pode-se dizer que o projeto teve início em meados de 2003, quando o Google criou um modelo de
programação que distribui o processamento a ser realizado entre vários computadores para ajudar o seu
mecanismo de busca a ficar mais rápido e livre da necessidades de servidores poderosos (e caros). Esta
tecnologia recebeu o nome de MapReduce.
Alguns meses depois, o Google apresentou o Google File System (GFS), um *sistema de arquivos
especialmente preparado para lidar com processamento distribuído e, como não poderia deixar de ser no
caso de uma empresa como esta, grandes volumes de dados (em grandezas de terabytes ou mesmo
petabytes).
*Em poucas palavras, o sistema de arquivos é um conjunto de instruções que determina como os
dados devem ser guardados, acessados, copiados, alterados, nomeados, eliminados e assim por diante.
Em 2004, uma implementação open source do GFS foi incorporada ao Nutch, um projeto de motor de
busca para a Web. O Nutch enfrentava problemas de escala - não conseguia lidar com um volume grande
de páginas - e a variação do GFS, que recebeu o nome Nutch Distributed Filesystem(NDFS), se mostrou
como uma solução. No ano seguinte, o Nutch já contava também com uma implementação do
MapReduce.
Na verdade, o Nutch fazia parte de um projeto maior: uma biblioteca para indexação de páginas
chamada Lucene. Os responsáveis por estes trabalhos logo viram que o que tinham em mãos também
poderia ser usado em aplicações diferentes das buscas na Web. Esta percepção motivou a criação de
outro projeto que engloba características do Nutch e do Lucene: o Hadoop, cuja implementação do
sistema de arquivos recebeu o nome de Hadoop Distributed File System (HDFS).
O Hadoop é tido como uma solução adequada para Big Data por vários motivos:
- É um projeto open source, como já informado, fato que permite a sua modificação para fins de
customização e o torna suscetível a melhorias constantes graças à sua rede de colaboração. Por causa
desta característica, vários projetos derivados ou complementares foram - e ainda são - criados;
- Proporciona economia, já que não exige o pagamento de licenças e suporta hardware convencional,
permitindo a criação de projetos com máquinas consideravelmente mais baratas;
- O Hadoop conta, por padrão, com recursos de tolerância a falhas, como replicação de dados;
- O Hadoop é escalável: havendo necessidade de processamento para suportar maior quantidade de
dados, é possível acrescentar computadores sem necessidade de realizar reconfigurações complexas no
sistema.

É claro que o Hadoop pode ser usado em conjunto com bancos de dados NoSQL. A própria Apache
Foundation mantém uma solução do tipo que é uma espécie de subprojeto do Hadoop: o já mencionado
banco de dados HBase, que funciona atrelado ao HDFS.

101
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A denominação Hadoop tem uma origem inusitada: este é o nome que o filho de Doug Cutting, principal nome por trás do projeto, deu ao seu elefante de pelúcia
amarelo.

O Hadoop, é bom frisar, é a opção de maior destaque, mas não é a única. É possível encontrar outras
soluções compatíveis com NoSQL ou que são baseadas em Massively Parallel Processing (MPP), por
exemplo.

4. Desenvolvimento Mobile: linguagens/frameworks: Java/Kotlin e Swift.

Considerando as linguagens de programação, frameworks e SDKs para aplicativos móveis, levamos


em conta o ambiente de desenvolvimento de front-end (UI), mas também devemos estar atentos ao
ambiente de desenvolvimento de back-end (lado do servidor)26. Os desenvolvedores que estão
codificando o front-end normalmente não são aqueles que estão codificando o back-end, mas em geral
trabalham uns com os outros (atuam, afinal, em empresas).
O framework costuma ser um conjunto de bibliotecas. A principal diferença entre eles é a inversão do
controle: são os frameworks que chamam uma ação. O framework já tem o template ou o frame do que
é preciso. Isso significa que ele pode chamar os recursos necessários com base nas informações que
ainda têm de ser preenchidas no código.
Abordaremos as linguagens de programação e frameworks de desenvolvimento Java, Kotlin e Swift de
aplicativos e back-ends móveis.

Java
Java é a linguagem padrão para escrever aplicativos do Android desde que a plataforma Android foi
introduzida, em 2008. É uma linguagem de programação orientada a objetos originalmente desenvolvida
pela Sun Microsystems em 1995 (agora é propriedade da Oracle). Era uma linguagem muito popular
como linguagem pura orientada a objetos (em comparação com C ++) e foi rapidamente adotada pela
plataforma Android. A plataforma compila para "bytecode" interpretado de acordo com tempo de execução
pela Java Virtual Machine (JVM) em execução no sistema operacional. Você escreve os aplicativos
móveis em Java e programa no Android SDK.
Até o momento, essa é a linguagem mais usada para o desenvolvimento de aplicativos do Android.
Seria impossível introduzir toda a sintaxe da linguagem Java em um único tutorial. Abordaremos o
básico da linguagem, de forma que se transmita conhecimento e prática suficientes para escrever
programas simples.

Frameworks Java mais utilizados


Há vários frameworks Java disponíveis27. O que determina qual deles usar são as habilidades do
desenvolvedor ou os requisitos do site ou aplicação. Estes são alguns frameworks open source muito
usados:
- Quarkus: framework Java de stack completo, compatível com infraestruturas nativas em nuvem e
baseadas em microsserviços
- Grails: escrito na linguagem de programação Groovy, oferece frameworks de aplicação web com
recursos de exibição para plugins CSS e HTML

26
https://www.tecmundo.com.br/software/204629-desenvolvimento-app-escolhendo-linguagem-programacao.htm
27
https://www.redhat.com/pt-br/topics/cloud-native-apps/what-is-a-Java-framework

102
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Hibernate: framework de mapeamento objeto-relacional (ORM) usado no gerenciamento de bancos
de dados relacionais. Pode executar consultas SQL com a ajuda das APIs da Java Database Connectivity
(JDBC)
- Google Web Toolkit (GWT): conecta as equipes de desenvolvimento de front-end às de back-end

Outros frameworks Java muito usados:


- Vaadin: voltado às interfaces de usuário.
- Play: compatível com a linguagem de programação Scala, simplifica o desenvolvimento de
aplicações web.
- Apache Struts: voltado a aplicações empresariais e compatíveis com técnicas assíncronas de
JavaScript e XML (AJAX). É uma ótima maneira de fazer alterações em web pages sem precisar atualizar
tudo.
- JavaServer Faces (JSF) do Oracle: oferece frameworks de front-end aos desenvolvedores de back-
end.

Palavras reservadas
Como qualquer linguagem de programação, a linguagem Java designa certas palavras que o
compilador reconhece como especiais. Por esse motivo, você não tem permissão para usá-los para
nomear suas construções Java28. A lista de palavras reservadas (também chamadas de palavras-chave)
é curta:
abstract
assert
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
enum
extends
final
finally
float
for
goto
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
short

28
https://developer.ibm.com/tutorials/j-introtojava2/

103
1678859 E-book gerado especialmente para DANIEL CRISTIAN
static
strictfp
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while

Não se pode usar true, false e null para citar construções Java. Tecnicamente, são literais, que são as
representações do código-fonte de um valor, em vez de palavras-chave.
Uma vantagem de programar com um IDE é que ele pode usar coloração de sintaxe para palavras
reservadas.

Estrutura de uma classe Java


Uma classe é um projeto para uma entidade discreta (objeto) que contém atributos e comportamento.
A classe define a estrutura básica do objeto; no tempo de execução, seu aplicativo cria uma instância do
objeto. Um objeto tem um limite e um estado bem definidos e pode fazer coisas quando solicitado
corretamente. Toda linguagem orientada a objetos tem regras sobre como definir uma classe.
Na linguagem Java, as classes são definidas conforme mostrado na Listagem 1:

Listagem 1. Definição de classe


package packageName;
import ClassNameToImport;
accessSpecifier class ClassName {
accessSpecifier dataType variableName [= initialValue];
accessSpecifier ClassName([argumentList]) {
constructorStatement(s)
}
accessSpecifier returnType methodName ([argumentList]) {
methodStatement(s)
}
// This is a comment
/∗ This is a comment too ∗/
/∗ This is a
multiline
comment ∗/
}

A Listagem 1 contém vários tipos de construções, incluindo package na linha 1, import na linha 2 e
class na linha 3. Essas três construções estão na lista de palavras reservadas, portanto, devem ser
exatamente o que são na Listagem 1. Os nomes que eu ' As outras construções da Listagem 1 descrevem
os conceitos que elas representam.

Nota: na Listagem 1 e alguns outros exemplos de código nesta seção, colchetes indicam que as
construções dentro deles não são necessárias. Os colchetes (ao contrário de {e }) não fazem parte da
sintaxe Java.

Observe que as linhas 11 a 15 na Listagem 1 são linhas de comentário. Na maioria das linguagens de
programação, os programadores podem adicionar comentários para ajudar a documentar o código. A
sintaxe Java permite comentários de uma linha e de várias linhas:

// This is a comment
/∗ This is a comment too ∗/

104
1678859 E-book gerado especialmente para DANIEL CRISTIAN
/∗ This is a
multiline
comment ∗/

Um comentário de linha única deve estar contido em uma linha, embora você possa usar comentários
de linha única adjacentes para formar um bloco. Um comentário de várias linhas começa com /∗, deve
terminar com ∗/e pode abranger qualquer número de linhas.

Aulas de embalagem
Com a linguagem Java, você pode escolher os nomes para suas classes, tais como Account, Person,
ou LizardMan. Às vezes, você pode acabar usando o mesmo nome para expressar dois conceitos
ligeiramente diferentes. Essa situação, chamada de colisão de nome, ocorre com frequência. A linguagem
Java usa pacotes para resolver esses conflitos.
Um pacote Java é um mecanismo para fornecer um namespace - uma área dentro da qual os nomes
são exclusivos, mas fora da qual eles podem não ser. Para identificar uma construção de forma exclusiva,
você deve qualificá-la totalmente, incluindo seu namespace.
Os pacotes também oferecem uma boa maneira de criar aplicativos mais complexos com unidades
discretas de funcionalidade.
Para definir um pacote, use a packagepalavra - chave seguida por um nome de pacote válido,
terminando com um ponto-e-vírgula. Frequentemente, os nomes dos pacotes seguem este esquema
padrão de fato:

package orgType.orgName.appName.compName;

Esta definição de pacote se divide em:


- orgTypeorgType: é o tipo de organização, como com, org, ou net.
- orgNameorgName: é o nome do domínio da organização, tais como jstevenperry, oracle, ou ibm.
- appNameappName é o nome do aplicativo, abreviado.
- compNamecompName é o nome do componente.

Você usará essa convenção em todo este material. A linguagem Java não o força a seguir esta
convenção de pacote. Você não precisa especificar um pacote, caso em que todas as suas classes devem
ter nomes exclusivos e estão no pacote padrão.

Declarações de importação
A seguir na definição de classe (referindo-se à Listagem 1) está a instrução import. Uma instrução de
importação informa ao compilador Java onde encontrar as classes às quais você faz referência dentro de
seu código. Qualquer classe não trivial usa outras classes para alguma funcionalidade, e a instrução
import é como você informa o compilador Java sobre elas.
Uma instrução de importação geralmente se parece com isto:
import ClassNameToImport;

Você especifica a importpalavra - chave, seguida pela classe que deseja importar, seguida por um
ponto e vírgula. O nome da classe deve ser totalmente qualificado, o que significa que deve incluir seu
pacote.
Para importar todas as classes de um pacote, você pode colocar .* após o nome do pacote. Por
exemplo, está instrução importa todas as classes do com.jstevenperrypacote:
import com.jstevenperry.∗;

Importar um pacote inteiro pode tornar seu código menos legível, portanto, recomendamos que você
importe apenas as classes de que precisa, usando seus nomes totalmente qualificados.

Declaração de classe
Para definir um objeto na linguagem Java, você deve declarar uma classe. Pense em uma classe como
um modelo para um objeto, como um cortador de biscoitos.
A Listagem 1 inclui esta declaração de classe:

accessSpecifier class ClassName {


accessSpecifier dataType variableName [= initialValue];

105
1678859 E-book gerado especialmente para DANIEL CRISTIAN
accessSpecifier ClassName([argumentList]) {
constructorStatement(s)
}
accessSpecifier returnType methodName([argumentList]) {
methodStatement(s)
}
}

O accessSpecifier de uma classe accessSpecifier pode ter vários valores, mas geralmente é public.
Você pode nomear as classes da maneira que quiser, mas a convenção é usar maiúsculas e
minúscula: comece com uma letra maiúscula, coloque a primeira letra de cada palavra concatenada em
maiúscula e coloque todas as outras letras em minúsculas. Os nomes das classes devem conter apenas
letras e números. Seguir essas diretrizes garante que seu código seja mais acessível para outros
desenvolvedores que seguem as mesmas convenções.

Variáveis e métodos
As classes podem ter dois tipos de membros - variáveis e métodos.

Variáveis
Os valores das variáveis de uma classe distinguem cada instância dessa classe e definem seu estado.
Esses valores são frequentemente chamados de variáveis de instância. Uma variável tem:
- Um accessSpecifieraccessSpecifier
- Um dataTypedataType
- A variableNamevariableName
- Opcionalmente, um initialValueinitialValue

Os possíveis valores de accessSpecifieraccessSpecifier são:


public: qualquer objeto em qualquer pacote pode ver a variável. (Nunca use esse valor; consulte a
barra lateral de variáveis públicas).
protected: qualquer objeto definido no mesmo pacote, ou uma subclasse (definida em qualquer
pacote), pode ver a variável.
Nenhum especificador (também chamado de acesso amigável ou privado de pacote): somente
objetos cujas classes são definidas no mesmo pacote podem ver a variável.
private: apenas a classe que contém a variável pode vê-la.

Observação: nunca é uma boa ideia usar variáveis públicas, mas em casos extremamente raros, pode
ser necessário, então existe a opção. A plataforma Java não restringe seus casos de uso, portanto,
depende de você ser disciplinado quanto ao uso de boas convenções de codificação, mesmo se tentado
a fazer o contrário.

O dataType de uma variável dataType depende do que a variável é - pode ser um tipo primitivo ou
outro tipo de classe.
O variableNamevariableName é com você, mas por convenção, os nomes das variáveis usam a
convenção camel case, exceto que eles começam com uma letra minúscula. (Às vezes, esse estilo é
chamado de caixa de camelo inferior).
Não se preocupe com o valor inicialinitialValue por enquanto; apenas saiba que você pode inicializar
uma variável de instância ao declará-la. (Caso contrário, o compilador gera um padrão para você que é
definido quando a classe é instanciada).

Exemplo: definição de classe para pessoa


Aqui está um exemplo que resume o que você aprendeu até agora. A Listagem 2 é uma definição de
classe para Person.

Listagem 2. Definição de classe básica para Person


package com.jstevenperry.intro;

public class Person {


private String name;
private int age;

106
1678859 E-book gerado especialmente para DANIEL CRISTIAN
private int height;
private int weight;
private String eyeColor;
private String gender;
}

Esta definição de classe básica para Person não é útil neste ponto, porque ela define apenas Personos
atributos de (e os particulares). Para ser mais completa, a Personclasse precisa de comportamento - e
isso significa métodos.

Métodos
Os métodos de uma classe definem seu comportamento.
Os métodos se enquadram em duas categorias principais: construtores; e todos os outros métodos,
que vêm em muitos tipos. Um método construtor é usado apenas para criar uma instância de uma classe.
Outros tipos de métodos podem ser usados para praticamente qualquer comportamento de aplicativo.
A definição de classe na Listagem 1 mostra a maneira de definir a estrutura de um método, que inclui
elementos como:
accessSpecifieraccessSpecifier
returnTypereturnType
methodNamemethodName
argumentListargumentList

A combinação desses elementos estruturais na definição de um método é chamada de assinatura do


método.
Agora dê uma olhada mais de perto nas duas categorias de métodos, começando com construtores.

- Métodos de construtor
Você usa construtores para especificar como instanciar uma classe. A Listagem 1 mostra a sintaxe de
declaração do construtor de forma abstrata, e aqui está novamente:
accessSpecifier ClassName([argumentList]) {
constructorStatement(s)
}

O accessSpecifier de um construtor accessSpecifier é o mesmo que para as variáveis. O nome do


construtor deve corresponder ao nome da classe. Portanto, se você chamar sua classe Person, o nome
do construtor também deve ser Person.
Para qualquer construtor diferente do construtor padrão (veja os Construtores são nota opcional), você
passa um argumentListargumentList, que é um ou mais dos seguintes:
argumentType argumentName

Construtores são opcionais


Se você não usar um construtor, o compilador fornecerá um para você, chamado de construtor padrão
(ou sem argumento ou sem arg). Se você usar um construtor diferente de um construtor sem arg, o
compilador não gerará um automaticamente para você.
Os argumentos em uma argumentListargumentList são separados por vírgulas e dois argumentos não
podem ter o mesmo nome. argumentTypeargumentType é um tipo primitivo ou outro tipo de classe (o
mesmo que os tipos de variáveis).

Definição de classe com um construtor


Agora, veja o que acontece quando você adiciona a capacidade de criar um Personobjeto de duas
maneiras: usando um construtor sem arg e inicializando uma lista parcial de atributos.
A Listagem 3 mostra como criar construtores e como usar argumentList:

Listagem 3. Definição de classe de pessoa com um construtor


public class Person {

private String name;


private int age;
private int height;

107
1678859 E-book gerado especialmente para DANIEL CRISTIAN
private int weight;
private String eyeColor;
private String gender;

public Person(String name, int age, int height, int weight, String eyeColor, String gender) {
super();
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
this.eyeColor = eyeColor;
this.gender = gender;

logger.info("Created Person object with name '" + getName() + "'");


}
}

Observe o uso da thispalavra - chave ao fazer as atribuições de variáveis na Listagem 3. A thispalavra-


chave é uma abreviatura Java para “este objeto” e você deve usá-la ao fazer referência a duas variáveis
com o mesmo nome. Nesse caso, age é um parâmetro do construtor e uma variável de classe, portanto,
a thispalavra - chave ajuda o compilador a saber qual é qual.
O Personobjeto está ficando mais interessante, mas precisa de mais comportamento. E para isso, você
precisa de mais métodos.

- Outros métodos
Um construtor é um tipo específico de método com uma função específica. Da mesma forma, muitos
outros tipos de métodos executam funções específicas em programas Java.

De volta à Listagem 1, você viu como declarar um método:


accessSpecifier returnType methodName ([argumentList]) {
methodStatement(s)
}

Outros métodos se parecem muito com construtores, com algumas exceções. Primeiro, você pode
nomear outros métodos como quiser (embora, é claro, certas regras se apliquem). Recomendamos as
seguintes convenções:
- Comece com uma letra minúscula.
- Evite números, a menos que sejam absolutamente necessários.
- Use apenas caracteres alfabéticos.

Em segundo lugar, ao contrário dos construtores, outros métodos têm um tipo de retorno opcional.

- Outros métodos da pessoa


Armado com essas informações básicas, você pode ver na Listagem 4 o que acontece quando você
adiciona mais alguns métodos ao Personobjeto. (Omiti construtores por questões de brevidade).

Listagem 4. Pessoa com alguns métodos novos


package com.jstevenperry.intro;

public class Person {


private String name;
private int age;
private int height;
private int weight;
private String eyeColor;
private String gender;

public String getName() { return name; }


public void setName(String value) { name = value; }

108
1678859 E-book gerado especialmente para DANIEL CRISTIAN
// Other getter/setter combinations...
}

Observe o comentário na Listagem 4 sobre “combinações getter / setter”. Um getter é um método para
recuperar o valor de um atributo e um setter é um método para modificar esse valor. A Listagem 4 mostra
apenas uma combinação getter / setter (para o Nameatributo), mas você pode definir mais de maneira
semelhante.
Observe na Listagem 4 que, se um método não retornar um valor, você deve informar ao compilador
especificando o voidtipo de retorno em sua assinatura.

- Métodos estáticos e de instância


Geralmente, dois tipos de métodos (não construtores) são usados: métodos de instância e métodos
estáticos. Os métodos de instância dependem do estado de uma instância de objeto específica para seu
comportamento. Os métodos estáticos às vezes também são chamados de métodos de classe, porque
seu comportamento não depende do estado de nenhum objeto individual. O comportamento de um
método estático ocorre no nível da classe.
Os métodos estáticos são amplamente usados para fins de utilidade; você pode pensar neles como
métodos globais (á la C) enquanto mantém o código do método com a classe que o define.
Por exemplo, você usará a Loggerclasse JDK para enviar informações para o console. Para criar uma
Loggerinstância de classe, você não instancia uma Loggerclasse; em vez disso, você invoca um método
estático denominado getLogger().
A sintaxe para invocar um método estático em uma classe é diferente da sintaxe usada para invocar
um método em um objeto. Você também usa o nome da classe que contém o método estático, conforme
mostrado nesta invocação:
Logger l = Logger.getLogger("NewLogger");

Neste exemplo, Loggeré o nome da classe e getLogger(...)é o nome do método. Portanto, para invocar
um método estático, você não precisa de uma instância de objeto, apenas o nome da classe.

Kotlin
Kotlin é uma linguagem de programação de código aberto estática, compatível com programação
orientada a objetos e funcional. O Kotlin fornece sintaxe e conceitos semelhantes de outras linguagens,
incluindo C#, Java e Scala, entre muitos outros. O Kotlin não pretende ser única. Em vez disso, ela se
inspira em décadas de desenvolvimento da linguagem. Ela existe em variantes que segmentam JVM
(Kotlin/JVM), JavaScript (Kotlin/JS) e código nativo (Kotlin/Native).
Kotlin é gerenciado pelo Kotlin Foundation (link em inglês), um grupo criado por JetBrains (em inglês)
e pelo Google que tem a tarefa de promover e desenvolver continuamente a linguagem. O Kotlin é
oficialmente compatível com o desenvolvimento do Google para Android, o que significa que a
documentação e as ferramentas do Android foram desenvolvidas com o Kotlin em mente.
Certas APIs do Android, como a Android KTX, são específicas do Kotlin, mas a maioria é gravada em
Java e pode ser chamada de Java ou Kotlin. A interoperabilidade do Kotlin com Java é fundamental para
seu crescimento. Isso significa que você pode chamar o código Java do Kotlin e vice-versa, aproveitando
todas as suas bibliotecas Java existentes. A popularidade do Kotlin resulta em uma experiência de
desenvolvimento mais agradável no Android, mas o desenvolvimento do framework do Android continua
com o Kotlin e Java em mente.
A interoperabilidade do Kotlin com Java significa que você não precisa adotar o Kotlin de uma só vez.
Você pode ter projetos com código Kotlin e Java. Para saber mais sobre como adicionar o Kotlin a um
app já existente, consulte Adicionar o Kotlin a um app já existente. Se você fizer parte de uma equipe
maior, o tamanho da sua organização e do codebase poderá exigir um foco especial.

Declaração de variável
O Kotlin usa duas palavras-chave diferentes para declarar variáveis: val e var29.
- Use val para uma variável cujo valor nunca muda. Não é possível reatribuir um valor a uma variável
que tenha sido declarada usando val.
- Use var para uma variável cujo valor possa ser mudado.

No exemplo abaixo, count é uma variável do tipo Int que recebe um valor inicial de 10:

29
https://developer.android.com/kotlin/learn?hl=pt

109
1678859 E-book gerado especialmente para DANIEL CRISTIAN
var count: Int = 10

Int é um tipo que representa um número inteiro, um dos muitos tipos numéricos que podem ser
representados em Kotlin. Assim como acontece com outras linguagens, você também pode usar Byte,
Short, Long, Float e Double, dependendo dos seus dados numéricos.
A palavra-chave var significa que você pode reatribuir valores a count conforme necessário. Por
exemplo, você pode mudar o valor de count de 10 para 15:
var count: Int = 10
count = 15

No entanto, alguns valores não podem ser mudados. Considere um String chamado languageName.
Se você quiser garantir que languageName sempre tenha o valor "Kotlin", poderá declarar languageName
usando a palavra-chave val:
val languageName: String = "Kotlin"

Essas palavras-chave permitem que você seja explícito sobre o que pode ser mudado. Use-as em seu
favor conforme necessário. Se uma referência de variável precisar ser reatribuível, declare-a como var.
Do contrário, use val.

Inferência de tipo
Continuando com o exemplo anterior, quando você atribui um valor inicial a languageName, o
compilador Kotlin pode inferir o tipo com base no tipo do valor atribuído.
Como o valor de "Kotlin" é do tipo String, o compilador infere que languageName também é um String.
O Kotlin é uma linguagem estática. Isso significa que o tipo é resolvido no momento da compilação e
nunca muda.
No exemplo a seguir, languageName é inferido como String. Portanto, não é possível chamar nenhuma
função que não faça parte da classe String:
val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

toUpperCase() é uma função que só pode ser chamada em variáveis do tipo String. Como o compilador
Kotlin inferiu languageName como String, você pode chamar toUpperCase() com segurança. inc(),
entretanto, é uma função de operador Int, por isso não pode ser chamada em String. A abordagem do
Kotlin para a inferência de tipos oferece concisão e segurança de tipos.

Segurança nula
Em algumas linguagens, uma variável de tipo de referência pode ser declarada sem fornecer um valor
explícito inicial. Nesses casos, as variáveis geralmente contêm um valor nulo. Por padrão, as variáveis
do Kotlin não podem reter valores nulos. Isso significa que o snippet a seguir é inválido.
// Fails to compile
val languageName: String = null

Para que uma variável mantenha um valor nulo, ela precisa ser do tipo anulável. Você pode
especificar uma variável como sendo anulável, usando um sufixo do tipo com ?, conforme mostrado
neste exemplo a seguir.
val languageName: String? = null

Com um tipo String?, você pode atribuir um valor String ou null a languageName.
Você precisa lidar com variáveis anuláveis com cuidado ou corre o risco de ter um
NullPointerException. Em Java, por exemplo, se você tentar invocar um método em um valor nulo, seu
programa falhará.
O Kotlin fornece uma série de mecanismos para trabalhar com segurança com variáveis anuláveis.
Para ver mais informações, consulte Padrões comuns do Kotlin no Android: anulação (link em inglês).

110
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Condicionais
O Kotlin apresenta vários mecanismos para implementar a lógica condicional. O mais comum deles é
uma instrução if-else. Se uma expressão entre parênteses ao lado de uma palavra-chave if for avaliada
como true, o código dentro dessa ramificação (ou seja, o código imediatamente seguinte que é
encapsulado entre chaves) será executado. Caso contrário, será executado o código dentro da
ramificação else.
if (count == 42) {
println("I have the answer.")
} else {
println("The answer eludes me.")
}

Você pode representar várias condições usando else if. Isso permite representar uma lógica mais
granular e complexa em uma única instrução condicional, conforme mostrado neste exemplo:
if (count == 42) {
println("I have the answer.")
} else if (count > 35) {
println("The answer is close.")
} else {
println("The answer eludes me.")
}

As instruções condicionais são úteis para representar a lógica com estado, mas você pode se repetir
ao gravá-las. No exemplo acima, você simplesmente imprime um String em cada ramificação. Para evitar
essa repetição, o Kotlin oferece expressões condicionais. O último exemplo pode ser regravado da
seguinte forma:
val answerString: String = if (count == 42) {
"I have the answer."
} else if (count > 35) {
"The answer is close."
} else {
"The answer eludes me."
}

println(answerString)

Implicitamente, cada ramificação condicional retorna o resultado da expressão na linha final, de modo
que não é necessário usar uma palavra-chave return. Como o resultado das três ramificações é do tipo
String, o resultado da expressão if-else também é do tipo String. Neste exemplo, answerString recebe um
valor inicial do resultado da expressão if-else. A inferência de tipos pode ser usada para omitir a
declaração de tipo explícito para answerString, mas geralmente é uma boa ideia incluí-la para fins de
clareza.
Conforme a complexidade da instrução condicional aumenta, é recomendável substituir a expressão
if-else por uma expressão when, conforme mostrado neste exemplo:
val answerString = when {
count == 42 -> "I have the answer."
count > 35 -> "The answer is close."
else -> "The answer eludes me."
}

println(answerString)

Cada ramificação em uma expressão when é representada por uma condição, uma seta (->) e um
resultado. Se a condição no lado esquerdo da seta for avaliada como verdadeira, o resultado da
expressão no lado direito será retornado. Observe que a execução não passa de uma ramificação para a
próxima. O código no exemplo de expressão when é funcionalmente equivalente ao do exemplo anterior,
mas é mais fácil de ler.
As condicionais do Kotlin destacam um dos recursos mais avançados, a transmissão inteligente. Em
vez de usar o operador de chamada segura ou o operador de declaração não nulo para trabalhar com

111
1678859 E-book gerado especialmente para DANIEL CRISTIAN
valores anuláveis, você pode verificar se uma variável contém uma referência a um valor nulo usando
uma instrução condicional, conforme mostrado neste exemplo:
val languageName: String? = null
if (languageName != null) {
// No need to write languageName?.toUpperCase()
println(languageName.toUpperCase())
}

Na ramificação condicional, languageName pode ser tratado como não anulável. O Kotlin é inteligente
o suficiente para reconhecer que a condição para executar a ramificação é que languageName não
contenha um valor nulo. Portanto, você não precisa tratar languageName como anulável nessa
ramificação. Essa transmissão inteligente funciona para verificações nulas, verificações de tipo ou
qualquer condição que satisfaça a um contrato.

Funções
Você pode agrupar uma ou mais expressões em uma função. Em vez de repetir a mesma série de
expressões sempre que precisar de um resultado, você pode unir as expressões em uma função e chamar
essa função.
Para declarar uma função, use a palavra-chave fun seguida pelo nome da função. Em seguida, defina
os tipos de entrada que sua função assume, se houver, e declare o tipo de saída retornada. No corpo de
uma função, você define expressões que são chamadas quando sua função é invocada.
Com base nos exemplos anteriores, veja uma função completa do Kotlin:
fun generateAnswerString(): String {
val answerString = if (count == 42) {
"I have the answer."
} else {
"The answer eludes me"
}

return answerString
}

A função no exemplo acima tem o nome generateAnswerString. Não é necessária nenhuma entrada.
Ela gera um resultado do tipo String. Para chamar uma função, use o nome dela, seguido pelo operador
de invocação (()). No exemplo abaixo, a variável answerString é inicializada com o resultado de
generateAnswerString().
val answerString = generateAnswerString()

As funções podem receber argumentos como entrada, conforme mostrado neste exemplo:
fun generateAnswerString(countThreshold: Int): String {
val answerString = if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}

return answerString
}

Ao declarar uma função, você pode especificar qualquer número de argumentos e os tipos. No exemplo
acima, generateAnswerString() leva um argumento chamado countThreshold do tipo Int. Dentro da
função, você pode se referir ao argumento usando o nome dele.
Ao chamar essa função, você precisa incluir um argumento nos parênteses da chamada da função:
val answerString = generateAnswerString(42)

Classes
Todos os tipos mencionados até agora estão integrados à linguagem de programação Kotlin. Se quiser
adicionar um tipo personalizado, você poderá definir uma classe usando a palavra-chave class, conforme
mostrado neste exemplo:

112
1678859 E-book gerado especialmente para DANIEL CRISTIAN
class Car

Propriedades
As classes representam o estado usando propriedades. Uma propriedade é uma variável de nível de
classe que pode incluir um getter, um setter e um campo de backup. Como um carro precisa de rodas
para dirigir, você pode adicionar uma lista de objetos Wheel como uma propriedade de Car, conforme
mostrado neste exemplo:
class Car {
val wheels = listOf<Wheel>()
}

Observe que wheels é um public val, o que significa que wheels pode ser acessado de fora da classe
Car e não pode ser reatribuído. Se você quiser ter uma instância de Car, primeiro é necessário chamar
seu construtor. A partir daí, você pode acessar qualquer uma das propriedades acessíveis.
val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

Se você quiser personalizar suas rodas, poderá definir um construtor personalizado que especifica
como as propriedades de classe são inicializadas:
class Car(val wheels: List<Wheel>)

No exemplo acima, o construtor de classe usa um List<Wheel> como argumento do construtor e usa
esse argumento para inicializar a propriedade wheels.

Funções de classe e encapsulamento


As classes usam funções para modelar o comportamento. As funções podem modificar o estado,
ajudando você a expor somente os dados que quer expor. Esse controle de acesso faz parte de um
conceito maior orientado a objetos, conhecido como encapsulamento.
No exemplo a seguir, a propriedade doorLock é mantida privada em qualquer item fora da classe Car.
Para desbloquear o carro, você precisa chamar a função unlockDoor() transmitindo uma chave válida,
conforme mostrado neste exemplo:
class Car(val wheels: List<Wheel>) {

private val doorLock: DoorLock = ...

fun unlockDoor(key: Key): Boolean {


// Return true if key is valid for door lock, false otherwise
}
}

Se quiser personalizar a forma como uma propriedade é referenciada, você poderá fornecer getter e
setter personalizados. Por exemplo, se quiser expor o getter de uma propriedade ao restringir o acesso a
setter, você poderá designar esse setter como private:
class Car(val wheels: List<Wheel>) {

private val doorLock: DoorLock = ...

var gallonsOfFuelInTank: Int = 15


private set

fun unlockDoor(key: Key): Boolean {


// Return true if key is valid for door lock, false otherwise
}
}

Com uma combinação de propriedades e funções, você pode criar classes que modelam todos os tipos
de objeto.

113
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Interoperabilidade
Uma das características mais importantes do Kotlin é a interoperabilidade fluida com Java. Como o
código Kotlin é compilado até o bytecode da JVM, seu código Kotlin pode ser chamado diretamente no
código Java e vice-versa. Isso significa que você pode aproveitar bibliotecas Java já existentes
diretamente do Kotlin. Além disso, a maioria das APIs do Android é gravada em Java, e você pode chamá-
las diretamente do Kotlin.

Swift
A linguagem da Apple chamada Swift está cada vez mais sendo utilizada. Muitos já estão criando ou
modificando seus projetos e aprendendo as vantagens que a linguagem traz30.
Comparada com Objective-C que está há mais de 20 anos no mercado, Swift traz facilidades e
característica que a antiga linguagem da Apple não tem como Generics e sintaxes mais simples onde
com poucas linhas de código o programador consegue fazer mais coisas do que faria com Objective-C,
lembrando linguagens de script como Ruby e Python, porém mantendo as chamadas dos métodos
principais parecidas com Objective-C facilitando assim a migração de novos e antigos desenvolvedores.
A linguagem é parecida com algumas linguagens como Ruby e Python mas não é só isso que a torna
fácil e sim por que diferentemente da Objective-C, além de ter tipagem forte ela pode ser dinâmica, ou
seja, você não precisa definir o tipo da variável, ele vai ser definido na primeira vez que a variável receber
um valor.
Essa linguagem desenvolvida também pode se tornar mais uma opção para quem quiser aprender a
usar o Cocoa framework. Sendo assim, ele consegue coexistir com Objective-C. Você pode criar classes
Swift em seu projeto Objective-C e fazer a comunicação entre eles sem ter que passar completamente
seu projeto para Swift. Esse framework contém funções que permitem os desenvolvedores desenhar
imagens e textos na tela, respondendo a interação do usuário. Dessa forma, quando você ouvir que
precisa ainda aprender Objective-C para fazer aplicações para IOS, a maioria quer dizer que você precisa
aprender como se usa o framework Cocoa Touch e com Swift você tem mais uma maneira de começar a
usar esse framework. Felizmente, para aprender essa linguagem você não precisa saber previamente
Objective-C, porque o Swift é tão flexível que pode se adaptar à sua maneira de programar. Mas se você
já programa em outra linguagem, a única dificuldade vai ser se adaptar aos novos padrões da sintaxe,
por exemplo:
if let blogId = json[“blogs”]?[“blog”]?[0]:[“id”]?.number {
println(“blog ID: \(blogId)”)
}

Playground
Com o lançamento da Swift, a Apple aprimorou sua ferramenta de desenvolvimento chamada de Xcode
adicionando uma parte Playground (ver Figura 1) onde o programador pode facilmente testar seu código
sem precisar fazer o build ou compilar. Ele, além de mostrar o resultado imediatamente, mostra também
os gráficos sobre a performance do código, neste caso há mais uma característica da linguagem que é a
de poder ser interpretada. Poderá ser desenvolvida toda uma aplicação usando o Playground e testar
protótipos com a finalidade de melhorar a implementação durante a fase de desenvolvimento do projeto.

Figura 1. Playground

30
https://www.devmedia.com.br/desenvolvimento-ios-conheca-a-linguagem-swift/31860

114
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Características da linguagem Swift
Essa linguagem vem inovando de várias maneiras, mas temos uma bem peculiar ao criar constantes
e variáveis. Por exemplo: ela permite criar variáveis com caracteres especiais incluindo o conteúdo delas,
o que torna possível nomear uma variável ou constante com emoticons e outros caracteres. Além disso,
traz vantagens tanto na forma de implementação como em padrões que Objective-C não apresenta.
Falando em padrões, com Swift é possível implementar Generics, o que torna viável criar códigos mais
flexíveis, como mostra o código da Listagem 1, sendo possível achar um valor dentro de qualquer array.

Listagem 1. Exemplo de uso de Generics em Swift


func findIndex<T>(array: [T], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}

Outra novidade é o caso do switch. Agora seus cases podem receber um range que permite mais
flexibilidade para o desenvolvedor implementar sua lógica, como demonstra o código da Listagem 2.

Listagem 2. Exemplo de switch


switch count {
case 0:
naturalCount = "no"
case 1...3:
naturalCount = "a few"
case 4...9:
naturalCount = "several"
case 10...99:
naturalCount = "tens of"
case 100...999:
naturalCount = "hundreds of"
case 1000...999_999:
naturalCount = "thousands of"
default:
naturalCount = "millions and millions of"
}

Para uma visualização mais prática e melhor entendimento das próximas características, o algoritmo
apresentado na Listagem 3 mistura Loops, Ternary e Array. Seguindo o mesmo princípio aplicado em
switches, o loop também aceita ranges. A Apple criou outros sinais para determinar como são tratados
esses ranges, como vimos anteriormente o sinal aplicado foi “…” entre os números, mas existe também
“..<” ou “..>”.

Listagem 3. Exemplo de sinais para tratamento de ranges


let words = ["The", "Swift", "Programming", "Language"]
let loops = 100
var singleWord:String?
var defaultWord:String?
var count = 0;
var list:[String] = []
for i in 0..<loops {
singleWord = i % 2 == 0 ? defaultWord : nil
defaultWord = singleWord ?? words[count]
count = i % words.count
if (singleWord != defaultWord) list += [defaultWord!]
else list[0…total-1] = [singleWord!]
}

115
1678859 E-book gerado especialmente para DANIEL CRISTIAN
print(list)

Controle de acesso
O Swift suporta seis níveis de controle de acesso com os símbolos: open, public, internal, fileprivate e
private. Ao contrário de outras linguagens orientadas a objetos, esses controles de acesso ignoram
hierarquias de herança: private indica que um símbolo só é acessível no escopo imediato, fileprivate indica
que só é acessível internamente ao arquivo, internal indica que é acessível no escopo do módulo que o
contém, public indica que é acessível de qualquer módulo, e open (apenas para classes e seus métodos)
indica que a classe pode ser "subclassificada" de fora do módulo31.

Tipagem opcional
Uma importante característica nova no Swift são tipos opcionais, que permitem que valores ou
referências operem de forma semelhante ao padrão comum do C, em que um ponteiro pode prover
referência a um valor ou ser nulo32.

Tipagem de valor
Em muitas linguagens orientadas a objetos, objetos são representados internamente em duas partes.
O objeto é armazenado como um bloco de dados posicionado no heap, enquanto o nome (ou "handle")
do objeto é representado por um ponteiro. Objetos são passados entre métodos por meio da cópia do
valor do ponteiro, permitindo assim, o acesso aos mesmos dados contidos no heap por qualquer um que
possua uma cópia. Por sua vez, tipos básicos como valores de inteiros e pontos flutuantes são
representados diretamente; o handle contém os dados, não um ponteiro para os mesmos, e esses dados
são passados diretamente aos métodos por cópia. Esses estilos de acesso são denominados passagem
por referência no caso dos objetos, e passagem por valor para tipos básicos.
De forma similar ao C#, o Swift oferece suporte nativo a objetos usando ambas as semânticas de
passagem por referência e passagem por valor, a primeira usando a declaração class e a outra usando
struct. Structs no Swift tem quase todas as mesmas características das classes: métodos, protocolos de
implementação e uso de mecanismos de extensão. Por esse motivo, a Apple nomeou genericamente
todos os dados como instâncias, ao invés de objetos ou valores. Contudo, structs não permitem herança33.
// Definição de struct
struct Resolution {
var width = 0
var height = 0
}

// Criação de instância
let hd = Resolution(width: 1920, height: 1080)

// Sendo "Resolution" um struct, uma cópia da instância é criada


var cinema = hd

Questões

01. (Prefeitura de Novo Hamburgo/RS - Técnico em Desenvolvimento de Sistemas - INSTITUTO


AOCP/2020) Considerando o ambiente de desenvolvimento Android Studio, assinale a alternativa que
apresenta apenas linguagens válidas ao adicionar uma nova activity ao projeto.
(A) Java e Swift.
(B) C e Java.
(C) JavaScript e Python.
(D) C# e PHP.
(E) Java e Kotlin.

02. (PRODEB - Analista de TIC II - INSTITUTO AOCP/2018) Considerando as linguagens de


programação mobile, qual das dispostas a seguir foi criada pela Apple e pode ser utilizada para o
desenvolvimento das suas aplicações?
(A) Kotlin

31
«Access Control — The Swift Programming Language (Swift 5)». docs.swift.org.
32
The Basics — The Swift Programming Language (Swift 5)». docs.swift.org.
33
«Structures and Classes — The Swift Programming Language (Swift 5)». docs.swift.org.

116
1678859 E-book gerado especialmente para DANIEL CRISTIAN
(B) Swift
(C) C#
(D) Python
(E) IOS

Gabarito
01. E/02. B

React Native 0.59;

REACT NATIVE34

Criado pelo Facebook em 2015 sobre a licença MIT, o React Native é um Framework para
desenvolvimento de aplicativos móveis multiplataforma.
Um Framework é um facilitador no desenvolvimento de diversas aplicações e, sem dúvida, sua
utilização poupa tempo e custos para quem utiliza, pois de forma mais básica, é um conjunto de
bibliotecas utilizadas para criar uma base, onde as aplicações são construídas, um otimizador de
recursos.
Baseado no React, framework JS para desenvolvimento web, o React Native possibilita a criação de
aplicações móvel multiplataforma (Android e iOS) utilizando apenas Javascript. Porém, diferente de outros
frameworks com esta mesma finalidade (Cordova, por exemplo), todo o código desenvolvido com o React
Native é convertido para linguagem nativa do sistema operacional, o que torna o app muito mais fluido.

Características
O React Native possui diversas características marcantes e fundamentais para sua ampla adoção,
como podemos ver abaixo:

- Acessar a interface e os recursos nativos do Android e iOS utilizando JavaScript;


- O código produzido é semelhante ao React para Web;
- Possui a base de conhecimento compartilhada entre o desenvolvimento mobile e front-end;
- Todo código desenvolvido é convertido para a linguagem nativa do sistema operacional;
- Com o React Native conseguimos desenvolver aplicações para Android e iOS utilizando um código
único;
- Por ser multiplataforma, podemos desenvolver aplicações com React Native utilizando qualquer
sistema operacional (Windows, macOS ou Linux).

Empresas que o utilizam


Por ser amplamente adotado na comunidade mobile, o React Native atraiu a atenção de diversas
empresas, que fizeram questão de utilizá-lo em suas soluções mobile. Dentre estas empresas, podemos
citar:
- AirBnb: O AirBnb é uma das principais empresas a utilizar o React Native para seu aplicativo móvel;
- Wix: O Wix, plataforma online de criação de sites, também utiliza o React Native em seu aplicativo
mobile;
- Facebook: Claro que o Facebook, empresa por trás do React Native, também iria utilizá-la para seu
aplicativo móvel;
- Tesla: A Tesla, principal montadora de carros elétricos, também utiliza esta tecnologia para seu
aplicativo móvel;
- Instagram: Além do Facebook, o Instagram (rede social do mesmo grupo), também utiliza o React
Native para o desenvolvimento do seu app.
Como vimos acima, a adoção do React Native por grandes empresas é bem nítida, o que o torna ainda
mais completo e utilizado por todo o mundo.

34
https://www.treinaweb.com.br/blog/o-que-e-o-react-native

117
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Sintaxe
Apesar de ser baseado no React, o React Native não utiliza tags HTML para a criação de seus
elementos. Ao invés disso, os elementos são criados utilizando tags nativas do React Native, como
podemos ver no trecho de código abaixo:

import React from 'react';


import { Text } from 'react-native';

export default function Ola() {


return (
<Text>Olá, mundo!</Text>
);
}
O Component serve para exibir um texto no aplicativo. Este componente é nativo do React Native,
além de tantos outros que podem ser vistos em sua documentação oficial.

Podemos concluir que…


É notável a crescente demanda do uso de aplicativos móveis nos últimos anos. Pensando nisso,
diversas empresas desenvolvem suas soluções para facilitar a criação destes aplicativos. Com o React
Native, o Facebook lançou uma solução incrível para o desenvolvimento multiplataforma utilizando
apenas código Javascript, o que facilita (e muito) os desenvolvedores que já possuem conhecimento em
desenvolvimento web e até os que não possuem, já que é um framework com alta curva de aprendizado.

Sistemas Android api 30 e iOS xCode 10.

NOÇÕES DE MANUSEIO DE APARELHOS COM SISTEMA ANDROID E IOS

Diferente dos celulares comuns, os smartphones acessam a internet, permitem o download de


aplicativos (que dão novas funções ao smartphone) e até se transformam em roteadores35. Veremos um
passo a passo que ensina a ajustar configurações básicas do sistema em tal sistema, como instalar
aplicativos, compartilhar a conexão de internet móvel ou configurar uma senha para desbloquear o
aparelho.

Configurar Rede Wi-Fi

Para iniciar a configuração do smartphone, é importante que o usuário esteja conectado à internet e
de preferência em uma rede Wi-Fi, pois geralmente é rápida e mais estável que conexões 4G. É
necessário que o usuário clique no ícone Aplicativos (presente em todos aparelhos Android) e clique em
Ajustes (na imagem, está ao lado esquerdo da tela) ou Configurações.
Aparecerá uma tela parecida com esta abaixo. Basta tocar sobre a chave de liga e desliga em Wi-Fi
que o sistema ligará a funcionalidade.

35
https://noticias.uol.com.br/tecnologia/album

118
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Após ligar as antenas Wi-Fi, escolha uma rede e conecte-se (às vezes, é necessário colocar uma
senha, que é definida pelo dono da rede).

Se tudo estiver correto, o ícone indicado mostrará que o smartphone está conectado à internet.

119
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Configurando uma Conta do Google

A Play Store é a loja de aplicativos do sistema Android. Nela há aplicativos gratuitos e pagos que vão
adicionando funções ao telefone. Na primeira vez que o usuário tentar entrar na loja virtual, o sistema
operacional pedirá que o dono do dispositivo forneça detalhes de sua conta do Google -- sem uma conta
válida do Google não é possível baixar programas pela loja.

Clique em Existente, caso já tenha um e-mail do Gmail (serviço do Google de correio eletrônico), ou
em Nova. O processo só vai funcionar se o aparelho estiver conectado à internet.

120
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Digite os dados solicitados e clique na seta para avançar.

Clique em Ok.

121
1678859 E-book gerado especialmente para DANIEL CRISTIAN
o sistema pergunta se o usuário quer manter uma cópia na internet de aplicativos e configurações que
ele instalar. Deixar esta opção marcada é importante para casos de roubo. Ao comprar um novo aparelho
Android e configurar a conta do Google, a pessoa terá todos seus programas e configurações de volta.
Clique em avançar.

Por configurar uma conta do Google, o sistema perguntará se ele pode sincronizar o Gmail com o
smartphone. Clique em Sincronizar agora. Ele trará os e-mails do usuário e permitirá o download de
programas na loja de aplicativos.

122
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Baixando Aplicativos

Por fim, aparecerá a tela da loja de aplicativos Google Play. Nela, é possível baixar livros, filmes, jogos
e programas que incrementam as funções do smartphone. Clique em Apps (sigla em inglês para
aplicativos).

Não há segredo para baixar programas gratuitos para o smartphone. Basta selecioná-lo e tocar sobre
o botão Instalar.
Antes do início do download, o sistema informa ao usuário sobre o tipo de acesso que o aplicativo terá
aos dados dele. Ao tocar em aceitar, a transferência começa automaticamente. No fim, o aparelho exibirá
uma notificação e criará um ícone do programa novo.

123
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Comprando Aplicativos

No caso de o usuário querer comprar um aplicativo, ele só pode fazer de uma forma: via cartão de
crédito. Para iniciar o processo de aquisição, o usuário deve dar um toque sobre o valor do programa e,
em seguida, aceitar as permissões do aplicativo.

O que é iOS?36

O iOS (anteriormente chamado iPhone OS) é um sistema operacional exclusivo para aparelhos da
Apple. Por ser um software para dispositivos móveis, ele é baseado no conceito de manipulação direta.
Isso significa que a interação é feita a partir de toques na tela, como deslizar um ou mais dedos para
realizar diferentes ações. As versões operacionais do programa são lançadas anualmente e o iOS 13 já
está em fase de testes, com lançamento oficial previsto para setembro deste ano.

O que significa a palavra iOS?

A palavra iOS tem origem na sigla "OS" (Sistema Operacional, em inglês), somada à letra "i", que
representa os produtos da Apple, como iPhone, iPad, iPod e iMac. É por meio desse programa que
funcionam os dispositivos móveis da empresa, que não permite oficialmente sua utilização no hardware
de terceiros. O iOS compete diretamente com o sistema operacional desenvolvido pelo Google, o Android.

Como o iOS foi criado e qual a sua história?

O primeiro sistema móvel da Apple tinha o nome de iPhone OS 1 e era exclusivo smartphone da
empresa. Lançada por Steve Jobs, essa primeira versão foi essencial para o desenvolvimento do
multitouch e para as novas ideias que surgiram com o tempo. Em 2010, com o lançamento da sua quarta
atualização, o sistema passou a se chamar iOS, justamente pelo fato de não ser mais exclusivo do iPhone.
O iOS 4 foi fundamental para a execução de multitarefas sem perder rendimento. Esse foi um fator
decisivo para manter o sistema em competição com o Android, que estava ganhando cada vez mais força
e popularidade. Hoje, o programa possui inúmeras funcionalidades e se tornou um dos sistemas mais
utilizados e valorizados no mundo todo, principalmente pelo fato de trazer atualizações anuais com um
grande número de novos recursos.
36
https://glo.bo/2YMW2QS

124
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Configuração37

1. Ligar o dispositivo

Mantenha pressionado o botão liga/desliga do dispositivo até o logotipo da Apple ser exibido. Em
seguida, você verá "Olá" escrito em vários idiomas. Siga as etapas para começar. Se você tem
deficiências visuais, é possível ativar o VoiceOver ou Zoom na tela Olá.

Quando solicitado, selecione o idioma. Em seguida, toque em seu país ou região. Isso afetará a
exibição das informações no dispositivo, o que inclui a data, hora, contatos e muito mais. Nesse ponto,
você pode tocar no botão de acessibilidade azul para configurar as opções de Acessibilidade, que podem
otimizar a experiência de configuração e o uso do novo dispositivo.

2. Se você tiver outro dispositivo com o iOS 11 ou posterior, usar o Início Rápido

Se você tiver outro dispositivo com o iOS 11 ou posterior, poderá usá-lo para configurar
automaticamente o novo dispositivo com o Início Rápido. Aproxime os dois dispositivos e siga as
instruções.
Se você não tiver outro dispositivo com o iOS 11 ou posterior, toque em "Configurar Manualmente"
para continuar.

37
https://apple.co/3glpgfx

125
1678859 E-book gerado especialmente para DANIEL CRISTIAN
3. Ativar o dispositivo

É necessário ter conexão com uma rede Wi-Fi, uma rede celular ou o iTunes para ativar o dispositivo
e continuar a configuração.
Toque na rede Wi-Fi desejada ou selecione uma opção diferente. Se estiver configurando um iPhone
ou um iPad (Wi-Fi + Celular) insira o cartão SIM primeiro

4. Configurar o Face ID ou Touch ID e criar um código de acesso

Em alguns dispositivos, você pode configurar o Face ID ou o Touch ID. Com esses recursos, você
pode usar reconhecimento facial ou impressão digital para desbloquear o dispositivo e fazer compras.
Toque em Continuar e siga as instruções ou toque em "Configurar Depois nos Ajustes".
Em seguida, defina um código de acesso de seis dígitos para ajudar a proteger os dados. É necessário
ter um código para usar recursos como Face ID, Touch ID e Apple Pay. Se você desejar usar um código
de acesso de quatro dígitos, um código personalizado ou não usar um código, toque em "Opções de
Código".

126
1678859 E-book gerado especialmente para DANIEL CRISTIAN
5. Restaurar ou transferir informações e dados

Se você tiver um backup do iCloud ou do iTunes ou um dispositivo Android, poderá restaurar ou


transferir os dados do dispositivo antigo para o novo.
Se você não tiver um backup ou outro dispositivo, selecione "Não Transferir Apps e Dados".

6. Iniciar sessão com seu ID Apple

Insira seu ID Apple e senha ou toque em "Esqueceu a senha ou não tem um ID Apple?". Nessa tela,
é possível recuperar o ID Apple ou a senha, criar um ID Apple ou configurá-lo mais tarde. Se você usa
mais de um ID Apple, toque em "Usar IDs Apple diferentes para o iCloud e o iTunes?".
Ao iniciar sessão com o ID Apple, talvez você precise inserir o código de verificação do dispositivo
anterior.

127
1678859 E-book gerado especialmente para DANIEL CRISTIAN
7. Ativar atualizações automáticas e configurar outros recursos

Nas próximas telas, você poderá decidir se deseja compartilhar informações com desenvolvedores de
apps e permitir atualizações automáticas do iOS.

8. Configurar a Siri e outros serviços

Depois, você poderá configurar ou ativar serviços e recursos, como a Siri. Em alguns dispositivos, você
precisará dizer algumas frases para que a Siri conheça sua voz.
Se você iniciou sessão com o ID Apple, siga as etapas para configurar o Apple Pay e as Chaves do
iCloud.

9. Configurar o recurso "Tempo de Uso" e mais opções da tela

O recurso Tempo de Uso informa quanto tempo você e seus filhos passam nos dispositivos. Ele
também permite definir limites de tempo para o uso diário de apps. Após configurar o recurso "Tempo de

128
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Uso", você poderá ativar o True Tone (se o dispositivo for compatível) e usar o "Zoom da Tela" para
ajustar o tamanho dos ícones e do texto da tela de Início.
Se você tiver um iPhone X ou posterior, saiba mais sobre como usar gestos para navegar no
dispositivo. Se você tiver um iPhone 7, iPhone 7 Plus, iPhone 8 ou iPhone 8 Plus, poderá ajustar o clique
do botão de Início.

Conclusão

Toque em "Começar" para começar a usar o dispositivo. Faça um backup dos dados para ter uma
cópia de segurança deles e saiba mais sobre outros recursos lendo o Manual do Usuário do iPhone, iPad
ou iPod touch.

Questões

01. (Prefeitura de Aracruz/ES - Auditor de Controle Interno - IBADE/2019) Dentre os sistemas


operacionais abaixo, o que foi desenhado para Tablets e Smart Phones denomina-se:
(A) Windows Server.
(B) Android.
(C) Windows Office.
(D) Linux.
(E) Z/OS.

02. (UFT - Administrador - COPESE - UFT/2018) Apesar das muitas especificações que um
smartphone pode ter, o maior responsável por sua usabilidade é o sistema operacional. Assim como nos
computadores, eles são a ponte que oferece ao usuário uma interação simples e amigável com os
aplicativos. É, portanto, um dos quesitos mais importantes para se analisar ao comprar um smartphone.
Assinale a única alternativa que contém exemplos de sistemas operacionais tradicionalmente
encontrados em smartphones.
(A) Apple iOS, Windows Phone e Google Android.
(B) Windows NT, Google plus e Apple Itunes.
(C) Firefox Edge, Apple Symbian e Microsoft Edge.
(D) Apple Intelligence, Google Analytics e Microsoft Azure.

Ganarito

01.B / 02.A

129
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Comentários

01. Resposta: B
Android é um sistema operacional (SO) baseado no núcleo Linux e atualmente desenvolvido pela
empresa de tecnologia Google. Com uma interface de usuário baseada na manipulação direta, o Android
é projetado principalmente para dispositivos móveis com tela sensível ao toque como smartphones e
tablets.

02. Resposta: A
Apple iOS, Windows Phone e Google Android são exemplos de sistemas operacionais encontrados
em smartphones.

5. Estrutura de dados e algoritmos: Busca sequencial e busca binária sobre


arrays; Ordenação (métodos da bolha, ordenação por seleção, ordenação por
inserção), lista encadeada, pilha, fila e noções sobre árvore binária.

LÓGICA DE PROGRAMAÇÃO

“Lógica é uma área da filosofia que estuda o fundamento, a estrutura e as expressões humanas do
conhecimento. A lógica foi criada por Aristóteles no século IV a.C. para estudar o pensamento humano e
distinguir interferências e argumentos certos e errados.38”

Então o que Podemos Entender por Lógica de Programação?


Lógica de programação é necessária para que se possa trabalhar com desenvolvimentos de
programas de computador, para estes serem desenvolvidos são utilizados dois passos básicos,
sequencias lógica (os passos executados desde para a resolução de um problema) e instruções (conjunto
de regras e ações para execução de instruções lógicas), tais passos são denominados algoritmos.

Algoritmos

“Um conjunto finito de regras que provê uma sequência de operações para resolver um tipo de
problema específico.”
Ao analisar a frase acima, podemos notar que fazemos o uso constante de algoritmos para a resolução
de qualquer problema em nosso dia-a-dia, desde o simples fato de calçar um par de sapatos ou até os
procedimentos utilizados para enviar um e-mail.
Os algoritmos devem seguir algumas características básicas que seguem abaixo:
- Um algoritmo deve ter início e fim;
- Resolver qualquer problema proposto;
- Nuca um algoritmos deve ser ambíguo (ter dupla interpretação);
- Todo algoritmos deve ter fim;

Em lógica de programação, os algoritmos são representados por várias formas diferentes, mas as
principais representações são:

Programas

Os programas de computadores ou software são algoritmos escritos com o suporte de uma linguagem
de programação como Cobol, Fortran, Visual Basic, Pascal, etc. então são transformados e linguagem de
máquina e executados no computador.

Descrição Narrativa

A descrição narrativa é uma das formas mais simples de representar um algoritmo, com essa forma, o
narrador descreve o passo-a-passo da resolução de um problema proporcionado ao receptor um
entendimento do assunto mesmo não conhecendo algoritmos.

38
Gabriela Cabral - http://www.brasilescola.com/filosofia/o-que-logica.htm

130
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Vejamos alguns exemplos:

Problema 1: Problema 3:
Chupar uma bala. Somar dois números.

Algoritmo: Algoritmo:
Pegue a bala, Atribua o primeiro número,
Retire-a do papel, Atribua o segundo número,
Coloque a bala na boca. Obtenha a soma dos números.

Problema 2: Problema 4:
Enviar um e-mail

Algoritmo: Algoritmo:
Abra o editor de e-mail,
Clique em redigir e-mail,
Selecione o destinatário,
Escreva o e-mail desejado,
Clique em Enviar.

Fluxograma ou Diagrama de Bloco

São utilizados símbolos gráficos universais que auxiliam para a compreensão dos passos que os
algoritmos seguem para que o problema seja resolvido, por se tratar de uma simbologia mundial, sua
interpretação é simples, porém, se faz necessário um conhecimento prévio.
Os símbolos mais utilizados são:

Símbolo Função

Início/Fim
Utilizado para ilustrar o Início e Fim do algoritmo.
Terminal

Calculo
Indica cálculos em geral no algoritmo.
Processamento

Entrada Apresenta a entrada de dados pelo teclado.

Entrada de Dados

Saída Apresenta as informações ou resultados no monitor ou


impressora.
Exibição

Decisã
o Apresenta estruturas de decisões no algoritmo.

Estrutura de Decisão

Exemplo:
Para resolução deste exemplo será utilizado o Problema 3, do tópico Descrição Narrativa:

131
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Linguagem Algorítmica

Linguagem Algorítmica consiste em uma pseudolinguagem de programação, que tem como função
facilitar o entendimento de uma linguagem de programação qualquer, nesse caso os comandos de
programação são escritos na linguagem nacional, em nosso caso, em português:
Exemplo:
Para resolução deste exemplo será utilizado o Problema 3, do tópico Descrição Narrativa:

Variáveis, Constantes, Operadores Lógicos e Tipos De Dados


Variável – Em programação de computadores, variáveis são espaços que ficam reservados
normalmente na memória RAM (memória física) do computador a fim de armazenar valores ou
expressões e seus valores podem variar durante a execução do programa.
Exemplo:

Variáveis

Lucro = ValorVenda ValorVenda


-

Variável Conteúdo
Nome “Luis Alexandre Boaygo dos
Santos”
Idade 32
Peso 80,00

Constantes: o valor sempre é fixo, não é modificado durante a execução do programa.

132
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplo:

𝑣𝑎𝑙𝑜𝑟1 + 𝑣𝑎𝑙𝑜𝑟2
𝑡𝑜𝑡𝑎𝑙 =
2

Constante

Tipos de Variáveis
As variáveis possuem quatro tipos básicos: numérico, caractere, alfanumérico e lógico.
- Numérico: especial para armazenar números inteiros e reais, também são utilizadas para cálculos
matemáticos;
- Caracteres: armazena um conjunto de caracteres não literais, não é possível realizar nenhum tipo de
cálculo com esse tipo de variável, sua utilização ideal é voltada ao armazenamento de textos, exemplo
um nome, um endereço, etc.
- Lógico: utilizada para testes e armazenamentos lógicos, assumem valores verdadeiro e falso.
- Alfanumérico: voltada ao armazenamento de letras e/ou números. Em determinados momentos
conter somente dados numéricos ou somente literais. Se utilizada para o armazenamento de números,
não poderá ser utilizada para operações matemáticas.

Operadores
Utilizados para a resolução de cálculos aritméticos, comparações de valores (chamados de operadores
relacionais) e testes lógicos, estes são descritos da seguinte forma:

Operadores Relacionais Operadores Aritméticos


Sinal Função Sinal Função
< Menor + Adição
> Maior - Subtração
<> Diferente * Multiplicação
Menor ou / Divisão
<=
igual
Maior ou ** Exponenciação
>=
igual
= Igual
E Operador E
Operador
OU
OU
Not Negação

Exemplos de operadores aritméticos:

subTotal := valoUn * Qtde

2+3*8

Total := 4 *( 2 / valor ) + 3

Exemplos de utilização de operadores relacionais:

Se ( num1 < num2) entao

enquanto ( i <= 10) faca


Utilização dos operadores AND, OR e NOT:

Valor 1 Operador Valor 2 Resultado


Verdadeiro AND Verdadeiro Verdadeiro
Verdadeiro AND Falso Falso
Falso AND Verdadeiro Falso
Falso AND Falso Falso
Verdadeiro OR Verdadeiro Verdadeiro

133
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Verdadeiro OR Falso Verdadeiro
Falso OR Verdadeiro Verdadeiro
Falso OR Falso Falso
Verdadeiro NOT Falso
Falso NOT Verdadeiro

Imagine a seguinte situação, A = 6, B = 9, C = 2

Expressões Resultado
A = AND B > Falso
B C
Como A = 6, B = 9 e C = 2 a expressão assume
os seguintes valores e verificações:
6=9e9>2
Feita a verificação, o resultado só será verdadeiro
se ambas as condições forem verdadeiras, logo 6 é
diferente de 2, portanto, o resultado é falso.

A AND B < Falso


<> B C
Nessa situação, a primeira expressão é
verdadeira, pois, 6 é diferente de 9, já a segunda é
falsa porque, 9 não é menor que 2, portanto a
resposta é Falsa.

A > NOT Verdadeiro


B
A primeira vista a expressão é falsa, pois, 6 é
menor que 9, mas como existe o operador de
negação NOT que faz a inversão da resposta, por tal
motivo a resposta é verdadeira.

A < OR B > Verdadeiro


B A
Para essa expressão retornar valor verdadeiro,
apenas uma das expressões deve ser verdadeira
nesse caso 6 é menor que 8, portanto retornou
verdadeiro.

Estruturas de Decisão e Repetição


São estruturas utilizadas para tomadas de decisão de acordo com uma comparação feita na estrutura
do algoritmo, assumindo respostas diferentes. A principal estruturas de comparação das linguagens é a
estrutura Se-Senão (If – Else), vejamos um exemplo em linguagem algorítmica e em linguagem de
programação.

Linguagem Algorítmica Linguagem de Programação

134
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Em ambos os casos acima, primeiro é calculada a média, a seguir é verificada se a média é maior que
6, caso seja, é retornada a mensagem afirmando que o aluno foi aprovado, caso não seja, a mensagem
retornada é de reprova do aluno.
As estruturas de repetição servem para repetir comandos até que a condição proposta seja satisfeita,
as principais são Enquanto (While) e Para (For), vejamos alguns exemplos:
O algoritmo abaixo foi desenvolvido em Linguagem de Programação Pascal a fim de exibir uma
tabuada escolhida pelo usuário, note que assim que o programa é executado a variável M recebe o valor
1, essa é chamada de variável de controle, então ao usuário digitar o valor da variável T solicitado pelo
programa, é feita uma verificação através da estrutura While até que a variável tenha o valor igual a 10,
esse valor é alterado toda vez que a estrutura passa pelo comando “M := M + 1”, ou seja, inicialmente M
possui valor 1, somando mais 1 fica 2 e assim por diante, até que ele tenha valor 10.

Linguagens de Programação

As Linguagens de programação (LP) são os meio de comunicação entre usuário e computador e são
utilizadas para resolução de problemas computacionais, ou seja, são utilizadas para a criação de
programas de computador (software). Ao longo do tempo essas linguagem estão passando por
transformações, portanto, foram classificadas em 5 gerações, sendo:

Primeira Geração: Linguagem de Máquina


Esta é uma linguagem de Instrução, isso quer dizer que, utiliza como suporte programação binária
operando diretamente endereço de registradores ou memória, então existe comunicação direta com o
hardware.

Segunda Geração: Linguagens Simbólicas (Assembly)


Desenvolvida com a finalidade de minimizar as dificuldades encontradas na programação em notação
binária.
A partir dessa geração é possível que os usuários operem utilizando mnemônicos (palavras reservadas
da linguagem de programação), sistemas de grupos de códigos operacionais semelhantes recebem
nomes que lembram suas funções.

Terceira Geração: Linguagens Orientadas ao Usuário (LOU)


São linguagens procedimentais (utilizam sequencias de comandos), tiveram início década de 60 como
FORTRAN, ALGOL, COBOL, PL/I, ADA e algumas perduram até os dias de hoje, como é o caso da
Linguagem Pascal. Essas linguagens revolucionaram o universo do desenvolvimento de software, pois,
através delas os softwares são capazes de utilizar instruções de entrada e saída efetuar cálculos
aritméticos e/ou lógicos e realizar controle de fluxo de execução.

Quarta Geração: Linguagens Orientadas à Aplicações


Seu objetivo é facilitar a programação de computadores proporcionando a usuários finais a resolvendo
problemas que antes só eram possíveis a programadores de computador.
Exemplo: Geradores de relatórios, preenchimento de formulários, Excel, Linguagens SQL.

Quinta Geração: Linguagens de Conhecimento


São utilizadas principalmente na área de inteligência artificial, a partir desse geração é possível que
os softwares autocriem bases de conhecimento obtidas a partir de especialistas.

135
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Compilação de Programas

Tradutores
São programas que traduzem o código fonte criado em uma linguagem de programação para um
programa objeto equivalente escrito em outra linguagem (denominada linguagem objeto).
Compiladores – Convertem programas escritos em linguagem de alto nível (linguagens de
programação) para programas equivalentes em linguagem simbólica ou de máquina, normalmente este
é um arquivo executável.
Interpretadores – São programas de computador que fazem a leitura de um código fonte de uma
linguagem de programação interpretada e o converte em código executável. Em certos casos, o
interpretador lê linha-por-linha e converte em código objeto (ou bytecode) à medida que vai executando
o programa e, em outros casos, converte o código fonte por inteiro e depois o executa. Uma
particularidade do interpretador é que ele trabalha utilizando uma máquina virtual(MV).

Estrutura de Dados
São utilizados para manipulação e organização de dados. A organização e os métodos para manipular
essa estrutura possibilitam a diminuição do espaço ocupado pela memória RAM, além de tornar o código-
fonte do programa mais simplificado.
Uma pilha é uma das várias estruturas de dados que admitem remoção de elementos e inserção de
novos elementos estas organizações são feitas através de pilhas e filas.
As filas e pilhas são estruturas usualmente implementadas através de listas, restringindo a política de
manipulação dos elementos da lista.
Uma fila estabelece uma política chamada FIFO -- first in, first out (primeiro a entrar e primeiro a sair).
Em outras palavras, a ordem estabelecida na lista é a ordem de inserção. No momento de retirar um nó
da lista, o nó mais antigo (o primeiro que entrou) é o primeiro a ser retirado.

Depuração (Debugging)

Programar é um processo complicado e, como é feito por seres humanos, frequentemente conduz a
erros39. Por mero capricho, erros em programas são chamados de bugs e o processo de encontrá-los e
corrigi-los é chamado de depuração (debugging).
Três tipos de erros podem acontecer em um programa: erros de sintaxe, erros em tempo de execução
(runtime errors) e erros de semântica (também chamados de erros de lógica). Distinguir os três tipos ajuda
a localizá-los mais rápido:

Erros de Sintaxe
O interpretador do Python só executa um programa se ele estiver sintaticamente correto. Caso
contrário, o processo falha e retorna uma mensagem de erro. O termo sintaxe refere-se à estrutura de um
programa e às regras sobre esta estrutura. Por exemplo, em português, uma frase deve começar com
uma letra maiúscula e terminar com um ponto.

esta frase contém um erro de sintaxe. Assim como esta

Para a maioria dos leitores, uns errinhos de sintaxe não chegam a ser um problema significativo e é
por isso que conseguimos ler a poesia moderna de e.e. cummings sem cuspir mensagens de erro. Python
não é tão indulgente. Se o seu programa tiver um único erro de sintaxe em algum lugar, o interpretador
Python vai exibir uma mensagem de erro e vai terminar, e o programa não vai rodar. Durante as primeiras
semanas da sua carreira como programador, você provavelmente perderá um bocado de tempo
procurando erros de sintaxe. Conforme for ganhando experiência, entretanto, cometerá menos erros e os
localizará mais rápido.

Erros em Tempo de Execução (Runtime Errors)


O segundo tipo de erro é o de runtime, ou erro em tempo de execução, assim chamado porque só
aparece quando você roda o programa. Esses erros também são conhecidos como exceções, porque
normalmente indicam que alguma coisa excepcional (e ruim) aconteceu.
Erros de runtime são raros nos programas simples que você vai ver nos primeiros capítulos. Então, vai
demorar um pouco até você se deparar com um erro desse tipo.

39
https://aprendacompy.readthedocs.io/pt/latest/capitulo_01.html#o-que-e-depuracao-debugging

136
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Erros de Semântica (Ou de Lógica)
O terceiro tipo de erro é o erro de semântica (mais comumente chamado de erro de lógica). Mesmo
que o seu programa tenha um erro de semântica, ele vai rodar com sucesso, no sentido de que o
computador não vai gerar nenhuma mensagem de erro. Só que o programa não vai fazer a coisa certa,
mas sim alguma outra coisa. Especificamente, aquilo que você tiver dito para ele fazer (o computador
trabalha assim: seguindo ordens).
O problema é que o programa que você escreveu não é aquele que você queria escrever. O significado
do programa (sua semântica ou lógica) está errado. Identificar erros semânticos pode ser complicado,
porque requer que você trabalhe de trás para frente, olhando a saída do programa e tentando imaginar o
que ele está fazendo.

Depuração Experimental (Debugging)


Uma das habilidades mais importantes que você vai adquirir é a de depurar. Embora possa ser
frustrante, depurar é uma das partes intelectualmente mais ricas, desafiadoras e interessantes da
programação.
De certa maneira, a depuração é como um trabalho de detetive. Você se depara com pistas, e tem que
deduzir os processos e eventos que levaram aos resultados que aparecem.
Depurar também é como uma ciência experimental. Uma vez que você tem uma ideia do que está
errado, você modifica o seu programa e tenta de novo. Se a sua hipótese estiver correta, então você
conseguiu prever o resultado da modificação e ficou um passo mais perto de um programa que funciona.
Se a sua hipótese estiver errada, você tem que tentar uma nova.
Para algumas pessoas, programação e depuração são a mesma coisa. Ou seja, programar é o
processo de depurar um programa gradualmente, até que ele faça o que você quer. A ideia é começar
com um programa que faça alguma coisa e ir fazendo pequenas modificações, depurando-as conforme
avança, de modo que você tenha sempre um programa que funciona.
Por exemplo, o Linux é um sistema operacional que contém milhares de linhas de código, mas
começou como um programa simples, que Linus Torvalds usou para explorar o chip Intel 80386. De
acordo com Larry Greenfield, um dos primeiros projetos de Linus Torvalds foi um programa que deveria
alternar entre imprimir AAAA e BBBB. Isso depois evoluiu para o Linux. (The Linux User’s Guide Versão
Beta 1)

Orientação a Objetos

A orientação a objetos (O.O.) é um padrão de desenvolvimento de software utilizado em várias


linguagens de programação como por exemplo C, Java, PHP, etc. Que são voltadas a utilizar estes
métodos para diminuir a diferença semântica entre a realidade e o modelo Para entendermos o
funcionamento da O.O. é necessário conhecermos as bases que sustentam essas técnicas, a seguir veja
algumas características:
Classe é algo generalizado, ou seja, uma classe define o comportamento dos objetos através de seus
métodos (ações de uma classe), Exemplo de classe: Cliente.
Uma classe é dividida um três partes, nome, atributos e métodos.
Vejamos um exemplo de ilustração de uma classe na UML:

Subclasse é uma nova classe que herda características (atributos e/ou métodos) de sua classe pai.
Objeto – É algo do mundo real, que proporciona vida a classe, enquanto Aluno é uma Classe Luís
Alexandre é um Objeto dessa classe.
Método definem as habilidades dos objetos. Bidu é uma instância da classe Cachorro, portanto tem
habilidade para latir, implementada através do método de um latido. Um método em uma classe é apenas
uma definição. A ação só ocorre quando o método é invocado através do objeto, no caso Bidu. Dentro do

137
1678859 E-book gerado especialmente para DANIEL CRISTIAN
programa, a utilização de um método deve afetar apenas um objeto em particular; Todos os cachorros
podem latir, mas você quer que apenas Bidu dê o latido. Normalmente, uma classe possui diversos
métodos, que no caso da classe Cachorro poderiam ser sente, coma e morda.
Herança (ou generalização) é o mecanismo pelo qual uma classe (subclasse) pode estender outra
classe (superclasse), aproveitando seus comportamentos (métodos) e variáveis possíveis (atributos). Um
exemplo de herança: Mamífero é superclasse de Humano. Ou seja, um Humano é um mamífero. Há
herança múltipla quando uma subclasse possui mais de uma superclasse. Essa relação é normalmente
chamada de relação "é um".
Polimorfismo consiste em quatro propriedades que a linguagem pode ter (atente para o fato de que
nem toda linguagem orientada a objeto tem implementado todos os tipos de polimorfismo):
Universal:Inclusão: um ponteiro para classe mãe pode apontar para uma instância de uma classe filha
(exemplo em Java: "List lista = new LinkedList();" (tipo de polimorfismo mais básico que existe)
Paramétrico: se restringe ao uso de templates (C++, por exemplo) e generics (Java/C♯)
Ad-Hoc: Sobrecarga: duas funções/métodos com o mesmo nome mas assinaturas diferentes
Coerção: a linguagem que faz as conversões implicitamente (como por exemplo atribuir um int a um
float em C++, isto é aceito mesmo sendo tipos diferentes pois a conversão é feita implicitamente)

Princípios de Resolução de Problemas40

Primeiramente, é importante entender e compreender a palavra "problema". Pode-se dizer que


problema é uma proposta duvidosa, que pode ter numerosas soluções, ou questão não solvida e que é
objeto de discussão, segundo definição encontrada no dicionário Aurélio.
Do ponto de vista desta obra, problema é uma questão que foge a uma determinada regra, ou melhor,
é o desvio de um percurso, o qual impede de atingir um determinado objetivo com eficiência e eficácia.
Diferentes das diagramações clássicas, que não fornecem grandes subsídios para análise, os
diagramas de blocos são realmente o melhor instrumento para avaliação do problema do fluxo de
informações de um dado sistema. Por esse motivo deve- -se resolver um problema de lógica
(principalmente se for da área de processamento eletrônico de dados) usando um procedimento de
desenvolvimento.
Para desenvolver um diagrama correto, deve-se considerar como procedimentos prioritários os itens
seguintes:
- Os diagramas devem ser feitos e quebrados em vários níveis. Os primeiros devem conter apenas as
ideias gerais, deixando para as etapas posteriores os detalhamentos necessários;
- Para o desenvolvimento correto de um fluxograma, sempre que possível, deve ser desenvolvido de
cima para baixo e da esquerda para direita;
- É incorreto e "proibido" ocorrer cruzamento das linhas de fluxo de dados.
Tome como exemplo uma escola qualquer, cujo cálculo da média é realizado com as quatro notas
bimestrais que determinam a aprovação ou reprovação dos seus alunos. Considere ainda que o valor da
média deve ser maior ou igual a 7 para que haja aprovação.

Diagrama de bloco para o cálculo da média escolar.

A segunda etapa apresenta um detalhamento no que se refere à entrada e saída, ou seja, deve-se
entrar com as quatro notas bimestrais para se obter, como resultado, o cálculo da média e assim definir
a aprovação ou reprovação do aluno. A figura abaixo apresenta o diagrama de blocos com mais detalhes.

40
MANZANO, JA OLIVEIRA, and J. F. Algoritmos. "Lógica para Desenvolvimento de Programação de Computadores." São Paulo: Érica (2000).

138
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A terceira etapa consiste em trabalhar o termo "determinar a aprovação". Para ser possível determinar
algo, é necessário estabelecer uma condição. Assim sendo, uma condição envolve uma decisão a ser
tomada segundo um determinado resultado. No caso, a média. Desta forma, a condição de aprovação:
média maior ou igual a 7 (sete), deve ser considerada no algoritmo. Com isso, inclui-se este bloco de
decisão, como mostra a figura abaixo.

139
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Muitas vezes é preferível construir o diagrama de blocos trabalhando com variáveis.

Exemplo da utilização de variáveis

Particularidades entre Lógicas

As representações gráficas de um diagrama de blocos podem ser feitas de várias maneiras e


possuírem estruturas diferenciadas, porém isto não impede que a maneira de solucionar o problema seja
eficiente. Segundo Verzello nos diz em uma de suas obras, assim como um arquiteto desenha e escreve
especificações para descrever como uma tarefa (por exemplo, construção de um edifício) deverá ser
efetuada e o engenheiro do projeto desenvolve um esquema detalhado das atividades de construção, um
especialista em informação desenvolve um plano, que será comunicado a outros, de como o problema
de processamento de dados deve ser resolvido.
Para auxiliarmos na sua compreensão, mostraremos como estes conceitos de estruturas, bem como
as particularidades de conexões e dos procedimentos entre o método lógico, encaixam-se ou não para
resolução dos problemas de processamento de dados. A seguir, são apresentados alguns tipos de
procedimentos individualmente.

Linear
A técnica lógica linear é conhecida como um modelo tradicional de desenvolvimento e resolução de
um problema. Não está ligada a regras de hierarquia ou de estruturas de linguagens específicas de
programação de computadores. Devemos entender que este tipo de procedimento está voltado à técnica
matemática, a qual permite determinar a atribuição de recursos limitados, utilizando uma coleção de
elementos organizados ou ordenados por uma só propriedade, de tal forma que cada um deles seja
executado passo a passo de cima para baixo, em que tenha um só "predecessor" e um só "sucessor". A
figura abaixo apresenta um exemplo deste tipo de lógica.

140
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Estruturada
A técnica da lógica estruturada é a mais usada pelos profissionais de processamento eletrônico de
dados. Possui características e padrões particulares, os quais diferem dos modelos das linguagens
elaboradas por seus fabricantes. Tem como pontos fortes para elaboração futura de um programa,
produzi-lo com alta qualidade e baixo custo.
A sequência, a seleção e a iteração são as três estruturas básicas para a construção do diagrama de
blocos. A figura abaixo seguinte apresenta um exemplo do tipo de lógica estruturada.

Modular
A técnica da lógica modular deve ser elaborada como uma estrutura de partes independentes,
denominada de módulos, cujo procedimento é controlado por um conjunto de regras. Segundo James
Martin, suas metas são as seguintes:
- Decompor um diagrama em partes independentes;
- Dividir um problema complexo em problemas menores e mais simples;
- Verificar a correção de um módulo de blocos, independentemente de sua utilização como uma
unidade em um processo maior.
A modularização deve ser desenvolvida, se possível, em diferentes níveis. Poderá ser utilizada para
separar um problema em sistemas, um sistema em programas e um programa em módulos. A figura
abaixo apresenta um exemplo do tipo de lógica modular.

141
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Português Estruturado
Como foi visto até agora, o diagrama de blocos é a primeira forma de notação gráfica, mas existe outra,
que é uma técnica narrativa denominada pseudocódigo, também conhecida como português estruturado
ou chamada por alguns de portugol.
Esta técnica de algoritmização é baseada em uma PDL - Program Design Language (Linguagem de
Projeto de Programação). Nesta obra, estamos apresentando-a em português. A forma original de escrita
é conhecida como inglês estruturado, muito parecida com a notação da linguagem PASCAL. A PDL (neste
caso, o português estruturado) é usada como referência genérica para uma linguagem de projeto de
programação, tendo como finalidade mostrar uma notação para elaboração de algoritmos, os quais serão
utilizados na definição, criação e desenvolvimento de uma linguagem computacional (Clipper, C, Pascal,
Delphi, Visual-Objects) e sua documentação. Abaixo, é apresentado um exemplo deste tipo de algoritmo.

programa MÉDIA
var
RESULTADO : caractere
N1, N2, N3, N4 : real
SOMA, MÉDIA : real
início
leia N1, N2, N3, N4
SOMA <- N1 + N2 + N3 + N4
MÉDIA <- SOMA / 4
se (MÉDIA >= 7) então
RESULTADO <— "Aprovado"
senão
RESULTADO <- "Reprovado"
fim_se
escreva "Nota 1 : ", N1
escreva "Nota 2 : ", N2
escreva "Nota 3 : ", N3
escreva "Nota 4: ", N4
escreva "Soma: “, SOMA
escreva "Média : ", MÉDIA
escreva "Resultado: ", RESULTADO
fim

A diferença entre uma linguagem de programação de alto nível utilizada em computação e uma PDL
é que esta (seja escrita em português, inglês ou qualquer outro idioma) não pode ser compilada em um
computador (por enquanto). Porém, existem "Processadores de PDL" que possibilitam traduzir essa
linguagem numa representação gráfica de projeto, fornecendo uma variedade de informações, como:
tabelas de referência cruzada, mapas de aninhamento, índice operacional do projeto, entre tantas outras.

142
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Tipos de Dados41

Existem três tipos básicos de dados que iremos manipular nos algoritmos que iremos criar:
- Dados numéricos
- Dados literais ou alfanuméricos
- Dados lógicos

Dados Numéricos
O conjunto de dados mais comuns é o de números naturais, que é representado por N. Este conjunto
é definido como
N = {0,1,2,3,...}

Este conjunto de números é usado quando queremos falar sobre o número de amigos que temos ou
quantos CDs musicais temos na nossa coleção. Embora seja fácil imaginar que um pastor de ovelhas há
3000 anos atrás pudesse usar este conjunto com facilidade, é bom lembrar que o conceito do número
zero é difícil de ser entendido. O conjunto dos números naturais usado pelos primeiros seres humanos
não incluía o zero. Os pastores sabiam que se 20 ovelhas tinham ido para os campos, eles tinha de
esperar pelas mesma 20 ovelhas na volta à noite. Agora se não havia ovelhas para que precisaríamos
contar, somar ou subtrair? A natureza tem horror ao vácuo e o nada é um conceito complicado para os
seres humanos. O número zero é uma invenção dos matemáticos hindus e é recente, sendo introduzido
em XXXX aproximadamente.
O conjunto dos números naturais é subconjunto dos conjunto dos números inteiros que é definido como
Z = {...,-3,-2,-1,0,+1,+2,+3,...}

Aqui estamos falando de conceitos mais complicados que hoje em dia já fazem parte do nosso dia a
dia. Estamos no domínio dos números relativos, que incluem os números positivos, o zero e os números
negativos. A mente humana teve de atingir graus de abstração maiores para imaginar operações que
incluíam números negativos. Hoje em dia não é necessário ser um matemático pós-graduado para
trabalhar facilmente com este tipo de números. Todos nós somos capazes de perceber que uma
temperatura de -3 graus centígrados e mais fria do que +3 graus. Quem não fica preocupado quando
sabe que sua conta corrente no banco está com saldo de -300 reais. Rapidamente iremos tentar
transformar este número negativo em número positivo, e o que mais importante sabemos que precisamos
de pelo menos 300 reais para sair do vermelho.
O próximo conjunto na nossa curta viagem pelos domínios dos números é o conjunto dos números
fracionários, que é representado por Q. Este conjunto é composto por todos os números que podem ser
escritos como uma fração da forma p/q onde p e q pertencem ao conjunto dos números inteiros. Este
conjunto pode ser definido como:
Q = {p/q | p, q pertencem a Z}

Continuando nesta excursão vamos para o conjunto dos números reais (R) que é a união do conjunto
dos números fracionários e o dos números irracionais. Números irracionais não são os números que não
conseguem pensar, mas sim aqueles que não podem ser expressos por uma fração p/q. Um exemplo
muito conhecido de número irracional é o PI que é igual a 3.14159...
O último conjunto é o dos números complexos. Neste conjunto os números são representados da
seguinte maneira
n = a + ib

Os números a e b são números reais e i representa a raiz quadrada do número inteiro -1. Quando b é
igual a 0 o número é um número pertencente ao conjunto dos reais. Como pode-se ver, a medida que
fomos passando de um conjunto para outro, aumentou o nível de abstração das quantidades que os
números pertencentes aos conjuntos representam.
A Figura a seguir é uma representação das relações de pertinência entre os conjuntos de números
que analisamos até aqui.

41
http://equipe.nce.ufrj.br/adriano/algoritmos/apostila/tipos.htm

143
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Os dados numéricos que os algoritmos que iremos criar e que a maioria dos computadores manipulam
são de dois tipos:
- Dados inteiros.
- Dados reais.

Neste ponto é importante assinalar dois fatos importantes. Primeiro computadores trabalham com uma
base diferente da base que usamos todos dias que é a base 10. Computadores usam a base 2, e no
processo de conversão entre bases podem ocorrer problemas de perda de dígitos significativos. Por
exemplo, o número real 0.6 ao ser convertido para a base dois gera uma dízima periódica.
Outro fato importante é que a largura das palavras de memória do computador é limitada e portanto o
número de dígitos binários que podem ser armazenados é função deste tamanho. Isto é similar ao que
aconteceria se tivéssemos que limitar o número o dígitos decimais que usamos para representar os
números de nossas contas no banco. Por exemplo, assuma que só podemos armazenar quantias com 6
dígitos inteiros e dois decimais, deste modo se ganhássemos na loteria R$ 1.000.000,00 não seria
possível representar este valor no extrato do banco. Portanto no processo de conversão e desconversão
entre bases pode haver perda de informação.

Dados Numéricos Inteiros


O conjunto dos dados inteiros pode ser definido como
Z={...,-3,-2,0,1,2,...}.

As linguagens usadas para programar computadores são muito exigentes com a maneira com que os
dados são representados. Por esta razão vamos passar a definir como deveremos representar os dados
nos algoritmos. Os dados inteiros tem a seguinte forma:
NúmeroInteiro = [+,-]algarismo{algarismo}

Neste texto iremos usar uma notação especial para definir como iremos representar os elementos da
linguagem. A medida que formos apresentando os elementos a notação será também explicada. No caso
da definição dos dados inteiros temos os seguintes elementos. O elemento básico é o algarismo que é
um dos seguintes caracteres:
algarismo = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Os elementos entre colchetes são opcionais. Portanto, o sinal de + e - entre colchetes significa que um
número inteiro pode ou não ter sinal. Em seguida temos um algarismo que é obrigatório. Isto é dados
inteiros tem de ter pelo menos um algarismo. A seguir temos a palavra algarismo entre chaves, o que
significa que um número inteiro deve ter pelo menos um algarismo e pode ser seguido por uma sequência
de algarismos. Deste modo elementos que aparecem entre chaves são elementos que podem ser
repetidos.
São portanto exemplos de números inteiros:
a) +3
b) 3
c) -324

Como exemplos de formas erradas de representação de números inteiros, temos:


a) +3.0 Não é possível usar ponto decimal
b) + 123 Espaços em branco não são permitidos

144
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Dados Numéricos Reais
Os dados reais tem a seguinte forma:
[+,-]algarismo{algarismo}.algarismo{algarismo}.

Ou seja um número real pode ou não ter sinal, em seguida um conjunto de pelo menos um algarismo,
um ponto decimal e depois um conjunto de pelo menos um algarismo. É importante notar que o separador
entre a parte inteira e a fracionário é o ponto e não a vírgula.
São exemplos de números reais:
a) 0.5
a) +0.5
a) -3.1415

Abaixo mostramos exemplos de formas erradas de representação de números reais, temos:


a) +3,0 Vírgula não pode ser separador entre as partes real e inteira
b) 0.333... Não é possível usar representação de dízimas

Dados Literais
Dados literais servem para tratamento de textos. Por exemplo, um algoritmo pode necessitar imprimir
um aviso para os usuários, ou um comentário junto com um resultado numérico. Outra possibilidade é a
necessidade de ler dados tais como nomes, letras, etc.
Este tipo de dados pode ser composto por um único caractere ou por um conjunto de pelo menos um
destes elementos. Conjuntos são conhecidos como cadeias de caracteres, tradução da expressão em
inglês, "character string".
Um ponto importante que deve ser abordado agora é o que se entende por caractere. Caracteres são
basicamente as letras minúsculas, maiúsculas, algarismos, sinais de pontuação, etc. Em computação
caracteres são representados por códigos binários e o mais disseminado de todos é o código ASCII. Este
padrão foi definido nos Estados Unidos e é empregado pela quase totalidade dos fabricantes de
computadores e programas. O apêndice mostra a tabela ASCII com estes códigos.
Os caracteres que normalmente são empregados nos algoritmos são os seguintes:
Letras maiúsculas:
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z

Letras minúsculas:
a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z

Algarismos:
0|1|2|3|4|5|6|7|8|9

Caracteres de pontuação:
;|:|!|?|*|(|)|\|/|+|-|=|<|>

Constantes Caracter
Caracteres podem aparecer sozinhos, e neste caso são chamados de constante caracter e são
representados entre o caracter '. Abaixo mostramos exemplos de constantes caracter:
'a'
'A'
';'
'+'

Cadeias de Caracter
Cadeias de caracteres são conjuntos de um ou mais caracteres e são cercados pelo caracter ". Por
exemplo:
"Linguagem de programação"
"Qual é o seu nome?"
"12345"

Dados Lógicos
Este tipo de dados é intensamente aplicado durante o processo de tomada de decisões que o
computador frequentemente é obrigado a fazer. Em muitos textos este tipo de dados também é chamado

145
1678859 E-book gerado especialmente para DANIEL CRISTIAN
de dados booleanos, devido a George Boole, matemático que deu ao nome à álgebra (álgebra booleana)
que manipula este tipo de dados. Os dados deste tipo somente podem assumir dois valores: verdadeiro
e falso.
Computadores tomam decisões, durante o processamento de um algoritmo, baseados nestes dois
valores. Por exemplo, considere a sentença a seguir que é um caso típico de decisão que o computador
é capaz de tomar sem intervenção humana.
Se está chovendo então procurar guarda-chuva.
Nesta sentença temos a questão lógica "Se está chovendo". Esta expressão somente pode ter como
resultado um de dois valores: verdade ou falso. Nos nossos algoritmos estes valores serão representados
por verdade e falso.
Tomando como exemplo a linguagem C, os dados podem assumir cinco tipos básicos que são:
- char: Caracter: O valor armazenado é um caractere. Caracateres geralmente são armazenados em
códigos (usualmente o código ASCII).
- int: Número inteiro é o tipo padrão e o tamanho do conjunto que pode ser representado normalmente
depende da máquina em que o programa está rodando.
- float: Número em ponto flutuante de precisão simples. São conhecidos normalmente como números
reais.
- double: Número em ponto flutuante de precisão dupla
- void: Este tipo serve para indicar que um resultado não tem um tipo definido. Uma das aplicações
deste tipo em C é criar um tipo vazio que pode posteriormente ser modificado para um dos tipos anteriores.

Estruturas de Dados: Vetores e Matrizes42

Os tipos básicos (inteiros, reais, caracteres) não são suficientes para exprimir estruturas de dados
complexas em algoritmos.
Um Vetor é uma das mais simples estruturas de dados.
Vetores são, essencialmente, listas de informações de um mesmo tipo, armazenadas em posição
contígua da memória, em ordem indexada.
Vetores são usados nos casos em que um conjunto de dados do mesmo tipo precisa ser armazenado
em uma mesma estrutura.
Um vetor é uma estrutura de dados homogênea, isto é, agrupa valores de um mesmo tipo;
O tipo do vetor é o mesmo tipo dos dados que ele armazena.
Um vetor é uma estrutura de dados indexada, ou seja:
- Cada valor pode ser acessado através de um índice, o qual corresponde a uma posição no vetor;
- Os índices são valores inteiros e positivos (0, 1, 2, 3, ...);
- Em outras palavras, uma posição específica do vetor pode ser acessada diretamente através do seu
índice.

Matriz: uma matriz é uma coleção de variáveis de um mesmo tipo que é referenciada por um nome
comum;
Vetores são matrizes unidimensionais;
Matrizes Bidimensionais: uma matriz bidimensional é uma matriz de matrizes unidimensionais;

Matrizes de uma Dimensão ou Vetores43


Este tipo de estrutura em particular é também denominado por alguns profissionais de como matrizes
unidimensionais. Sua utilização mais comum está vinculada à criação de tabelas. Caracteriza-se por ser
defini da uma única variável dimensionada com um determinado tamanho. A dimensão de uma matriz é
constituída por constantes inteiras e positivas. Os nomes dados às matrizes seguem as mesmas regras
de nomes utilizados para indicar as variáveis simples.
Para ter uma idéia de como utilizar matrizes em uma determinada situação, considere o seguinte
problema: "Calcular e apresentar a média geral de uma turma de 8 alunos. A média a ser obtida deve ser
a média geral das médias de cada aluno obtida durante o ano letivo". Desta forma será necessário somar
todas as médias e dividi-las por 8. A tabela seguintes apresenta o número de alunos, suas notas
bimestrais e respectivas médias anuais. É da média década aluno que será efetuado o cálculo da média
da turma.

42
http://www.deinf.ufma.br/~vidal/algoritmos1/vetoresmatrizes
43
MANZANO, JA OLIVEIRA, and J. F. Algoritmos. "Lógica para Desenvolvimento de Programação de Computadores." São Paulo: Érica (2000).

146
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Agora basta escrever um programa para efetuar o cálculo das 8 médias de cada aluno. Para
representar a média do primeiro aluno será utilizada a variável MD1, para o segundo MD2 e assim por
diante. Então tem-se:
MD1 =4.5
MD2 =6.5
MD3 =8.0
MD 4 = 3.5
MD 5 = 6.0
MD 6 = 7.0
MD 7 = 6.5
MD 8 = 6.0

Com o conhecimento adquirido até este momento, seria então elaborado um programa que efetuaria
a leitura de cada nota, a soma delas e a divisão do valor da soma por 8, obtendo-se desta forma a média,
conforme exemplo abaixo em português estruturado:

programa MÉDIA_TURMA
var
MD1, MD2, MD3, MD4, MD5, MD6, MD7, MD8 : real
SOMA, MÉDIA : real
início
SOMA <- 0
leia <- MD1, MD2, MD3, MD4, MD5, MD6, MD7, MD8
SOMA <- MD1 + MD2 + MD3 + MD4 + MD5 + MD6 + MD7 + MD8
MÉDIA <- SOMA / 8
escreva MÉDIA
fim

Perceba que para receber a média foram utilizadas oito variáveis. Com a técnica de matrizes poderia
ter sido utilizada apenas uma variável com a capacidade de armazenar oito valores.

Operações Básicas com Matrizes do Tipo Vetor


Uma matriz de uma dimensão ou vetor será, neste trabalho, representada por seu nome e seu tamanho
(dimensão) entre colchetes. Desta forma seria uma matriz MD[1 ..8], sendo seu nome MD, possuindo um
tamanho de 1 a 8. Isto significa que poderão ser armazenados em MD até oito elementos. Perceba que
na utilização de variáveis simples existe um regra: uma variável somente pode conter um valor por vez.
No caso das matrizes, poderão armazenar mais de um valor por vez, pois são dimensionadas exatamente
para este fim. Desta forma poder-se-á manipular uma quantidade maior de informação com pouco
trabalho de processamento. Deve-se apenas considerar que com relação à manipulação dos elementos
de uma matriz, eles ocorrerão de forma individualizada, pois não é possível efetuar a manipulação de
todos os elementos do conjunto ao mesmo tempo.
No caso do exemplo do cálculo da média dos 8 alunos, ter-se-ia então uma única variável indexada (a
matriz) contendo todos os valores das 8 notas. Isto seria representado da seguinte forma:
MD[1] = 4.5 MD[2] = 6.5 MD[3] = 8.0 MD [4] =3.5 MD[5] = 6.0 MD[6] = 7.0 MD[7] = 6.5 MD[8] = 6.0

Observe que o nome é um só. O que muda é a informação indicada dentro dos colchetes. A esta
informação dá-se o nome de índice, sendo este o endereço em que o elemento está armazenado. E

147
1678859 E-book gerado especialmente para DANIEL CRISTIAN
necessário que fique bem claro que elemento é o conteúdo da matriz, neste caso os valores das notas.
No caso de MD[1 ] = 4.5, o número 1 é o índice; o endereço cujo elemento é 4.5 está armazenado.

Atribuição de uma Matriz


Anteriormente, foram utilizadas várias instruções em português estruturado para poder definir e montar
um programa. No caso da utilização de matrizes, será definida a instrução conjunto que indicará em
português estruturado a utilização de uma matriz, tendo como sintaxe: VARIÁVEL : conjunto[<dimensão>]
de ctipo de dado>, sendo que <dimensão> será a indicação dos valores inicial e final do tamanho do vetor
e <tipo de dado> se o vetor em questão irá utilizar valores reais, inteiros, lógicos ou caracteres.

Leitura dos Dados de uma Matriz


A leitura de uma matriz é processada passo a passo, um elemento por vez. A instrução de leitura é
leia seguida da variável mais o índice. A seguir, são apresentados diagrama de blocos e codificação em
português estruturado da leitura das notas dos 8 alunos, cálculo da média e a sua apresentação.

Diagrama de Blocos

Diagrama de blocos para leitura dos elementos de uma matriz tipo vetor.

programa MÉDIA_TURMA
var
MD : conjunto[1..8] de real
SOMA, MÉDIA : real
I : inteiro
início
SOMA <r- 0
para I de 1 até 8 passo 1 faça
leia MD[I]
SOMA <r- SOMA + MD[I]
fim_para
MÉDIA <r- SOMA / 8
escreva MEDIA
fim

Veja que o programa ficou mais compacto, além de possibilitar uma mobilidade maior, pois se houver
a necessidade de efetuar o cálculo para um número maior de alunos, basta dimensionar a matriz e mudar

148
1678859 E-book gerado especialmente para DANIEL CRISTIAN
o valor final da instrução para. Observe que no exemplo anterior, a leitura é processada uma por vez.
Desta forma, a matriz é controlada pelo número do índice que faz com que cada entrada aconteça em
uma posição diferente da outra. Assim sendo, a matriz passa a ter todas as notas. A tabela seguinte,
mostra como ficarão os valores armazenados na matriz:

Tenha cuidado para não confundir o índice com o elemento. índice é o endereço de alocação de uma
unidade da matriz, enquanto elemento é o conteúdo armaze¬nado em um determinado endereço.

Escrita dos Dados de uma Matriz


O processo de escrita de uma matriz é bastante parecido com o processo de leitura de seus
elementos. Para esta ocorrência deverá ser utilizada a instrução escreva seguida da indicação da variável
e seu índice. Supondo que após a leitura das 8 notas, houvesse a necessidade de apresentá-las antes
da apresentação do valor da média. Abaixo são apresentados o diagrama de blocos e a codificação em
português estruturado da escrita das notas dos 8 alunos antes de ser apresentado o cálculo da média.

Diagrama de Blocos

Diagrama de bloco para escrita dos elementos de uma matriz tipo vetor.

149
1678859 E-book gerado especialmente para DANIEL CRISTIAN
programa MÉDIA_TURMA
var
MD : conjunto[1..8] de real
SOMA, MÉDIA : real
I : inteiro
início
SOMA <- 0
para I de 1 até 8 passo 1 faça
leia MD[I]
SOMA <- SOMA + MD[I]
fim_para
MÉDIA <- SOMA / 8
para I de 1 até 8 passo 1 faça
escreva MD[I]
fim_para
escreva MÉDIA
fim

As Sub-rotinas

No geral, problemas complexos exigem algoritmos complexos. Mas sempre é possível dividir um
problema grande em problemas menores. Desta forma, cada parte menor tem um algoritmo mais simples,
e é esse trecho menor que é chamado de sub-rotina. Uma sub-rotina é na verdade um programa, e sendo
um programa poderá efetuar diversas operações computacionais (entrada, processamento e saída) e
deverá ser tratada como foram os programas projetados até este momento. As sub-rotinas são utilizadas
na divisão de algoritmos complexos, permitindo assim possuir a modularização de um determinado
problema, considerado grande e de difícil solução.
Ao trabalhar com esta técnica, pode-se deparar com a necessidade de dividir uma sub-rotina em outras
tantas quantas forem necessárias, buscando uma solução mais simples de uma parte do problema maior.
O processo de dividir sub-rotinas em outras é denominado Método de Refinamento Sucessivo.

O Método Top-Down
O processo de programar um computador torna-se bastante simples quando aplicado o método de
utilização de sub-rotinas (módulos de programas). Porém, a utilização dessas sub-rotinas deverá ser feita
com aplicação do método top down.
Um método bastante adequado para a programação de um computador é trabalhar com o conceito de
programação estruturada, pois a maior parte das linguagens de programação utilizadas atualmente
também são, o que facilita a aplicação deste processo de trabalho. O método mais adequado para a
programação estruturada é o Top-Down (De cima para baixo) o qual se caracteriza basicamente por:
Antes de iniciar a construção do programa, o programador deverá ter em mente as tarefas principais
que este deverá executar. Não é necessário saber como funcionarão, somente saber quantas são.
Conhecidas todas as tarefas a serem executadas, tem-se em mente como deverá ser o programa
principal, o qual vai controlar todas as outras tarefas distribuídas em suas sub-rotinas.
Tendo definido o programa principal, é iniciado o processo de detalhamento para cada sub-rotina.
Desta forma são definidos vários algoritmos, um para cada rotina em separado, para que se tenha uma
visão do que deverá ser executado em cada módulo de programa. Existem programadores que
estabelecem o número máximo de linhas de programa que uma rotina deverá possuir. Se o número de
linhas ultrapassa o limite preestabelecido, a rotina em desenvolvimento é dividida em outra sub-rotina (é
neste ponto que se aplica o método de refinamento sucessivo).
O método Top-Down faz com que o programa tenha uma estrutura semelhante a um organograma.
A figura abaixo apresenta um exemplo desta estrutura.
A utilização do método "de cima para baixo" permite que seja efetuado cada módulo de programa em
separado. Desta forma, cada um pode ser testado separadamente garantindo que o programa completo
esteja sem erro ao seu término.
Outro detalhe a ser considerado é que muitas vezes existem em um programa trechos de códigos que
são repetidos várias vezes. Esses trechos poderão ser utilizados como sub-rotinas, proporcionando um
programa menor e mais fácil de ser alterado num futuro próximo.

150
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A utilização de sub-rotinas e o uso do método Top-Down na programação permitem ao programador
elaborar rotinas exclusivas. Por exemplo, uma rotina somente para entrada, outra para a parte de
processamento e outra para a saída dos dados. Se o leitor comparar esta proposta com o que foi estudado
anteriormente, verá suas vantagens. Lembre-se dos programas anteriores todos os seus algoritmos de
saída obrigavam de certa forma efetuar primeiro a entrada dos dados.

Procedimentos
Um procedimento é um bloco de programa contendo início e fim e será identificado por um nome, por
meio do qual será referenciado em qualquer parte do programa principal ou do programa chamador da
rotina. Quando uma sub-rotina é chamada por um programa, ela é executada e ao seu término o controle
de processamento retorna automaticamente para a primeira linha de instrução após a linha que efetuou
a chamada da sub-rotina.
Com relação à criação da rotina, será idêntica a tudo o que já foi estudado sobre programação. Na
representação do diagrama de blocos, não há quase nenhuma mudança, a não ser pela troca das
identificações Início e Fim nos símbolos de Terminal e o novo símbolo Sub-rotina, que é idêntico ao
símbolo de processamento, porém se caracteriza pelas linhas paralelas às bordas esquerda e direita,
devendo ser utilizado no programa chamador. A sintaxe em português estruturado será também idêntica
ao estudo anterior. Observe em seguida, o código em português estruturado.

Português Estruturado
procedimento <nome do procedimento>
var
<variáveis>
inicio
<instruções>
fim

A melhor maneira de entender como trabalhar com uma sub-rotina é fazer a sua aplicação em um
programa mais complexo. Para tanto, imagine o seguinte problema:
Criar um programa calculadora que apresente um menu de seleções no programa principal. Esse menu
deverá dar ao usuário a possibilidade de escolher uma entre quatro operações aritméticas. Escolhida a
opção desejada, deverá ser solicitada a entrada de dois números, e processada a operação deverá ser
exibido o resultado.

Algoritmo
Note que esse programa deverá ser um conjunto de cinco rotinas, sendo uma principal e quatro
secundárias. A rotina principal efetuará o controle das quatro rotinas secundárias que, por sua vez,
pedirão a leitura de dois valores, farão a operação e apresentarão o resultado obtido. Afigura 11.2
apresenta um organograma com a ideia de hierarquização das rotinas do programa. A quinta opção não
se caracteriza por ser uma rotina, apenas a opção que vai encerrar o looping de controle do menu.

151
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Tendo uma ideia da estrutura geral do programa, será escrito em separado cada algoritmo com os
seus detalhes de operação. Primeiro o programa principal e depois as outras rotinas, de preferência na
mesma ordem em que estão mencionadas no organograma.

Programa Principal
1- Apresentar um menu de seleção com cinco opções:
1-Adição
2-Subtração
3-Multiplicação
4- Divisão
5-Fim de Programa

2- Ao ser selecionado um valor, a rotina correspondente deverá ser executada;


3- Ao escolher o valor 5, o programa deverá ser encerrado.

Rotina 1 - Adição
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a soma das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Rotina 2 - Subtração
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a subtração das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Rotina 3 - Multiplicação
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a multiplicação das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Rotina 4 - Divisão
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a divisão das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Observe que em cada rotina serão utilizadas as mesmas variáveis, mas elas não serão executadas ao
mesmo tempo para todas as operações. Serão utilizadas em separado e somente para a rotina escolhida.

Diagramas de Blocos
Perceba que na diagramação cada rotina é definida em separado como um programa independente.
O que muda é a forma de identificação do símbolo Terminal. Em vez de se utilizarem os termos Início e
Fim, utilizam-se o nome da sub-rotina para iniciar e a palavra Retomar para encerrar. Com relação ao
módulo principal, ele faz uso do símbolo Sub-rotina que indica a chamada de uma sub-rotina.

152
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Diagramas de blocos para o programa calculadora com sua sub-rotina.

153
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Português Estruturado
Em código português estruturado, serão mencionadas em primeiro lugar as sub-rotinas e por último o
programa principal. Quando no programa principal ou rotina chamadora for referenciada uma sub-rotina,
ela será sublinhada para facilitar sua visualização. Observe no programa a variável OPÇÃO para controlar
a opção do operador que é do tipo caractere. Outro detalhe a ser observado é o nome de cada rotina
mencionado junto da verificação da instrução se no módulo de programa principal.

programa CALCULADORA
var
OPÇÃO : caractere

{Sub-rotinas de cálculos} procedimento ROTSOMA


var
R, A, B : real
inicio
escreva "Rotina de Adição"
escreva "Entre um valor para A: “
leia A
escreva "Entre um valor para B: " leia B
R <- A + B
escreva "A soma de A com B é = ", R
fim

procedimento ROTSUBTRAÇÃO
var
R, A, B: real inicio
escreva "Rotina de Subtração" escreva "Entre um valor para A: "
leia A
escreva "Entre um valor para B: “
leia B
R <- A - B
escreva "A subtração de A com B é = ", R
fim

procedimento ROTMULTIPLICAÇÃO
var
R, A, B : real
inicio
escreva "Rotina de Multiplicação"
escreva "Entre um valor para A: “
leia A
escreva "Entre um valor para B: "
leia B
R <r- A * B
escreva "A multiplicação de A com B é = ", R
fim

procedimento ROTDIVISÃO var


R, A, B: real
inicio
escreva "Rotina de Divisão”
escreva "Entre um valor para A: "
leia A
escreva "Entre um valor para B: "
leia B
R <- A / B
escreva "A divisão de A com B é = ", R
fim

154
1678859 E-book gerado especialmente para DANIEL CRISTIAN
(Programa Principal}
início OPÇÃO <- "0"
enquanto (OPÇÃO <> "5") faça
escreva "1 - Adição"
escreva "2 - Subtração"
escreva "3 - Multiplicação"
escreva "4 - Divisão"
escreva "5 - Fim de Programa"
escreva "Escolha uma opção: "
leia OPÇÃO
se (OPÇÃO = "1") então rotsoma fim_se
se (OPÇÃO = "2") então rotsubtracão fim_se
se (OPÇÃO = "3") então rotmultiolicacão fim_se
se (OPÇÃO = "4") então rotdivisão fim_se
fim_enquanto
fim

Funções
Função também é um bloco de programa, como são os procedimentos, contendo início e fim e sendo
identificada por um nome, por meio do qual também será referenciada em qualquer parte do programa
principal. Uma sub-rotina de função é na verdade muito parecida com uma sub-rotina de procedimento.
A sintaxe em português estruturado é também idêntica ao estudo anterior. Observe em seguida, o código
em português estruturado de uma função.

Português Estruturado
função <nome da função> (parâmetros) : <tipo da função>
var
<variáveis>
início
<instruções>
fim
A sua principal diferença está no fato de uma função retornar um determinado valor, que é retornado
no próprio nome da função. Quando se diz valor, devem ser levados em consideração os valores
numéricos, lógicos ou literais (caracteres).

Parâmetros

Os parâmetros têm por finalidade servir como um ponto de comunicação bidirecional entre uma sub-
rotina e o programa principal ou uma outra sub-rotina hierarquicamente de nível mais alto. Desta forma,
é possível passar valores de uma sub-rotina ou rotina chamadora à outra sub-rotina e vice-versa,
utilizando parâmetros que podem ser formais ou reais.

Parâmetros Formais e Reais


Serão considerados parâmetros Formais quando forem declarados por meio de variáveis juntamente
com a identificação do nome da sub-rotina, os quais serão tratados exatamente da mesma forma que são
tratadas as variáveis globais ou locais. Considere como exemplo de parâmetros formais o código em
português estruturado da sub-rotina apresentado abaixo:

procedimento CALCSOMA(A, B : inteiro)


var
Z : inteiro
inicio
Z <- A + B escreva Z
fim

Observe que a variável Z é local e está sendo usada para armazenar a soma das variáveis A e B que
representam os parâmetros formais da sub-rotina CALCSOMA.
Serão considerados parâmetros Reais quando substituírem os parâmetros formais, quando da
utilização da sub-rotina por um programa principal ou por uma rotina chamadora. Considere como

155
1678859 E-book gerado especialmente para DANIEL CRISTIAN
exemplo de parâmetros reais o código em português estruturado do programa que faz uso da sub-rotina
CALCSOMA apresentado em seguida:

inicio
leia X
leia V
calcsoma (X. Y)
leia W
leia T
calcsoma (W, T)
calcsoma(8, 2)
fim

No trecho acima, toda vez que a sub-rotina CALCSOMA é chamada, faz-se uso de parâmetros reais.
Desta forma, são parâmetros reais as variáveis X, Y, W e T, pois seus valores são fornecidos pela
instrução leia e também os valores 8 e 2.

A Linguagem C44

A linguagem C foi criada por Dennis Ritchie, em 1972, no centro de Pesquisas da Bell Laboratories.
Sua primeira utilização importante foi a reescrita do Sistema Operacional UNIX, que até então era escrito
em assembly.
Em meados de 1970 o UNIX saiu do laboratório para ser liberado para as universidades. Foi o
suficiente para que o sucesso da linguagem atingisse proporções tais que, por volta de 1980, já existiam
várias versões de compiladores C oferecidas por várias empresas, não sendo mais restritas apenas ao
ambiente UNIX, porém compatíveis com vários outros sistemas operacionais.
O C é uma linguagem de propósito geral, sendo adequada à programação estruturada. No entanto é
mais utilizada escrever compiladores, analisadores léxicos, bancos de dados, editores de texto, etc.
A linguagem C pertence a uma família de linguagens cujas características são: portabilidade,
modularidade, compilação separada, recursos de baixo nível, geração de código eficiente, confiabilidade,
regularidade, simplicidade e facilidade de uso.

Visão Geral de um Programa C


A geração do programa executável a partir do programa fonte obedece a uma sequência de operações
antes de tornar-se um executável. Depois de escrever o módulo fonte em um editor de textos, o
programador aciona o compilador que no UNIX é chamado pelo comando cc. Essa ação desencadeia
uma sequência de etapas, cada qual traduzindo a codificação do usuário para uma forma de linguagem
de nível inferior, que termina com o executável criado pelo lincador.

Editor (módulo fonte em C)



Pré-processador (novo fonte expandido)

Compilador (arquivo objeto)

Lincador (executável)

Sintaxe
A sintaxe são regras detalhadas para cada construção válida na linguagem C. Estas regras estão
relacionadas com os tipos, as declarações, as funções e as expressões.
Os tipos definem as propriedades dos dados manipulados em um programa.
As declarações expressam as partes do programa, podendo dar significado a um identificador, alocar
memória, definir conteúdo inicial, definir funções.
As funções especificam as ações que um programa executa quando roda.
A determinação e alteração de valores, e a chamada de funções de I/O são definidas nas expressões.
As funções são as entidades operacionais básicas dos programas em C, que por sua vez são a união
de uma ou mais funções executando cada qual o seu trabalho. Há funções básicas que estão definidas

44
ftp://ftp.unicamp.br/pub/apoio/treinamentos/linguagens/c.pdf

156
1678859 E-book gerado especialmente para DANIEL CRISTIAN
na biblioteca C. As funções printf() e scanf() por exemplo, permitem respectivamente escrever na tela e
ler os dados a partir do teclado. O programador também pode definir novas funções em seus programas,
como rotinas para cálculos, impressão, etc.
Todo programa C inicia sua execução chamando a função main(), sendo obrigatória a sua declaração
no programa principal.
Comentários no programa são colocados entre /* e */ não sendo considerados na compilação.
Cada instrução encerra com ; (ponto e vírgula) que faz parte do comando.
Ex.:

main() /* função obrigatória */


{
printf("oi");
}

Identificadores
São nomes usados para se fazer referência a variáveis, funções, rótulos e vários outros objetos
definidos pelo usuário. O primeiro caracter deve ser uma letra ou um sublinhado. Os 32 primeiros
caracteres de um identificador são significativos. É case sensitive, ou seja, as letras maiúsculas diferem
das minúsculas.
int x; /*é diferente de int X;*/
Tipos
Quando você declara um identificador dá a ele um tipo. Os tipos principais podem ser colocados dentro
da classe do tipo de objeto de dado. Um tipo de objeto de dados determina como valores de dados são
representados, que valores pode expressar, e que tipo de operações você pode executar com estes
valores.

Tipos Inteiros

char [0,128) igual a signed char ou unsigned char


signed char (- inteiro de pelo menos 8 bits
128,128)
unsigned char (0,256) mesmo que signed char sem negativos
short (2-15,2 inteiro de pelo menos 16 bits tamanho
15 pelo menos igual a char

157
1678859 E-book gerado especialmente para DANIEL CRISTIAN
unsigned sort [0,216) mesmo tamanho que sort sem negativos
int (2- inteiro de pelo menos 16 bits; tamanho
15,215) pelo menos igual a short
unsigned int [0,216) mesmo tamanho que int sem negativos
long (2- inteiro com sinal de pelo menos 32 bits;
31,231) tamanho pelo menos igual a int
unsigned log [0,232) mesmo tamanho que long sem valores
negativos

Uma implementação do compilador pode mostrar um faixa maior do que a mostrada na tabela, mas
não uma faixa menor. As potencias de 2 usadas significam:

Tipos Flutuantes

float [3.4-38, pelo menos 6 dígitos de precisão


3.4+38] decimal
double (1.7- pelo menos 10 dígitos decimais e
308,1.7+308) precisão maior que do float
long double (1.7- pelo menos 10 dígitos decimais e
308,1.7+308) precisão maior que do double

Ex.:
main()
{
char c;
unsigned char uc;
int i;
unsigned int ui;
float f;
double d;
printf("char %d",sizeof(c));
printf("unsigned char %d",sizeof(uc));
printf("int %d",sizeof(i));
printf("unsigned int %d",sizeof(ui));
printf("float %d",sizeof(f));
printf("double %d",sizeof(d));
}

Operadores

Operador de Atribuição
O operador de atribuição em C é o sinal de igual "=". Ao contrário de outras linguagens, o operador de
atribuição pode ser utilizado em expressões que também envolvem outros operadores.

Aritméticos
Os operadores *, /, + e - funcionam como na maioria das linguagens, o operador % indica o resto de
uma divisão inteira.
i+=2; -> i=i+2;
x*=y+1; -> x=x*(y+1); d-=3; -> d=d-3;
Ex.:
main()
{

158
1678859 E-book gerado especialmente para DANIEL CRISTIAN
int x,y; x=10; y=3; printf("%d\n",x/y);
printf("%d\n",x%y);
}

Operadores de Relação e Lógicos


Relação refere-se as relações que os valores podem ter um com o outro e lógico se refere às maneiras
como essas relações podem ser conectadas. Verdadeiro é qualquer valor que não seja 0, enquanto que
0 é falso. As expressões que usam operadores de relação e lógicos retornarão 0 para falso e 1 para
verdadeiro.
Tanto os operadores de relação como os lógicos tem a precedência menor que os operadores
aritméticos. As operações de avaliação produzem um resultado 0 ou 1.

relacionais lógicos

> maior que && and


>= maior ou igual || ou
< menor ! not
<= menor ou igual
== igual
!= não igual

Ex.:

main()
{
int i,j;
printf ("digite dois números: ");
scanf("%d%d",&i,&j);
printf("%d == %d é %d\n",i,j,i==j);
printf("%d != %d é %d\n",i,j,i!=j);
printf("%d <= %d é %d\n",i,j,i<=j);
printf("%d >= %d é %d\n",i,j,i>=j);
printf("%d < %d é %d\n",i,j,i< j);
printf("%d > %d é %d\n",i,j,i> j);
}

Ex.:

main()
{
int x=2,y=3,produto;
if ((produto=x*y)>0) printf("é maior");
}

Incremento e Decremento
O C fornece operadores diferentes para incrementar variáveis. O operador soma 1 ao seu operando,
e o decremento subtrai 1. O aspecto não usual desta notação é que podem ser usado como operadores
pré-fixo(++x) ou pós-fixo(x++).
++ x incrementa x antes de utilizar o seu valor. x++ incrementa x depois de ser utilizado.
Ex.:

main()
{
int x=0;
printf("x= %d\n",x++);
printf("x= %d\n",x);
printf("x= %d\n",++x);

159
1678859 E-book gerado especialmente para DANIEL CRISTIAN
printf("x= %d\n",x);
}

Precedência
O nível de precedência dos operadores é avaliado da esquerda para a direita. Os parênteses podem
ser utilizados para alterar a ordem da avaliação.
++ -- mais alta
*/%
+ - mais baixa

Operador Cast
Sintaxe:
(tipo) expressão
Podemos forçar uma expressão a ser de um determinado tipo usando o operador cast.
Ex.:
main()
{
int i=1;
printf("%d/3 é: %f",i,(float) i/3);
}

Operador Sizeof
O operador sizeof retorna o tamanho em bytes da variável, ou seja, do tipo que está em seu operando.
É utilizado para assegurar a portabilidade do programa.

Funções Básicas da Biblioteca C

Função printf()
Sintaxe:
printf("expressão de controle",argumentos);
É uma função de I/O, que permite escrever no dispositivo padrão (tela).
A expressão de controle pode conter caracteres que serão exibidos na tela e os códigos de formatação
que indicam o formato em que os argumentos devem ser impressos. Cada argumento deve ser separado
por vírgula.

\n nova linha %c caractere simples


\t tab %d decimal
\b retrocesso %e notação científica
\" aspas %f ponto flutuante
\\ barra %o octal
\f salta formulário %s cadeia de
caracteres
\0 nulo %u decimal sem sinal
%x hexadecimal

Ex.:

main()
{
printf("Este é o numero dois: %d",2);
printf("%s está a %d milhões de milhas\ndo sol","Vênus",67);
}

Tamanho de campos na impressão:


Ex.:

main()
{

160
1678859 E-book gerado especialmente para DANIEL CRISTIAN
printf("\n%2d",350); printf("\n%4d",350);
printf("\n%6d",350)
}

Para arredondamento:
Ex.:

main()
{
printf("\n%4.2f",3456.78); printf("\n%3.2f",3456.78);
printf("\n%3.1f",3456.78);
printf("\n%10.3f",3456.78);
}

Para alinhamento:
Ex.:
main(){ printf("\n%10.2f %10.2f %10.2f",8.0,15.3,584.13);
printf("\n%10.2f %10.2f %10.2f",834.0,1500.55,4890.21);
}

Complementando com zeros à esquerda:


Ex.:

main()
{
printf("\n%04d",21);
printf("\n%06d",21);
printf("\n%6.4d",21);
printf("\n%6.0d",21);
}

Imprimindo caracteres:
Ex.:

main()
{
printf("%d %c %x %o\n",'A','A','A','A');
printf("%c %c %c %c\n",'A',65,0x41,0101);
}

A tabela ASCII possui 256 códigos de 0 a 255, se imprimirmos em formato caractere um número maior
que 255, será impresso o resto da divisão do número por 256 ; se o número for 3393 será impresso A
pois o resto de 3393 por 256 é 65.

Função scanf()
Também é uma função de I/O implementada em todos compiladores C. Ela é o complemento de printf()
e nos permite ler dados formatados da entrada padrão
(teclado).
Sua sintaxe é similar a printf ().
scanf("expressão de controle", argumentos);
A lista de argumentos deve consistir nos endereços das variáveis. C oferece um operador para tipos
básicos chamado operador de endereço e referenciado pelo símbolo "&" que retorna o endereço do
operando.

161
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Operador de Endereço &:
A memória do computador é dividida em bytes, e são numerados de 0 até o limite da memória. Estas
posições são chamadas de endereços. Toda variável ocupa uma certa localização na memória, e seu
endereço é o primeiro byte ocupado por ela.
Ex.:

main()
{
int num;
printf("Digite um número: ");
scanf("%d",&num);
printf("\no número é %d",num);
printf("\no endereço e %u",&num);
}

Função getchar()
É a função original de entrada de caractere dos sistemas baseados em UNIX.
getchar() armazena a entrada até que ENTER seja pressionada.
Ex.:

main()
{
char ch; ch=getchar();
printf("%c\n,ch);
}

Função putchar()
Escreve na tela o argumento de seu caractere na posição corrente.
Ex.:

main()
{
char ch;
printf("digite uma letra minúscula : ");
ch=getchar();
putchar(toupper(ch));
putchar('\n');
}

Há inúmeras outras funções de manipulação de char complementares às que foram vistas, como
isalpha(), isupper(), islower(), isdigit(), isespace(), toupper(), tolower().

Estruturas de Controle de Fluxo


Os comandos de controle de fluxo são a essência de qualquer linguagem, porque governam o fluxo
da execução do programa. São poderosos e ajudam a explicar a popularidade da linguagem. Podemos
dividir em três categorias. A primeira consiste em instruções condicionais if e switch. A segunda são os
comandos de controle de loop o while, for e o do-while. A terceira contém instruções de desvio
incondicional goto.

If
sintaxe:
if (condição)comando;
else comando;
Se a condição avaliar em verdadeiro (qualquer coisa menos 0), o computador executará o comando
ou o bloco, de outro modo, se a cláusula else existir, o computador executará o comando ou o bloco que
é seu objetivo.
Ex.:

162
1678859 E-book gerado especialmente para DANIEL CRISTIAN
main()
{
int a,b;
printf("digite dois números:");
scanf("%d%d",&a,&b);
if (b) printf("%d\n",a/b);
else printf("divisão por zero\n");
}

Ex.:

#include <stdlib.h>
#include <time.h>
main()
{
int num,segredo;
srand(time(NULL));
segredo=rand()/100;
printf("Qual e o numero: ");
scanf("%d",&num);
if (segredo==num)
{
printf("Acertou!");
printf("\nO numero e %d\n",segredo);
}
else if (segredo<num)
printf("Errado, muito alto!\n");
else printf("Errado, muito baixo!\n");
}

If-else-if
Uma variável é testada sucessivamente contra uma lista de variáveis inteiras ou de caracteres. Depois
de encontrar uma coincidência, o comando ou o bloco de comandos é executado.
Ex.:

#include <stdlib.h>
#include <time.h>
main()
{
int num,segredo;
srand(time(NULL));
segredo=rand()/100;
printf("Qual e o numero: ");
scanf("%d",&num);
if (segredo==num)
{
printf("Acertou!");
printf("\nO numero e %d\n",segredo);
}
else if (segredo<num)
printf("Errado, muito alto!\n");
else printf("Errado, muito baixo!\n");
}

Operador Ternário
Sintaxe:

condição?expressão1:expressão2
É uma maneira compacta de expressar if-else.

163
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Ex.:

main()
{
int x,y,max;
printf("Entre com dois números: ");
scanf(%d,%d,&x,&y); max=(x>y)?1:0;
printf("max= %d\n",max);
}

Switch
Sintaxe:

switch(variável)
{
case constante1:
sequência de comandos
break;
case constante2:
sequência de comandos
break;
default:
sequência de comandos
}
Uma variável é testada sucessivamente contra uma lista de variáveis inteiras ou de caracteres. Depois
de encontrar uma coincidência, o comando ou o bloco de comandos é executado.
Se nenhuma coincidência for encontrada o comando default será executado. O default é opcional. A
sequência de comandos é executada até que o comando break seja encontrado.
Ex.:

main()
{
char x; printf("1. inclusão\n");
printf("2. alteração\n");
printf("3. exclusão\n");
printf(" Digite sua opção:");
x=getchar();
switch(x)
{
case '1':
printf("escolheu inclusão\n");
break;
case '2':
printf("escolheu alteração\n");
break;
case '3':
printf("escolheu exclusão\n");
break;
default:
printf("opção inválida\n");
}
}

Loop for
Sintaxe:

for (inicialização;condição;incremento) comando;


O comando for é de alguma maneira encontrado em todas linguagens procedurais de programação.

164
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Em sua forma mais simples, a inicialização é um comando de atribuição que o compilador usa para
estabelecer a variável de controle do loop. A condição é uma expressão de relação que testa a variável
de controle do loop contra algum valor para determinar quando o loop terminará. O incremento define a
maneira como a variável de controle do loop será alterada cada vez que o computador repetir o loop.
Ex.:

main()
{
int x;for(x=1;x<100;x++)printf("%d\n",x);
}
Ex:
main()
{
int x,y;
for (x=0,y=0;x+y<100;++x,++y) printf("%d ",x+y);
}

Um uso interessante para o for é o loop infinito, como nenhuma das três definições são obrigatórias,
podemos deixar a condição em aberto.
Ex.:

main()
{
for(;;) printf("loop infinito\n");
}
Outra forma usual do for é o for aninhado, ou seja, um for dentro de outro.
Ex:
main()
{
int linha,coluna;
for(linha=1;linha<=24;linha++)
{
for(coluna=1;coluna<40;coluna++)
printf("-"); putchar('\n');
}
}

While
Sintaxe:

while(condição) comando;

Uma maneira possível de executar um laço é utilizando o comando while. Ele permite que o código
fique sendo executado numa mesma parte do programa de acordo com uma determinada condição.
- O comando pode ser vazio, simples ou bloco
- Ele é executado desde que a condição seja verdadeira
- Testa a condição antes de executar o laço
Ex.:

main()
{
char ch;
while(ch!='a') ch=getchar();
}

Do while
Sintaxe:

165
1678859 E-book gerado especialmente para DANIEL CRISTIAN
do
{
comando;
}
while(condição);

Também executa comandos repetitivos.


Ex.:
main()
{
char ch; printf("1. inclusão\n");
printf("2. alteração\n");
printf("3. exclusão\n");
printf(" Digite sua opção:");
do
{
ch=getchar(); switch(ch)
{
case '1': printf("escolheu inclusao\n");
break;
case '2': printf("escolheu alteracao\n");
break;
case '3': printf("escolheu exclusao\n");
break;
case '4': printf("sair\n");
}
}
while(ch!='1' && ch!='2' && ch!='3' && ch!='4');
}

Break
Quando o comando break é encontrado em qualquer lugar do corpo do for, ele causa seu término
imediato. O controle do programa passará então imediatamente para o código que segue o loop.
Ex.:

main()
{
char ch;
for(;;)
{
ch=getchar();
if (ch=='a') break;
}
}

Continue
Algumas vezes torna-se necessário "saltar" uma parte do programa, para isso utilizamos o "continue".
- Força a próxima iteração do loop.
- Pula o código que estiver em seguida.
Ex.:
main()
{
int x;
for(x=0;x<100;x++)
{
if(x%2)continue;
printf("%d\n",x);
}
}

166
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Matrizes
A matriz é um tipo de dado usado para representar uma certa quantidade de variáveis que são
referenciados pelo mesmo nome. Consiste em locações contíguas de memória. O endereço mais baixo
corresponde ao primeiro elemento.

Matriz Unidimensional
Sintaxe:

tipo nome[tamanho];
As matrizes tem 0 como índice do primeiro elemento, portanto sendo declarada uma matriz de inteiros
de 10 elementos, o índice varia de 0 a 9.
Ex.:

main()
{
int x[10];
int t;
for(t=0;t<10;t++)
{
x[t]=t*2;
printf("%d\n",x[t];
}
}

Ex:

main()
{
int notas[5],i,soma;
for(i=0;i<5;i++)
{
printf("Digite a nota do aluno %d: ",i);
scanf("%d",&notas[i]);
}
soma=0;
for(i=0;i<5;i++) soma=soma+notas[i];
printf("Media das notas: %d.",soma/5);
}

Matriz Multidimensional

Sintaxe:

tipo nome[tamanho][tamanho] ...;


Funciona como na matriz de uma dimensão (vetor), mas tem mais de um índice.
Ex.:
main()
{
int x[10][10];
int t,p=0;
for(t=0;t<10;t++,p++)
{
x[t][p]=t*p;
printf("%d\n",x[t][p]);
}
}

167
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Matrizes Estáticas
Os vetores de dados podem ser inicializados como os dados de tipos simples, mas somente como
variáveis globais. Quando for inicializar uma matriz local sua classe deve ser static.
Ex.:

main()
{
int i;
static int x[10]={0,1,2,3,4,5,6,7,8,9};
for(i=0;i<10;i++) printf("%d\n",x[i]);
}

Limites das Matrizes


A verificação de limites não é feita pela linguagem, nem mensagem de erros são enviadas, o programa
tem que testar os limites das matrizes.
Ex.:

main()
{
int erro[10],i;
for(i=0;i<100;i++)
{
erro[i]=1;
printf("%d\n",erro[i];
}
}

Manipulação de Strings
Em C não existe um tipo de dado string, no seu lugar é utilizado uma matriz de caracteres. Uma string
é uma matriz tipo char que termina com '\0'. Por essa razão uma string deve conter uma posição a mais
do que o número de caracteres que se deseja. Constantes strings são uma lista de caracteres que
aparecem entre aspas, não sendo necessário colocar o '\0', que é colocado pelo compilador.
Ex.:

main()
{
static re[]=“lagarto”;
puts(re); puts(&re[0]);
putchar('\n');
}

Função gets()
Sintaxe:

gets(nome_matriz);
É utilizada para leitura de uma string através do dispositivo padrão, até que o ENTER seja pressionado.
A função gets() não testa limites na matriz em que é chamada.
Ex.:
main()
{
char str[80];
gets(str);
printf("%s",str);
}

168
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Função puts()
Sintaxe:

puts(nome_do_vetor_de_caracteres);
Escreve o seu argumento no dispositivo padrão de saída (vídeo), coloca um '\n' no final. Reconhece
os códigos de barra invertida.
Ex.:
main()
{
puts("mensagem");
}

Função strcpy()
Sintaxe:

strcpy(destino,origem);
Copia o conteúdo de uma string.
Ex.:
main()
{
char str[80];
strcpy(str,"alo");
puts(str);
}

Função strcat()
Sintaxe:

strcat(string1,string2);
Concatena duas strings. Não verifica tamanho.
Ex.:
main()
{
char um[20],dois[10];
strcpy(um,"bom");
strcpy(dois," dia");
strcat(um,dois);
printf("%s\n",um);
}

Função strcmp()
Sintaxe:

strcmp(s1,s2);
Compara duas strings, se forem iguais devolve 0.
Ex.:
main()
{
char s[80];
printf("Digite a senha:");
gets(s);
if (strcmp(s,"laranja"))
printf("senha inválida\n");
else
printf("senha ok!\n") ;
}
Além das funções acima, há outras que podem ser consultadas no manual da linguagem, como strlen()
e atoi().

169
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Ponteiros
Sintaxe:

tipo *nomevar;
É uma variável que contém o endereço de outra variável. Os ponteiros são utilizados para alocação
dinâmica, podendo substituir matrizes com mais eficiência. Também fornecem a maneira pelas quais
funções podem modificar os argumentos chamados, como veremos no capítulo de funções.
Declarando Ponteiros
Se uma variável irá conter um ponteiro, então ela deve ser declarada como tal:
int x,*px; px=&x; /*a variável px aponta para x */
Se quisermos utilizar o conteúdo da variável para qual o ponteiro aponta: y=*px;
O que é a mesma coisa que:
y=x;

Manipulação de Ponteiros
Desde que os pointers são variáveis, eles podem ser manipulados como as variáveis podem. Se py é
um outro ponteiro para um inteiro então podemos fazer a declaração: py=px;
Ex.:

main()
{
int x,*px,*py; x=9;
px=&x; py=px;
printf("x= %d\n",x);
printf("&x= %d\n",&x);
printf("px= %d\n",px);
printf("*px= %d\n",*px);
printf("*px= %d\n",*px);
}

Expressões com Ponteiros


Os ponteiros podem aparecer em expressões, se px aponta para um inteiro x, então *px pode ser
utilizado em qualquer lugar que x seria.
O operador * tem maior precedência que as operações aritméticas, assim a expressão abaixo pega o
conteúdo do endereço que px aponta e soma 1 ao seu conteúdo.
y=*px+1;
No próximo caso somente o ponteiro será incrementado e o conteúdo da próxima posição da memória
será atribuído a y: y=*(px+1);
Os incrementos e decrementos dos endereços podem ser realizados com os operadores ++ e --, que
possuem procedência sobre o * e operações matemáticas e são avaliados da direita para a esquerda:
*px++; /* sob uma posição na memória */
*( px--); /* mesma coisa de *px-- */
No exemplo abaixo os parênteses são necessários, pois sem eles px seria incrementado em vez do
conteúdo que é apontado, porque os operadores * e ++ são avaliados da direita para esquerda. ( *px)++
/* equivale a x=x+1; ou *px+=1 */
Ex.:

main()
{
nt x,*px; x=1;
px=&x;
printf("x= %d\n",x);
printf("px= %u\n",px);
printf("*px+1= %d\n",*px+1);
printf("px= %u\n",px);
printf("*px= %d\n",*px);
printf("*px+=1= %d\n",*px+=1);
printf("px= %u\n",px);
printf("(*px)++= %d\n",(*px)++);

170
1678859 E-book gerado especialmente para DANIEL CRISTIAN
printf("px= %u\n",px);
printf("*(px++)= %d\n",*(px++));
printf("px= %u\n",px);
printf("*px++-= %d\n",*px++);
printf("px= %u\n",px);
}

Ponteiros para Ponteiros


Um ponteiro para um ponteiro é uma forma de indicação múltipla. Num ponteiro normal, o valor do
ponteiro é o valor do endereço da variável que contém o valor desejado. Nesse caso o primeiro ponteiro
contém o endereço do segundo, que aponta para a variável que contém o valor desejado.
float **balanço; balanço é um ponteiro para um ponteiro float.
Ex.:

main()
{
int x,*p,**q; x=10; p=&x;
q=&p;
printf("%d",**q);
}

Problemas com Ponteiros


O erro chamado de ponteiro perdido é um dos mais difíceis de se encontrar, pois a cada vez que a
operação com o ponteiro é utilizada, poderá estar sendo lido ou gravado em posições desconhecidas da
memória. Isso pode acarretar em sobreposições sobre áreas de dados ou mesmo área do programa na
memória.
int,*p; x=10; *p=x;
Estamos atribuindo o valor 10 a uma localização desconhecida de memória. A conseqüência desta
atribuição é imprevisível.

Ponteiros e Matrizes
Em C existe um grande relacionamento entre ponteiros e matrizes, sendo que eles podem ser tratados
da mesma maneira. As versões com ponteiros geralmente são mais rápidas.

Manipulando Matrizes Através de Ponteiros


Considerando a declaração da matriz int a[10];
Sendo pa um ponteiro para inteiro então:
pa=&a[0]; /*passa o endereço inicial do vetor a para o ponteiro pa */
pa=a; /* é a mesma coisa de pa=&a[0]; */ x=*pa; /*(passa o conteúdo de a[0] para x */

Se pa aponta para um elemento particular de um vetor a, então por definição pa+1 aponta para o
próximo elemento, e em geral pa-i aponta para i elementos antes de pa e pa+i para i elementos depois.
Se pa aponta para a[0] então: *( pa+1) aponta para a [1] pa+i é o endereço de a[i] e *(pa+i) é o
conteúdo.
É possível fazer cópia de caracteres utilizando matrizes e ponteiros:
Ex.: (versão matriz)

main()
{
int i=0; char t[10];
static char s[]="abobora";
while(t[i]=s[i])i++;
printf("%s\n",t);
}
Ex.: (versão ponteiro)

main()
{
char *ps,*pt,t[10],s[10];

171
1678859 E-book gerado especialmente para DANIEL CRISTIAN
strcpy(s,"abobora");
ps=s; pt=&t[0];
while(*ps)*pt++=*ps++;
printf("%s",t);
}

String de Ponteiros
Sendo um ponteiro para caracter char *texto;:, podemos atribuir uma constante string para texto, que
não é uma cópia de caracteres, somente ponteiros são envolvidos. Neste caso a string é armazenada
como parte da função em que aparecem, ou seja, como constante.
Char *texto="composto"; /* funciona como static char texto[]=“composto”; */

Ex.:

main()
{
char *al="conjunto";
char re[]=”simples”;
puts(al); puts(&re[0]); /* ou puts(re); */
for(;*al;al++) putchar(*al);
putchar('\n');
}

Matrizes de Ponteiros
A declaração de matrizes de ponteiros é semelhante a qualquer outro tipo de matrizes:
int *x[10];
Para atribuir o endereço de uma variável inteira chamada var ao terceiro elemento da matriz de
ponteiros: x[2]=&var;
Verificando o conteúdo de var:
*x[2]
As matrizes de ponteiros são tradicionalmente utilizadas para mensagens de erro, que são constantes:
char *erro[]={"arquivo não encontrado\n","erro de leitura\n"}; printf("%s",erro[0]); printf("%s",erro[1]);
Ex.:

main()
{
char *erro[2];
erro[0]="arquivo nao encontrado\n";
erro[1]="erro da leitura\n";
for(;*erro[0];)
printf("%c",*erro[0]++);

Funções
É uma unidade autônoma de código do programa é desenhada para cumprir uma tarefa particular.
Geralmente os programas em C consistem em várias pequenas funções. A declaração do tipo da função
é obrigatória no C do UNIX. Os parâmetros de recepção de valores devem ser separados por vírgulas.
Sintaxe:

tipo nome(parâmetros);
{ comandos }

Função sem Retorno


Quando uma função não retorna um valor para a função que a chamou ela é declarada como void.
Ex.:

void inverso();
main()
{
char *vet="abcde";

172
1678859 E-book gerado especialmente para DANIEL CRISTIAN
inverso(vet);
}
void inverso(s) char *s;
{ int t=0; for(;*s;s++,t++);
s--;
for(;t--;)printf("%c",*s--); putchar('\n');
}

Função com Retorno


O Tipo de retorno da função deve ser declarado.
Ex.:

int elevado();
main()
{
int b,e;
printf("Digite a base e expoente x,y : ");
scanf("%d,%d",&b,&e);
printf("valor=%d\n",elevado(b,e));
}

int elevado(base,expoente) int base,expoente;


{
int i;
if (expoente<0) return;
i=1;
for(;expoente;expoente--)i=base*i;
return i;
}

Parâmetros Formais
Quando uma função utiliza argumentos, então ela deve declarar as variáveis que aceitaram os valores
dos argumentos, sendo essas variáveis os parâmetros formais.
Ex.:

int pertence(string,caracter) /* pertence(char *string,char caracter) */ char *string,caracter;


{
while (*string) if (*string==caracter) return 1;
else string++;
return 0;
}

Chamada por Valor


O valor de um argumento é copiado para o parâmetro formal da função, portanto as alterações no
processamento não alteram as variáveis.
Ex.:

int sqr();
main()
{
int t=10;
printf("%d %d",sqr(t),t);
}

int sqr(x)
int x;
{
x=x*x; return(x)
}

173
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Chamada por Referência
Permite a alteração do valor de uma variável. Para isso é necessário a passagem do endereço do
argumento para a função.
Ex.:

void troca();
main()
{
int x=10,y=20;
troca(&x,&y);
printf("x=%d y=%d\n",x,y);
}
void troca(a,b)
int *a,*b;
{
int temp;
temp=*a; *a=*b;
*b=temp;
}

Classe de Variáveis
Uma função pode chamar outras funções, mas o código que compreende o corpo de uma função (bloco
entre {}) está escondido do resto do programa, ele não pode afetar nem ser afetado por outras partes do
programa, a não ser que o código use variáveis globais. Existem três classes básicas de variáveis: locais,
estáticas e globais.

- Variáveis Locais
As variáveis que são declaradas dentro de uma função são chamadas de locais. Na realidade toda
variável declarada entre um bloco { } podem ser referenciadas apenas dentro deste bloco. Elas existem
apenas durante a execução do bloco de código no qual estão declaradas. O armazenamento de variáveis
locais por default é na pilha, assim sendo uma região dinâmica.
Ex.:

void linha;
main()
{
int tamanho;
printf("Digite o tamanho: ");
scanf("%d",&tamanho);
linha(tamanho);
}
void linha(x)
int x;
{
int i;
for(i=0;i<=x;i++)putchar(95);
/* A variável i na função linha não é reconhecida pela função main.*/
}

- Variáveis Globais
São conhecidas por todo programa e podem ser usadas em qualquer parte do código. Permanecem
com seu valor durante toda execução do programa. Deve ser declarada fora de qualquer função e até
mesmo antes da declaração da função main.
Fica numa região fixa da memória própria para esse fim.
Ex.:

void func1(),func2();
int cont;
main()

174
1678859 E-book gerado especialmente para DANIEL CRISTIAN
{
cont=100;
func1();
}
void func1()
{
int temp;
temp=cont;
func2();
printf("cont é = %d",cont);
}
void func2()
{
int cont;
for(cont=1;cont<10;cont++) printf(".");
}

- Variáveis Estáticas
Funcionam de forma parecida com as variáveis globais, conservando o valor durante a execução de
diferentes funções do programa. No entanto só são reconhecidas na função onde estão declaradas. São
muitos utilizadas para inicializar vetores.
Ex.:

main()
{
int i;
static int x[10]={0,1,2,3,4,5,6,7,8,9};
for(i=0;i<10;i++) printf("%d\n",x[i]);
}

Funções com Matrizes

- Passando Parâmetros Formais


É necessário passar somente o endereço e não uma cópia da matriz.
Ex.:

void mostra();
main()
{
int t[10],i;
for(i=0;i<10;i++)t[i]=i;
mostra(t);
}
void mostra(num)
int num[]; /* ou declarar int *num; */
{
int i;
for(i=0;i<10;i++)printf("%d",num[i]);
}

- Alterando o Valores da Matriz


Ex.:

void maiusc();
main()
{
char s[80];
gets(s);
maiusc(s);

175
1678859 E-book gerado especialmente para DANIEL CRISTIAN
}
void maiusc(string)
char *string;
{
register int t;
for(t=0;string[t];t++)
{
string[t]=toupper(string[t]);
printf("%c",string[t]);
}
}

Argumentos da Linha de Comando


No ambiente C existe uma maneira de passar argumentos através da linha de comandos para um
programa quando ele inicia. O primeiro argumento (argc) é a quantidade de argumentos que foram
passados quando o programa foi chamado; o segundo argumento (argv) é um ponteiro de vetores de
caracteres que contém os argumentos, um para cada string.
Por convenção argv[0] é o nome do programa que foi chamado, portanto argc é pelo menos 1. Cada
argumento da linha de comando deve ser separado por um espaço ou tab.
Ex.:

main(argc,argv)
int argc;
char *argv[];
{
if (argc!=2) {
printf("falta digitar o nome\n"); exit(0);
} printf("alo %s",argv[1]); }
Ex: main(argc,argv) int argc; char *argv[];
{ int disp,cont; if (argc<2)
{
printf("falta digitar o valor para contagem\n"); exit(0);
}
if (argc==3&&!strcmp(argv[2],"display")) disp=1;
else disp=0;
for(cont=atoi(argv[1]);cont;--cont) if(disp)printf("%d",cont);
printf("%c",7);}

Estruturas
Ao manusearmos dados muitas vezes deparamos com informações que não são fáceis de armazenar
em variáveis escalares como são os tipos inteiros e pontos flutuantes, mas na verdade são conjuntos de
coisas. Este tipo de dados são compostos com vários dos tipos básicos do C. As estruturas permitem
uma organização dos dados dividida em campos e registros.
Ex.:

struct lapis { int dureza; char fabricante;


int numero;
};
main()
{
int i; struct lapis p[3]; p[0].dureza=2; p[0].fabricante='F'; p[0].numero=482; p[1].dureza=0;
p[1].fabricante='G'; p[1].numero=33; p[2].dureza=3; p[2].fabricante='E'; p[2].numero=107;
printf("Dureza Fabricante Numero\n");
for(i=0;i<3;i+ +)
printf("%d\t%c\t\t%d\n",p[i].dureza,p[i].fabricante,p[i].numero);
}

Como ocorre com as variáveis, as estruturas também podem ser referenciadas por ponteiros. Assim,
definindo-se por exemplo o ponteiro *p para a estrutura acima (lapis), pode-se usar a sintaxe (*p).dureza.

176
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Porém, para referenciar o ponteiro há ainda outra sintaxe, através do operador -> , como por exemplo, p-
>dureza.

Noções de Alocação Dinâmica


Há duas maneiras de armazenar variáveis na memória do computador. Primeiro por variáveis globais
e static locais, segundo através de alocação dinâmica, quando o C armazena a informação em uma área
de memória livre, de acordo com a necessidade. No caso do C standart, a alocação dinâmica fica
disponível com a inclusão de stdio.h.
Ex..

#include <stdio> main()


{ int *p, t;
p=(int *) malloc(40*sizeof(int)); if (!p)
printf("memoria insuficiente\n"); else
{
for(t=0;t<40;++t) *(p+t)=t; for(t=0;t<40;++t) printf("%d ",*(p+t)); free(p);
}}

Ex.:

#include <stdio> main()


{ int i,quant; float max,min,*p;
printf ("quantidade de numeros:\n");
scanf("%d",&quant);
if (!(p=(float*)malloc((quant+1)*sizeof(float))))
{
printf("sem memoria\n");
exit(1);
}
printf("digite %d numeros:\n",quant);
for (i=1;i<=quant;i++)
{
scanf("%f",(p+i));
}
max=*(p+1); for (i=2;i<=quant;i++)
{
if (*(p+i)>=max)
max=*(p+i);
}
printf("maior e :%f\n",max); free(p); }

Noções de Manipulação de Arquivos


Para tratar de arquivos a linguagem C fornece um nível de abstração entre o programador e o
dispositivo que estiver sendo usado. Esta abstração é chamada fila de bytes e o dispositivo normalmente
é o arquivo. Existe um sistema bufferizado de acesso ao arquivo, onde um ponteiro de arquivo define
vários aspectos do arquivo, como nome, status e posição corrente, além de ter a fila associada a ele.
Ex.:

#include <stdio.h>
main ()
{
FILE *fp; char ch;int nu,*pn; pn=&nu;
fp=fopen("teste.dat","w");
printf("Entre com os numeros para gravar e 0 para sair: "); scanf("%d",&nu);
while(nu)
{
fprintf(fp,"%d ",nu);
scanf("%d",&nu);
} fclose(fp);

177
1678859 E-book gerado especialmente para DANIEL CRISTIAN
fp=fopen("teste.dat","r"); while(!feof(fp))
{
fscanf(fp,"%d ",&nu); printf("%d",nu);
} }

Linguagem Algorítmica
Linguagem Algorítmica consiste em uma pseudolinguagem de programação, que tem como função
facilitar o entendimento de uma linguagem de programação qualquer, nesse caso os comandos de
programação são escritos na linguagem nacional, em nosso caso, em português:
Ex.:
Para resolução deste exemplo será utilizado o Problema 3, do tópico Descrição Narrativa:

Variáveis, Constantes, Operadores Lógicos e Tipos De Dados


Variável – Em programação de computadores, variáveis são espaços que ficam reservados
normalmente na memória RAM (memória física) do computador a fim de armazenar valores ou
expressões e seus valores podem variar durante a execução do programa.
Exemplo:

Variáveis

Lucro = ValorVenda ValorVenda


-

Variável Conteúdo
Nome “Luis Alexandre Boaygo dos Santos”
Idade 32
Peso 80,00

Constantes: o valor sempre é fixo, não é modificado durante a execução do programa.


Exemplo:

𝑣𝑎𝑙𝑜𝑟1 + 𝑣𝑎𝑙𝑜𝑟2
𝑡𝑜𝑡𝑎𝑙 =
2

Constante

Tipos de Variáveis
As variáveis possuem quatro tipos básicos: numérico, caractere, alfanumérico e lógico.

178
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Numérico: especial para armazenar números inteiros e reais, também são utilizadas para cálculos
matemáticos;
- Caracteres: armazena um conjunto de caracteres não literais, não é possível realizar nenhum tipo de
cálculo com esse tipo de variável, sua utilização ideal é voltada ao armazenamento de textos, exemplo
um nome, um endereço, etc.
- Lógico: utilizada para testes e armazenamentos lógicos, assumem valores verdadeiro e falso.
- Alfanumérico: voltada ao armazenamento de letras e/ou números. Em determinados momentos
conter somente dados numéricos ou somente literais. Se utilizada para o armazenamento de números,
não poderá ser utilizada para operações matemáticas.

Operadores
Utilizados para a resolução de cálculos aritméticos, comparações de valores (chamados de operadores
relacionais) e testes lógicos, estes são descritos da seguinte forma:

Operadores Relacionais Operadores Aritméticos


Sinal Função Sinal Função
< Menor + Adição
> Maior - Subtração
<> Diferente * Multiplicação
<= Menor ou igual / Divisão
>= Maior ou igual ** Exponenciação
= Igual
E Operador E
OU Operador OU
Not Negação

Exemplos de operadores aritméticos:

subTotal := valoUn * Qtde

2+3*8

Total := 4 *( 2 / valor ) + 3
Exemplos de utilização de operadores relacionais:

Se ( num1 < num2) entao

enquanto ( i <= 10) faca


Utilização dos operadores AND, OR e NOT:

Valor 1 Operador Valor 2 Resultado


Verdadeiro AND Verdadeiro Verdadeiro
Verdadeiro AND Falso Falso
Falso AND Verdadeiro Falso
Falso AND Falso Falso
Verdadeiro OR Verdadeiro Verdadeiro
Verdadeiro OR Falso Verdadeiro
Falso OR Verdadeiro Verdadeiro
Falso OR Falso Falso
Verdadeiro NOT Falso
Falso NOT Verdadeiro

Imagine a seguinte situação, A = 6, B = 9, C = 2

Expressões Resultado
A=B AND B>C Falso
Como A = 6, B = 9 e C = 2 a expressão assume
os seguintes valores e verificações:
6=9e9>2

179
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Feita a verificação, o resultado só será verdadeiro
se ambas as condições forem verdadeiras, logo 6
é diferente de 2, portanto, o resultado é falso.

A <> B AND B<C Falso


Nessa situação, a primeira expressão é
verdadeira, pois, 6 é diferente de 9, já a segunda
é falsa porque, 9 não é menor que 2, portanto a
resposta é Falsa.

A>B NOT Verdadeiro


A primeira vista a expressão é falsa, pois, 6 é
menor que 9, mas como existe o operador de
negação NOT que faz a inversão da resposta, por
tal motivo a resposta é verdadeira.

A<B OR B>A Verdadeiro


Para essa expressão retornar valor verdadeiro,
apenas uma das expressões deve ser verdadeira
nesse caso 6 é menor que 8, portanto retornou
verdadeiro.

Estruturas de Decisão e Repetição


São estruturas utilizadas para tomadas de decisão de acordo com uma comparação feita na estrutura
do algoritmo, assumindo respostas diferentes. A principal estruturas de comparação das linguagens é a
estrutura Se-Senão (If – Else), vejamos um exemplo em linguagem algorítmica e em linguagem de
programação.

Linguagem Algorítmica Linguagem de Programação

Em ambos os casos acima, primeiro é calculada a média, a seguir é verificada se a média é maior que
6, caso seja, é retornada a mensagem afirmando que o aluno foi aprovado, caso não seja, a mensagem
retornada é de reprova do aluno.
As estruturas de repetição servem para repetir comandos até que a condição proposta seja satisfeita,
as principais são Enquanto (While) e Para (For), vejamos alguns exemplos:
O algoritmo abaixo foi desenvolvido em Linguagem de Programação Pascal a fim de exibir uma
tabuada escolhida pelo usuário, note que assim que o programa é executado a variável M recebe o valor
1, essa é chamada de variável de controle, então ao usuário digitar o valor da variável T solicitado pelo
programa, é feita uma verificação através da estrutura While até que a variável tenha o valor igual a 10,
esse valor é alterado toda vez que a estrutura passa pelo comando “M := M + 1”, ou seja, inicialmente M
possui valor 1, somando mais 1 fica 2 e assim por diante, até que ele tenha valor 10.

180
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Linguagens de Programação

As Linguagens de programação (LP) são os meio de comunicação entre usuário e computador e são
utilizadas para resolução de problemas computacionais, ou seja, são utilizadas para a criação de
programas de computador (software). Ao longo do tempo essas linguagem estão passando por
transformações, portanto, foram classificadas em 5 gerações, sendo:

Primeira Geração: Linguagem de Máquina


Esta é uma linguagem de Instrução, isso quer dizer que, utiliza como suporte programação binária
operando diretamente endereço de registradores ou memória, então existe comunicação direta com o
hardware.

Segunda Geração: Linguagens Simbólicas (Assembly)


Desenvolvida com a finalidade de minimizar as dificuldades encontradas na programação em notação
binária.
A partir dessa geração é possível que os usuários operem utilizando mnemônicos (palavras reservadas
da linguagem de programação), sistemas de grupos de códigos operacionais semelhantes recebem
nomes que lembram suas funções.
Terceira Geração: Linguagens Orientadas ao Usuário (LOU)
São linguagens procedimentais (utilizam sequencias de comandos), tiveram início década de 60 como
FORTRAN, ALGOL, COBOL, PL/I, ADA e algumas perduram até os dias de hoje, como é o caso da
Linguagem Pascal. Essas linguagens revolucionaram o universo do desenvolvimento de software, pois,
através delas os softwares são capazes de utilizar instruções de entrada e saída efetuar cálculos
aritméticos e/ou lógicos e realizar controle de fluxo de execução.

Quarta Geração: Linguagens Orientadas à Aplicações


Seu objetivo é facilitar a programação de computadores proporcionando a usuários finais a resolvendo
problemas que antes só eram possíveis a programadores de computador.
Exemplo: Geradores de relatórios, preenchimento de formulários, Excel, Linguagens SQL.

Quinta Geração: Linguagens de Conhecimento


São utilizadas principalmente na área de inteligência artificial, a partir desse geração é possível que
os softwares autocriem bases de conhecimento obtidas a partir de especialistas.

Compilação de Programas

Tradutores
São programas que traduzem o código fonte criado em uma linguagem de programação para um
programa objeto equivalente escrito em outra linguagem (denominada linguagem objeto).
Compiladores – Convertem programas escritos em linguagem de alto nível (linguagens de
programação) para programas equivalentes em linguagem simbólica ou de máquina, normalmente este
é um arquivo executável.
Interpretadores – São programas de computador que fazem a leitura de um código fonte de uma
linguagem de programação interpretada e o converte em código executável. Em certos casos, o
interpretador lê linha-por-linha e converte em código objeto (ou bytecode) à medida que vai executando

181
1678859 E-book gerado especialmente para DANIEL CRISTIAN
o programa e, em outros casos, converte o código fonte por inteiro e depois o executa. Uma
particularidade do interpretador é que ele trabalha utilizando uma máquina virtual(MV).

Estrutura de Dados
São utilizados para manipulação e organização de dados. A organização e os métodos para manipular
essa estrutura possibilitam a diminuição do espaço ocupado pela memória RAM, além de tornar o código-
fonte do programa mais simplificado.
Uma pilha é uma das várias estruturas de dados que admitem remoção de elementos e inserção de
novos elementos estas organizações são feitas através de pilhas e filas.
As filas e pilhas são estruturas usualmente implementadas através de listas, restringindo a política de
manipulação dos elementos da lista.
Uma fila estabelece uma política chamada FIFO -- first in, first out (primeiro a entrar e primeiro a sair).
Em outras palavras, a ordem estabelecida na lista é a ordem de inserção. No momento de retirar um nó
da lista, o nó mais antigo (o primeiro que entrou) é o primeiro a ser retirado.

Depuração (Debugging)

Programar é um processo complicado e, como é feito por seres humanos, frequentemente conduz a
erros45. Por mero capricho, erros em programas são chamados de bugs e o processo de encontrá-los e
corrigi-los é chamado de depuração (debugging).
Três tipos de erros podem acontecer em um programa: erros de sintaxe, erros em tempo de execução
(runtime errors) e erros de semântica (também chamados de erros de lógica). Distinguir os três tipos ajuda
a localizá-los mais rápido:

Erros de Sintaxe
O interpretador do Python só executa um programa se ele estiver sintaticamente correto. Caso
contrário, o processo falha e retorna uma mensagem de erro. O termo sintaxe refere-se à estrutura de um
programa e às regras sobre esta estrutura. Por exemplo, em português, uma frase deve começar com
uma letra maiúscula e terminar com um ponto.

esta frase contém um erro de sintaxe. Assim como esta


Para a maioria dos leitores, uns errinhos de sintaxe não chegam a ser um problema significativo e é
por isso que conseguimos ler a poesia moderna de e.e. cummings sem cuspir mensagens de erro. Python
não é tão indulgente. Se o seu programa tiver um único erro de sintaxe em algum lugar, o interpretador
Python vai exibir uma mensagem de erro e vai terminar, e o programa não vai rodar. Durante as primeiras
semanas da sua carreira como programador, você provavelmente perderá um bocado de tempo
procurando erros de sintaxe. Conforme for ganhando experiência, entretanto, cometerá menos erros e os
localizará mais rápido.

Erros em Tempo de Execução (Runtime Errors)


O segundo tipo de erro é o de runtime, ou erro em tempo de execução, assim chamado porque só
aparece quando você roda o programa. Esses erros também são conhecidos como exceções, porque
normalmente indicam que alguma coisa excepcional (e ruim) aconteceu.
Erros de runtime são raros nos programas simples que você vai ver nos primeiros capítulos. Então, vai
demorar um pouco até você se deparar com um erro desse tipo.

Erros de Semântica (Ou de Lógica)


O terceiro tipo de erro é o erro de semântica (mais comumente chamado de erro de lógica). Mesmo
que o seu programa tenha um erro de semântica, ele vai rodar com sucesso, no sentido de que o
computador não vai gerar nenhuma mensagem de erro. Só que o programa não vai fazer a coisa certa,
mas sim alguma outra coisa. Especificamente, aquilo que você tiver dito para ele fazer (o computador
trabalha assim: seguindo ordens).
O problema é que o programa que você escreveu não é aquele que você queria escrever. O significado
do programa (sua semântica ou lógica) está errado. Identificar erros semânticos pode ser complicado,
porque requer que você trabalhe de trás para frente, olhando a saída do programa e tentando imaginar o
que ele está fazendo.

45
https://aprendacompy.readthedocs.io/pt/latest/capitulo_01.html#o-que-e-depuracao-debugging

182
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Depuração Experimental (Debugging)
Uma das habilidades mais importantes que você vai adquirir é a de depurar. Embora possa ser
frustrante, depurar é uma das partes intelectualmente mais ricas, desafiadoras e interessantes da
programação.
De certa maneira, a depuração é como um trabalho de detetive. Você se depara com pistas, e tem que
deduzir os processos e eventos que levaram aos resultados que aparecem.
Depurar também é como uma ciência experimental. Uma vez que você tem uma ideia do que está
errado, você modifica o seu programa e tenta de novo. Se a sua hipótese estiver correta, então você
conseguiu prever o resultado da modificação e ficou um passo mais perto de um programa que funciona.
Se a sua hipótese estiver errada, você tem que tentar uma nova.
Para algumas pessoas, programação e depuração são a mesma coisa. Ou seja, programar é o
processo de depurar um programa gradualmente, até que ele faça o que você quer. A ideia é começar
com um programa que faça alguma coisa e ir fazendo pequenas modificações, depurando-as conforme
avança, de modo que você tenha sempre um programa que funciona.
Por exemplo, o Linux é um sistema operacional que contém milhares de linhas de código, mas
começou como um programa simples, que Linus Torvalds usou para explorar o chip Intel 80386. De
acordo com Larry Greenfield, um dos primeiros projetos de Linus Torvalds foi um programa que deveria
alternar entre imprimir AAAA e BBBB. Isso depois evoluiu para o Linux. (The Linux User’s Guide Versão
Beta 1)

Orientação a Objetos

A orientação a objetos (O.O.) é um padrão de desenvolvimento de software utilizado em várias


linguagens de programação como por exemplo C, Java, PHP, etc. Que são voltadas a utilizar estes
métodos para diminuir a diferença semântica entre a realidade e o modelo Para entendermos o
funcionamento da O.O. é necessário conhecermos as bases que sustentam essas técnicas, a seguir veja
algumas características:
Classe é algo generalizado, ou seja, uma classe define o comportamento dos objetos através de seus
métodos (ações de uma classe), Exemplo de classe: Cliente.
Uma classe é dividida um três partes, nome, atributos e métodos.
Vejamos um exemplo de ilustração de uma classe na UML:

Subclasse é uma nova classe que herda características (atributos e/ou métodos) de sua classe pai.
Objeto – É algo do mundo real, que proporciona vida a classe, enquanto Aluno é uma Classe Luís
Alexandre é um Objeto dessa classe.
Método definem as habilidades dos objetos. Bidu é uma instância da classe Cachorro, portanto tem
habilidade para latir, implementada através do método de um latido. Um método em uma classe é apenas
uma definição. A ação só ocorre quando o método é invocado através do objeto, no caso Bidu. Dentro do
programa, a utilização de um método deve afetar apenas um objeto em particular; Todos os cachorros
podem latir, mas você quer que apenas Bidu dê o latido. Normalmente, uma classe possui diversos
métodos, que no caso da classe Cachorro poderiam ser sente, coma e morda.
Herança (ou generalização) é o mecanismo pelo qual uma classe (subclasse) pode estender outra
classe (superclasse), aproveitando seus comportamentos (métodos) e variáveis possíveis (atributos). Um
exemplo de herança: Mamífero é superclasse de Humano. Ou seja, um Humano é um mamífero. Há
herança múltipla quando uma subclasse possui mais de uma superclasse. Essa relação é normalmente
chamada de relação "é um".
Polimorfismo consiste em quatro propriedades que a linguagem pode ter (atente para o fato de que
nem toda linguagem orientada a objeto tem implementado todos os tipos de polimorfismo):

183
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Universal:Inclusão: um ponteiro para classe mãe pode apontar para uma instância de uma classe filha
(exemplo em Java: "List lista = new LinkedList();" (tipo de polimorfismo mais básico que existe)
Paramétrico: se restringe ao uso de templates (C++, por exemplo) e generics (Java/C♯)
Ad-Hoc: Sobrecarga: duas funções/métodos com o mesmo nome mas assinaturas diferentes
Coerção: a linguagem que faz as conversões implicitamente (como por exemplo atribuir um int a um
float em C++, isto é aceito mesmo sendo tipos diferentes pois a conversão é feita implicitamente)

Princípios de Resolução de Problemas46

Primeiramente, é importante entender e compreender a palavra "problema". Pode-se dizer que


problema é uma proposta duvidosa, que pode ter numerosas soluções, ou questão não solvida e que é
objeto de discussão, segundo definição encontrada no dicionário Aurélio.
Do ponto de vista desta obra, problema é uma questão que foge a uma determinada regra, ou melhor,
é o desvio de um percurso, o qual impede de atingir um determinado objetivo com eficiência e eficácia.
Diferentes das diagramações clássicas, que não fornecem grandes subsídios para análise, os
diagramas de blocos são realmente o melhor instrumento para avaliação do problema do fluxo de
informações de um dado sistema. Por esse motivo deve- -se resolver um problema de lógica
(principalmente se for da área de processamento eletrônico de dados) usando um procedimento de
desenvolvimento.
Para desenvolver um diagrama correto, deve-se considerar como procedimentos prioritários os itens
seguintes:
- Os diagramas devem ser feitos e quebrados em vários níveis. Os primeiros devem conter apenas as
ideias gerais, deixando para as etapas posteriores os detalhamentos necessários;
- Para o desenvolvimento correto de um fluxograma, sempre que possível, deve ser desenvolvido de
cima para baixo e da esquerda para direita;
- É incorreto e "proibido" ocorrer cruzamento das linhas de fluxo de dados.
Tome como exemplo uma escola qualquer, cujo cálculo da média é realizado com as quatro notas
bimestrais que determinam a aprovação ou reprovação dos seus alunos. Considere ainda que o valor da
média deve ser maior ou igual a 7 para que haja aprovação.

Diagrama de bloco para o cálculo da média escolar.

A segunda etapa apresenta um detalhamento no que se refere à entrada e saída, ou seja, deve-se
entrar com as quatro notas bimestrais para se obter, como resultado, o cálculo da média e assim definir
a aprovação ou reprovação do aluno. A figura abaixo apresenta o diagrama de blocos com mais detalhes.

46
MANZANO, JA OLIVEIRA, and J. F. Algoritmos. "Lógica para Desenvolvimento de Programação de Computadores." São Paulo: Érica (2000).

184
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A terceira etapa consiste em trabalhar o termo "determinar a aprovação". Para ser possível determinar
algo, é necessário estabelecer uma condição. Assim sendo, uma condição envolve uma decisão a ser
tomada segundo um determinado resultado. No caso, a média. Desta forma, a condição de aprovação:
média maior ou igual a 7 (sete), deve ser considerada no algoritmo. Com isso, inclui-se este bloco de
decisão, como mostra a figura abaixo.

Muitas vezes é preferível construir o diagrama de blocos trabalhando com variáveis.

185
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplo da utilização de variáveis

Particularidades entre Lógicas

As representações gráficas de um diagrama de blocos podem ser feitas de várias maneiras e


possuírem estruturas diferenciadas, porém isto não impede que a maneira de solucionar o problema seja
eficiente. Segundo Verzello nos diz em uma de suas obras, assim como um arquiteto desenha e escreve
especificações para descrever como uma tarefa (por exemplo, construção de um edifício) deverá ser
efetuada e o engenheiro do projeto desenvolve um esquema detalhado das atividades de construção, um
especialista em informação desenvolve um plano, que será comunicado a outros, de como o problema
de processamento de dados deve ser resolvido.
Para auxiliarmos na sua compreensão, mostraremos como estes conceitos de estruturas, bem como
as particularidades de conexões e dos procedimentos entre o método lógico, encaixam-se ou não para
resolução dos problemas de processamento de dados. A seguir, são apresentados alguns tipos de
procedimentos individualmente.

Linear
A técnica lógica linear é conhecida como um modelo tradicional de desenvolvimento e resolução de
um problema. Não está ligada a regras de hierarquia ou de estruturas de linguagens específicas de
programação de computadores. Devemos entender que este tipo de procedimento está voltado à técnica
matemática, a qual permite determinar a atribuição de recursos limitados, utilizando uma coleção de
elementos organizados ou ordenados por uma só propriedade, de tal forma que cada um deles seja
executado passo a passo de cima para baixo, em que tenha um só "predecessor" e um só "sucessor". A
figura abaixo apresenta um exemplo deste tipo de lógica.

Estruturada
A técnica da lógica estruturada é a mais usada pelos profissionais de processamento eletrônico de
dados. Possui características e padrões particulares, os quais diferem dos modelos das linguagens
elaboradas por seus fabricantes. Tem como pontos fortes para elaboração futura de um programa,
produzi-lo com alta qualidade e baixo custo.
A sequência, a seleção e a iteração são as três estruturas básicas para a construção do diagrama de
blocos. A figura abaixo seguinte apresenta um exemplo do tipo de lógica estruturada.

186
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Modular
A técnica da lógica modular deve ser elaborada como uma estrutura de partes independentes,
denominada de módulos, cujo procedimento é controlado por um conjunto de regras. Segundo James
Martin, suas metas são as seguintes:
- Decompor um diagrama em partes independentes;
- Dividir um problema complexo em problemas menores e mais simples;
- Verificar a correção de um módulo de blocos, independentemente de sua utilização como uma
unidade em um processo maior.
A modularização deve ser desenvolvida, se possível, em diferentes níveis. Poderá ser utilizada para
separar um problema em sistemas, um sistema em programas e um programa em módulos. A figura
abaixo apresenta um exemplo do tipo de lógica modular.

Português Estruturado
Como foi visto até agora, o diagrama de blocos é a primeira forma de notação gráfica, mas existe outra,
que é uma técnica narrativa denominada pseudocódigo, também conhecida como português estruturado
ou chamada por alguns de portugol.
Esta técnica de algoritmização é baseada em uma PDL - Program Design Language (Linguagem de
Projeto de Programação). Nesta obra, estamos apresentando-a em português. A forma original de escrita
é conhecida como inglês estruturado, muito parecida com a notação da linguagem PASCAL. A PDL (neste
caso, o português estruturado) é usada como referência genérica para uma linguagem de projeto de
programação, tendo como finalidade mostrar uma notação para elaboração de algoritmos, os quais serão
utilizados na definição, criação e desenvolvimento de uma linguagem computacional (Clipper, C, Pascal,
Delphi, Visual-Objects) e sua documentação. Abaixo, é apresentado um exemplo deste tipo de algoritmo.

programa MÉDIA
var
RESULTADO : caractere
N1, N2, N3, N4 : real
SOMA, MÉDIA : real
início

187
1678859 E-book gerado especialmente para DANIEL CRISTIAN
leia N1, N2, N3, N4
SOMA <- N1 + N2 + N3 + N4
MÉDIA <- SOMA / 4
se (MÉDIA >= 7) então
RESULTADO <— "Aprovado"
senão
RESULTADO <- "Reprovado"
fim_se
escreva "Nota 1 : ", N1
escreva "Nota 2 : ", N2
escreva "Nota 3 : ", N3
escreva "Nota 4: ", N4
escreva "Soma: “, SOMA
escreva "Média : ", MÉDIA
escreva "Resultado: ", RESULTADO
fim

A diferença entre uma linguagem de programação de alto nível utilizada em computação e uma PDL
é que esta (seja escrita em português, inglês ou qualquer outro idioma) não pode ser compilada em um
computador (por enquanto). Porém, existem "Processadores de PDL" que possibilitam traduzir essa
linguagem numa representação gráfica de projeto, fornecendo uma variedade de informações, como:
tabelas de referência cruzada, mapas de aninhamento, índice operacional do projeto, entre tantas outras.

Tipos de Dados47

Existem três tipos básicos de dados que iremos manipular nos algoritmos que iremos criar:
- Dados numéricos
- Dados literais ou alfanuméricos
- Dados lógicos

Dados Numéricos
O conjunto de dados mais comuns é o de números naturais, que é representado por N. Este conjunto
é definido como
N = {0,1,2,3,...}

Este conjunto de números é usado quando queremos falar sobre o número de amigos que temos ou
quantos CDs musicais temos na nossa coleção. Embora seja fácil imaginar que um pastor de ovelhas há
3000 anos atrás pudesse usar este conjunto com facilidade, é bom lembrar que o conceito do número
zero é difícil de ser entendido. O conjunto dos números naturais usado pelos primeiros seres humanos
não incluía o zero. Os pastores sabiam que se 20 ovelhas tinham ido para os campos, eles tinha de
esperar pelas mesma 20 ovelhas na volta à noite. Agora se não havia ovelhas para que precisaríamos
contar, somar ou subtrair? A natureza tem horror ao vácuo e o nada é um conceito complicado para os
seres humanos. O número zero é uma invenção dos matemáticos hindus e é recente, sendo introduzido
em XXXX aproximadamente.
O conjunto dos números naturais é subconjunto dos conjunto dos números inteiros que é definido como
Z = {...,-3,-2,-1,0,+1,+2,+3,...}

Aqui estamos falando de conceitos mais complicados que hoje em dia já fazem parte do nosso dia a
dia. Estamos no domínio dos números relativos, que incluem os números positivos, o zero e os números
negativos. A mente humana teve de atingir graus de abstração maiores para imaginar operações que
incluíam números negativos. Hoje em dia não é necessário ser um matemático pós-graduado para
trabalhar facilmente com este tipo de números. Todos nós somos capazes de perceber que uma
temperatura de -3 graus centígrados e mais fria do que +3 graus. Quem não fica preocupado quando
sabe que sua conta corrente no banco está com saldo de -300 reais. Rapidamente iremos tentar
transformar este número negativo em número positivo, e o que mais importante sabemos que precisamos
de pelo menos 300 reais para sair do vermelho.

47
http://equipe.nce.ufrj.br/adriano/algoritmos/apostila/tipos.htm

188
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O próximo conjunto na nossa curta viagem pelos domínios dos números é o conjunto dos números
fracionários, que é representado por Q. Este conjunto é composto por todos os números que podem ser
escritos como uma fração da forma p/q onde p e q pertencem ao conjunto dos números inteiros. Este
conjunto pode ser definido como:
Q = {p/q | p, q pertencem a Z}

Continuando nesta excursão vamos para o conjunto dos números reais (R) que é a união do conjunto
dos números fracionários e o dos números irracionais. Números irracionais não são os números que não
conseguem pensar, mas sim aqueles que não podem ser expressos por uma fração p/q. Um exemplo
muito conhecido de número irracional é o PI que é igual a 3.14159...
O último conjunto é o dos números complexos. Neste conjunto os números são representados da
seguinte maneira
n = a + ib

Os números a e b são números reais e i representa a raiz quadrada do número inteiro -1. Quando b é
igual a 0 o número é um número pertencente ao conjunto dos reais. Como pode-se ver, a medida que
fomos passando de um conjunto para outro, aumentou o nível de abstração das quantidades que os
números pertencentes aos conjuntos representam.
A Figura a seguir é uma representação das relações de pertinência entre os conjuntos de números
que analisamos até aqui.

Os dados numéricos que os algoritmos que iremos criar e que a maioria dos computadores manipulam
são de dois tipos:
- Dados inteiros.
- Dados reais.

Neste ponto é importante assinalar dois fatos importantes. Primeiro computadores trabalham com uma
base diferente da base que usamos todos dias que é a base 10. Computadores usam a base 2, e no
processo de conversão entre bases podem ocorrer problemas de perda de dígitos significativos. Por
exemplo, o número real 0.6 ao ser convertido para a base dois gera uma dízima periódica.
Outro fato importante é que a largura das palavras de memória do computador é limitada e portanto o
número de dígitos binários que podem ser armazenados é função deste tamanho. Isto é similar ao que
aconteceria se tivéssemos que limitar o número o dígitos decimais que usamos para representar os
números de nossas contas no banco. Por exemplo, assuma que só podemos armazenar quantias com 6
dígitos inteiros e dois decimais, deste modo se ganhássemos na loteria R$ 1.000.000,00 não seria
possível representar este valor no extrato do banco. Portanto no processo de conversão e desconversão
entre bases pode haver perda de informação.

Dados Numéricos Inteiros


O conjunto dos dados inteiros pode ser definido como
Z={...,-3,-2,0,1,2,...}.

As linguagens usadas para programar computadores são muito exigentes com a maneira com que os
dados são representados. Por esta razão vamos passar a definir como deveremos representar os dados
nos algoritmos. Os dados inteiros tem a seguinte forma:
NúmeroInteiro = [+,-]algarismo{algarismo}

189
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Neste texto iremos usar uma notação especial para definir como iremos representar os elementos da
linguagem. A medida que formos apresentando os elementos a notação será também explicada. No caso
da definição dos dados inteiros temos os seguintes elementos. O elemento básico é o algarismo que é
um dos seguintes caracteres:
algarismo = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Os elementos entre colchetes são opcionais. Portanto, o sinal de + e - entre colchetes significa que um
número inteiro pode ou não ter sinal. Em seguida temos um algarismo que é obrigatório. Isto é dados
inteiros tem de ter pelo menos um algarismo. A seguir temos a palavra algarismo entre chaves, o que
significa que um número inteiro deve ter pelo menos um algarismo e pode ser seguido por uma sequência
de algarismos. Deste modo elementos que aparecem entre chaves são elementos que podem ser
repetidos.
São portanto exemplos de números inteiros:
a) +3
b) 3
c) -324

Como exemplos de formas erradas de representação de números inteiros, temos:


a) +3.0 Não é possível usar ponto decimal
b) + 123 Espaços em branco não são permitidos

Dados Numéricos Reais


Os dados reais tem a seguinte forma:
[+,-]algarismo{algarismo}.algarismo{algarismo}.

Ou seja um número real pode ou não ter sinal, em seguida um conjunto de pelo menos um algarismo,
um ponto decimal e depois um conjunto de pelo menos um algarismo. É importante notar que o separador
entre a parte inteira e a fracionário é o ponto e não a vírgula.
São exemplos de números reais:
a) 0.5
a) +0.5
a) -3.1415

Abaixo mostramos exemplos de formas erradas de representação de números reais, temos:


a) +3,0 Vírgula não pode ser separador entre as partes real e inteira
b) 0.333... Não é possível usar representação de dízimas

Dados Literais
Dados literais servem para tratamento de textos. Por exemplo, um algoritmo pode necessitar imprimir
um aviso para os usuários, ou um comentário junto com um resultado numérico. Outra possibilidade é a
necessidade de ler dados tais como nomes, letras, etc.
Este tipo de dados pode ser composto por um único caractere ou por um conjunto de pelo menos um
destes elementos. Conjuntos são conhecidos como cadeias de caracteres, tradução da expressão em
inglês, "character string".
Um ponto importante que deve ser abordado agora é o que se entende por caractere. Caracteres são
basicamente as letras minúsculas, maiúsculas, algarismos, sinais de pontuação, etc. Em computação
caracteres são representados por códigos binários e o mais disseminado de todos é o código ASCII. Este
padrão foi definido nos Estados Unidos e é empregado pela quase totalidade dos fabricantes de
computadores e programas. O apêndice mostra a tabela ASCII com estes códigos.
Os caracteres que normalmente são empregados nos algoritmos são os seguintes:
Letras maiúsculas:
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z

Letras minúsculas:
a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z

Algarismos:
0|1|2|3|4|5|6|7|8|9

190
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Caracteres de pontuação:
;|:|!|?|*|(|)|\|/|+|-|=|<|>

Constantes Caracter
Caracteres podem aparecer sozinhos, e neste caso são chamados de constante caracter e são
representados entre o caracter '. Abaixo mostramos exemplos de constantes caracter:
'a'
'A'
';'
'+'

Cadeias de Caracter
Cadeias de caracteres são conjuntos de um ou mais caracteres e são cercados pelo caracter ". Por
exemplo:
"Linguagem de programação"
"Qual é o seu nome?"
"12345"

Dados Lógicos
Este tipo de dados é intensamente aplicado durante o processo de tomada de decisões que o
computador frequentemente é obrigado a fazer. Em muitos textos este tipo de dados também é chamado
de dados booleanos, devido a George Boole, matemático que deu ao nome à álgebra (álgebra booleana)
que manipula este tipo de dados. Os dados deste tipo somente podem assumir dois valores: verdadeiro
e falso.
Computadores tomam decisões, durante o processamento de um algoritmo, baseados nestes dois
valores. Por exemplo, considere a sentença a seguir que é um caso típico de decisão que o computador
é capaz de tomar sem intervenção humana.
Se está chovendo então procurar guarda-chuva.
Nesta sentença temos a questão lógica "Se está chovendo". Esta expressão somente pode ter como
resultado um de dois valores: verdade ou falso. Nos nossos algoritmos estes valores serão representados
por verdade e falso.
Tomando como exemplo a linguagem C, os dados podem assumir cinco tipos básicos que são:
- char: Caracter: O valor armazenado é um caractere. Caracateres geralmente são armazenados em
códigos (usualmente o código ASCII).
- int: Número inteiro é o tipo padrão e o tamanho do conjunto que pode ser representado normalmente
depende da máquina em que o programa está rodando.
- float: Número em ponto flutuante de precisão simples. São conhecidos normalmente como números
reais.
- double: Número em ponto flutuante de precisão dupla
- void: Este tipo serve para indicar que um resultado não tem um tipo definido. Uma das aplicações
deste tipo em C é criar um tipo vazio que pode posteriormente ser modificado para um dos tipos anteriores.

Estruturas de Dados: Vetores e Matrizes48

Os tipos básicos (inteiros, reais, caracteres) não são suficientes para exprimir estruturas de dados
complexas em algoritmos.
Um Vetor é uma das mais simples estruturas de dados.
Vetores são, essencialmente, listas de informações de um mesmo tipo, armazenadas em posição
contígua da memória, em ordem indexada.
Vetores são usados nos casos em que um conjunto de dados do mesmo tipo precisa ser armazenado
em uma mesma estrutura.
Um vetor é uma estrutura de dados homogênea, isto é, agrupa valores de um mesmo tipo;
O tipo do vetor é o mesmo tipo dos dados que ele armazena.
Um vetor é uma estrutura de dados indexada, ou seja:
- Cada valor pode ser acessado através de um índice, o qual corresponde a uma posição no vetor;
- Os índices são valores inteiros e positivos (0, 1, 2, 3, ...);

48
http://www.deinf.ufma.br/~vidal/algoritmos1/vetoresmatrizes

191
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Em outras palavras, uma posição específica do vetor pode ser acessada diretamente através do seu
índice.

Matriz: uma matriz é uma coleção de variáveis de um mesmo tipo que é referenciada por um nome
comum;
Vetores são matrizes unidimensionais;
Matrizes Bidimensionais: uma matriz bidimensional é uma matriz de matrizes unidimensionais;

Matrizes de uma Dimensão ou Vetores49


Este tipo de estrutura em particular é também denominado por alguns profissionais de como matrizes
unidimensionais. Sua utilização mais comum está vinculada à criação de tabelas. Caracteriza-se por ser
defini da uma única variável dimensionada com um determinado tamanho. A dimensão de uma matriz é
constituída por constantes inteiras e positivas. Os nomes dados às matrizes seguem as mesmas regras
de nomes utilizados para indicar as variáveis simples.
Para ter uma idéia de como utilizar matrizes em uma determinada situação, considere o seguinte
problema: "Calcular e apresentar a média geral de uma turma de 8 alunos. A média a ser obtida deve ser
a média geral das médias de cada aluno obtida durante o ano letivo". Desta forma será necessário somar
todas as médias e dividi-las por 8. A tabela seguintes apresenta o número de alunos, suas notas
bimestrais e respectivas médias anuais. É da média década aluno que será efetuado o cálculo da média
da turma.

Agora basta escrever um programa para efetuar o cálculo das 8 médias de cada aluno. Para
representar a média do primeiro aluno será utilizada a variável MD1, para o segundo MD2 e assim por
diante. Então tem-se:
MD1 =4.5
MD2 =6.5
MD3 =8.0
MD 4 = 3.5
MD 5 = 6.0
MD 6 = 7.0
MD 7 = 6.5
MD 8 = 6.0

Com o conhecimento adquirido até este momento, seria então elaborado um programa que efetuaria
a leitura de cada nota, a soma delas e a divisão do valor da soma por 8, obtendo-se desta forma a média,
conforme exemplo abaixo em português estruturado:

programa MÉDIA_TURMA
var
MD1, MD2, MD3, MD4, MD5, MD6, MD7, MD8 : real
SOMA, MÉDIA : real
início
SOMA <- 0
leia <- MD1, MD2, MD3, MD4, MD5, MD6, MD7, MD8
SOMA <- MD1 + MD2 + MD3 + MD4 + MD5 + MD6 + MD7 + MD8
MÉDIA <- SOMA / 8
escreva MÉDIA
fim

49
MANZANO, JA OLIVEIRA, and J. F. Algoritmos. "Lógica para Desenvolvimento de Programação de Computadores." São Paulo: Érica (2000).

192
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Perceba que para receber a média foram utilizadas oito variáveis. Com a técnica de matrizes poderia
ter sido utilizada apenas uma variável com a capacidade de armazenar oito valores.

Operações Básicas com Matrizes do Tipo Vetor


Uma matriz de uma dimensão ou vetor será, neste trabalho, representada por seu nome e seu tamanho
(dimensão) entre colchetes. Desta forma seria uma matriz MD[1 ..8], sendo seu nome MD, possuindo um
tamanho de 1 a 8. Isto significa que poderão ser armazenados em MD até oito elementos. Perceba que
na utilização de variáveis simples existe um regra: uma variável somente pode conter um valor por vez.
No caso das matrizes, poderão armazenar mais de um valor por vez, pois são dimensionadas exatamente
para este fim. Desta forma poder-se-á manipular uma quantidade maior de informação com pouco
trabalho de processamento. Deve-se apenas considerar que com relação à manipulação dos elementos
de uma matriz, eles ocorrerão de forma individualizada, pois não é possível efetuar a manipulação de
todos os elementos do conjunto ao mesmo tempo.
No caso do exemplo do cálculo da média dos 8 alunos, ter-se-ia então uma única variável indexada (a
matriz) contendo todos os valores das 8 notas. Isto seria representado da seguinte forma:
MD[1] = 4.5 MD[2] = 6.5 MD[3] = 8.0 MD [4] =3.5 MD[5] = 6.0 MD[6] = 7.0 MD[7] = 6.5 MD[8] = 6.0

Observe que o nome é um só. O que muda é a informação indicada dentro dos colchetes. A esta
informação dá-se o nome de índice, sendo este o endereço em que o elemento está armazenado. E
necessário que fique bem claro que elemento é o conteúdo da matriz, neste caso os valores das notas.
No caso de MD[1 ] = 4.5, o número 1 é o índice; o endereço cujo elemento é 4.5 está armazenado.
Atribuição de uma Matriz
Anteriormente, foram utilizadas várias instruções em português estruturado para poder definir e montar
um programa. No caso da utilização de matrizes, será definida a instrução conjunto que indicará em
português estruturado a utilização de uma matriz, tendo como sintaxe: VARIÁVEL : conjunto[<dimensão>]
de ctipo de dado>, sendo que <dimensão> será a indicação dos valores inicial e final do tamanho do vetor
e <tipo de dado> se o vetor em questão irá utilizar valores reais, inteiros, lógicos ou caracteres.

Leitura dos Dados de uma Matriz


A leitura de uma matriz é processada passo a passo, um elemento por vez. A instrução de leitura é
leia seguida da variável mais o índice. A seguir, são apresentados diagrama de blocos e codificação em
português estruturado da leitura das notas dos 8 alunos, cálculo da média e a sua apresentação.

Diagrama de Blocos

Diagrama de blocos para leitura dos elementos de uma matriz tipo vetor.

193
1678859 E-book gerado especialmente para DANIEL CRISTIAN
programa MÉDIA_TURMA
var
MD : conjunto[1..8] de real
SOMA, MÉDIA : real
I : inteiro
início
SOMA <r- 0
para I de 1 até 8 passo 1 faça
leia MD[I]
SOMA <r- SOMA + MD[I]
fim_para
MÉDIA <r- SOMA / 8
escreva MEDIA
fim

Veja que o programa ficou mais compacto, além de possibilitar uma mobilidade maior, pois se houver
a necessidade de efetuar o cálculo para um número maior de alunos, basta dimensionar a matriz e mudar
o valor final da instrução para. Observe que no exemplo anterior, a leitura é processada uma por vez.
Desta forma, a matriz é controlada pelo número do índice que faz com que cada entrada aconteça em
uma posição diferente da outra. Assim sendo, a matriz passa a ter todas as notas. A tabela seguinte,
mostra como ficarão os valores armazenados na matriz:

Tenha cuidado para não confundir o índice com o elemento. índice é o endereço de alocação de uma
unidade da matriz, enquanto elemento é o conteúdo armaze¬nado em um determinado endereço.

Escrita dos Dados de uma Matriz


O processo de escrita de uma matriz é bastante parecido com o processo de leitura de seus
elementos. Para esta ocorrência deverá ser utilizada a instrução escreva seguida da indicação da variável
e seu índice. Supondo que após a leitura das 8 notas, houvesse a necessidade de apresentá-las antes
da apresentação do valor da média. Abaixo são apresentados o diagrama de blocos e a codificação em
português estruturado da escrita das notas dos 8 alunos antes de ser apresentado o cálculo da média.

194
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Diagrama de Blocos

Diagrama de bloco para escrita dos elementos de uma matriz tipo vetor.

programa MÉDIA_TURMA
var
MD : conjunto[1..8] de real
SOMA, MÉDIA : real
I : inteiro
início
SOMA <- 0
para I de 1 até 8 passo 1 faça
leia MD[I]
SOMA <- SOMA + MD[I]
fim_para
MÉDIA <- SOMA / 8
para I de 1 até 8 passo 1 faça
escreva MD[I]
fim_para
escreva MÉDIA
fim

As Sub-rotinas

No geral, problemas complexos exigem algoritmos complexos. Mas sempre é possível dividir um
problema grande em problemas menores. Desta forma, cada parte menor tem um algoritmo mais simples,
e é esse trecho menor que é chamado de sub-rotina. Uma sub-rotina é na verdade um programa, e sendo
um programa poderá efetuar diversas operações computacionais (entrada, processamento e saída) e
deverá ser tratada como foram os programas projetados até este momento. As sub-rotinas são utilizadas
na divisão de algoritmos complexos, permitindo assim possuir a modularização de um determinado
problema, considerado grande e de difícil solução.

195
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Ao trabalhar com esta técnica, pode-se deparar com a necessidade de dividir uma sub-rotina em outras
tantas quantas forem necessárias, buscando uma solução mais simples de uma parte do problema maior.
O processo de dividir sub-rotinas em outras é denominado Método de Refinamento Sucessivo.

O Método Top-Down
O processo de programar um computador torna-se bastante simples quando aplicado o método de
utilização de sub-rotinas (módulos de programas). Porém, a utilização dessas sub-rotinas deverá ser feita
com aplicação do método top down.
Um método bastante adequado para a programação de um computador é trabalhar com o conceito de
programação estruturada, pois a maior parte das linguagens de programação utilizadas atualmente
também são, o que facilita a aplicação deste processo de trabalho. O método mais adequado para a
programação estruturada é o Top-Down (De cima para baixo) o qual se caracteriza basicamente por:
Antes de iniciar a construção do programa, o programador deverá ter em mente as tarefas principais
que este deverá executar. Não é necessário saber como funcionarão, somente saber quantas são.
Conhecidas todas as tarefas a serem executadas, tem-se em mente como deverá ser o programa
principal, o qual vai controlar todas as outras tarefas distribuídas em suas sub-rotinas.
Tendo definido o programa principal, é iniciado o processo de detalhamento para cada sub-rotina.
Desta forma são definidos vários algoritmos, um para cada rotina em separado, para que se tenha uma
visão do que deverá ser executado em cada módulo de programa. Existem programadores que
estabelecem o número máximo de linhas de programa que uma rotina deverá possuir. Se o número de
linhas ultrapassa o limite preestabelecido, a rotina em desenvolvimento é dividida em outra sub-rotina (é
neste ponto que se aplica o método de refinamento sucessivo).
O método Top-Down faz com que o programa tenha uma estrutura semelhante a um organograma.
A figura abaixo apresenta um exemplo desta estrutura.
A utilização do método "de cima para baixo" permite que seja efetuado cada módulo de programa em
separado. Desta forma, cada um pode ser testado separadamente garantindo que o programa completo
esteja sem erro ao seu término.
Outro detalhe a ser considerado é que muitas vezes existem em um programa trechos de códigos que
são repetidos várias vezes. Esses trechos poderão ser utilizados como sub-rotinas, proporcionando um
programa menor e mais fácil de ser alterado num futuro próximo.

A utilização de sub-rotinas e o uso do método Top-Down na programação permitem ao programador


elaborar rotinas exclusivas. Por exemplo, uma rotina somente para entrada, outra para a parte de
processamento e outra para a saída dos dados. Se o leitor comparar esta proposta com o que foi estudado
anteriormente, verá suas vantagens. Lembre-se dos programas anteriores todos os seus algoritmos de
saída obrigavam de certa forma efetuar primeiro a entrada dos dados.

Procedimentos
Um procedimento é um bloco de programa contendo início e fim e será identificado por um nome, por
meio do qual será referenciado em qualquer parte do programa principal ou do programa chamador da
rotina. Quando uma sub-rotina é chamada por um programa, ela é executada e ao seu término o controle

196
1678859 E-book gerado especialmente para DANIEL CRISTIAN
de processamento retorna automaticamente para a primeira linha de instrução após a linha que efetuou
a chamada da sub-rotina.
Com relação à criação da rotina, será idêntica a tudo o que já foi estudado sobre programação. Na
representação do diagrama de blocos, não há quase nenhuma mudança, a não ser pela troca das
identificações Início e Fim nos símbolos de Terminal e o novo símbolo Sub-rotina, que é idêntico ao
símbolo de processamento, porém se caracteriza pelas linhas paralelas às bordas esquerda e direita,
devendo ser utilizado no programa chamador. A sintaxe em português estruturado será também idêntica
ao estudo anterior. Observe em seguida, o código em português estruturado.

Português Estruturado
procedimento <nome do procedimento>
var
<variáveis>
inicio
<instruções>
fim

A melhor maneira de entender como trabalhar com uma sub-rotina é fazer a sua aplicação em um
programa mais complexo. Para tanto, imagine o seguinte problema:
Criar um programa calculadora que apresente um menu de seleções no programa principal. Esse menu
deverá dar ao usuário a possibilidade de escolher uma entre quatro operações aritméticas. Escolhida a
opção desejada, deverá ser solicitada a entrada de dois números, e processada a operação deverá ser
exibido o resultado.

Algoritmo
Note que esse programa deverá ser um conjunto de cinco rotinas, sendo uma principal e quatro
secundárias. A rotina principal efetuará o controle das quatro rotinas secundárias que, por sua vez,
pedirão a leitura de dois valores, farão a operação e apresentarão o resultado obtido. Afigura 11.2
apresenta um organograma com a ideia de hierarquização das rotinas do programa. A quinta opção não
se caracteriza por ser uma rotina, apenas a opção que vai encerrar o looping de controle do menu.

Tendo uma ideia da estrutura geral do programa, será escrito em separado cada algoritmo com os
seus detalhes de operação. Primeiro o programa principal e depois as outras rotinas, de preferência na
mesma ordem em que estão mencionadas no organograma.

Programa Principal
1- Apresentar um menu de seleção com cinco opções:
1-Adição
2-Subtração
3-Multiplicação
4- Divisão
5-Fim de Programa

2- Ao ser selecionado um valor, a rotina correspondente deverá ser executada;


3- Ao escolher o valor 5, o programa deverá ser encerrado.

Rotina 1 - Adição
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a soma das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

197
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Rotina 2 - Subtração
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a subtração das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Rotina 3 - Multiplicação
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a multiplicação das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Rotina 4 - Divisão
1- Ler dois valores, no caso variáveis A e B;
2- Efetuar a divisão das variáveis A e B, implicando o seu resultado na variável R;
3- Apresentar o valor da variável R.

Observe que em cada rotina serão utilizadas as mesmas variáveis, mas elas não serão executadas ao
mesmo tempo para todas as operações. Serão utilizadas em separado e somente para a rotina escolhida.

Diagramas de Blocos
Perceba que na diagramação cada rotina é definida em separado como um programa independente.
O que muda é a forma de identificação do símbolo Terminal. Em vez de se utilizarem os termos Início e
Fim, utilizam-se o nome da sub-rotina para iniciar e a palavra Retomar para encerrar. Com relação ao
módulo principal, ele faz uso do símbolo Sub-rotina que indica a chamada de uma sub-rotina.

198
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Diagramas de blocos para o programa calculadora com sua sub-rotina.

Português Estruturado
Em código português estruturado, serão mencionadas em primeiro lugar as sub-rotinas e por último o
programa principal. Quando no programa principal ou rotina chamadora for referenciada uma sub-rotina,
ela será sublinhada para facilitar sua visualização. Observe no programa a variável OPÇÃO para controlar
a opção do operador que é do tipo caractere. Outro detalhe a ser observado é o nome de cada rotina
mencionado junto da verificação da instrução se no módulo de programa principal.

programa CALCULADORA
var
OPÇÃO : caractere

{Sub-rotinas de cálculos} procedimento ROTSOMA


var
R, A, B : real
inicio
escreva "Rotina de Adição"
escreva "Entre um valor para A: “
leia A
escreva "Entre um valor para B: " leia B
R <- A + B
escreva "A soma de A com B é = ", R
fim

procedimento ROTSUBTRAÇÃO
var
R, A, B: real inicio
escreva "Rotina de Subtração" escreva "Entre um valor para A: "
leia A
escreva "Entre um valor para B: “
leia B
R <- A - B
escreva "A subtração de A com B é = ", R
fim

199
1678859 E-book gerado especialmente para DANIEL CRISTIAN
procedimento ROTMULTIPLICAÇÃO
var
R, A, B : real
inicio
escreva "Rotina de Multiplicação"
escreva "Entre um valor para A: “
leia A
escreva "Entre um valor para B: "
leia B
R <r- A * B
escreva "A multiplicação de A com B é = ", R
fim

procedimento ROTDIVISÃO var


R, A, B: real
inicio
escreva "Rotina de Divisão”
escreva "Entre um valor para A: "
leia A
escreva "Entre um valor para B: "
leia B
R <- A / B
escreva "A divisão de A com B é = ", R
fim

(Programa Principal}
início OPÇÃO <- "0"
enquanto (OPÇÃO <> "5") faça
escreva "1 - Adição"
escreva "2 - Subtração"
escreva "3 - Multiplicação"
escreva "4 - Divisão"
escreva "5 - Fim de Programa"
escreva "Escolha uma opção: "
leia OPÇÃO
se (OPÇÃO = "1") então rotsoma fim_se
se (OPÇÃO = "2") então rotsubtracão fim_se
se (OPÇÃO = "3") então rotmultiolicacão fim_se
se (OPÇÃO = "4") então rotdivisão fim_se
fim_enquanto
fim

Funções
Função também é um bloco de programa, como são os procedimentos, contendo início e fim e sendo
identificada por um nome, por meio do qual também será referenciada em qualquer parte do programa
principal. Uma sub-rotina de função é na verdade muito parecida com uma sub-rotina de procedimento.
A sintaxe em português estruturado é também idêntica ao estudo anterior. Observe em seguida, o código
em português estruturado de uma função.

Português Estruturado
função <nome da função> (parâmetros) : <tipo da função>
var
<variáveis>
início
<instruções>
fim
A sua principal diferença está no fato de uma função retornar um determinado valor, que é retornado
no próprio nome da função. Quando se diz valor, devem ser levados em consideração os valores
numéricos, lógicos ou literais (caracteres).

200
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Parâmetros

Os parâmetros têm por finalidade servir como um ponto de comunicação bidirecional entre uma sub-
rotina e o programa principal ou uma outra sub-rotina hierarquicamente de nível mais alto. Desta forma,
é possível passar valores de uma sub-rotina ou rotina chamadora à outra sub-rotina e vice-versa,
utilizando parâmetros que podem ser formais ou reais.

Parâmetros Formais e Reais


Serão considerados parâmetros Formais quando forem declarados por meio de variáveis juntamente
com a identificação do nome da sub-rotina, os quais serão tratados exatamente da mesma forma que são
tratadas as variáveis globais ou locais. Considere como exemplo de parâmetros formais o código em
português estruturado da sub-rotina apresentado abaixo:

procedimento CALCSOMA(A, B : inteiro)


var
Z : inteiro
inicio
Z <- A + B escreva Z
fim

Observe que a variável Z é local e está sendo usada para armazenar a soma das variáveis A e B que
representam os parâmetros formais da sub-rotina CALCSOMA.
Serão considerados parâmetros Reais quando substituírem os parâmetros formais, quando da
utilização da sub-rotina por um programa principal ou por uma rotina chamadora. Considere como
exemplo de parâmetros reais o código em português estruturado do programa que faz uso da sub-rotina
CALCSOMA apresentado em seguida:

inicio
leia X
leia V
calcsoma (X. Y)
leia W
leia T
calcsoma (W, T)
calcsoma(8, 2)
fim

No trecho acima, toda vez que a sub-rotina CALCSOMA é chamada, faz-se uso de parâmetros reais.
Desta forma, são parâmetros reais as variáveis X, Y, W e T, pois seus valores são fornecidos pela
instrução leia e também os valores 8 e 2.

Pesquisa Sequencial e Binária50

Estudo de como recuperar informação a partir de uma grande massa de informação previamente
armazenada.
- A informação é dividida em registros.
- Cada registro possui uma chave para ser usada na pesquisa.
- Objetivo da pesquisa: Encontrar uma ou mais ocorrências de registros com chaves iguais à chave de
pesquisa.

É importante considerar os algoritmos de pesquisa como tipos abstratos de dados (TADs), de tal forma
que haja uma independência de implementação para as operações.
Operações mais comuns:
1. Inicializar a estrutura de dados.
2. Pesquisar um ou mais registros com determinada chave.
3. Inserir um novo registro.
4. Retirar um registro específico.

50
http://www.decom.ufop.br/

201
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Dicionário é um TAD com as operações:
1. Inicializa
2. Pesquisa
3. Insere
4. Retira

Analogia com um dicionário da língua portuguesa:


- Chaves ⇔ palavras
- Registros ⇔ entradas associadas com *pronúncia, definição, sinônimos, outras informações.

Busca Sequencial51
É o método de pesquisa mais simples: a partir do primeiro registro, pesquise sequencialmente até
encontrar a chave procurada; então pare.
Armazenamento de um conjunto de registros por meio de um array.
A Busca (find):
- Pesquisa retorna o índice do registro que contém a chave x;
- Caso não esteja presente, o valor retornado é -1.
- A implementação não suporta mais de um registro com uma mesma chave, pois retorna o primeiro
encontrado.

Análise:
Pesquisa com sucesso:

51
https://www.ime.usp.br/~pf/algoritmos/aulas/bubi2.html

202
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- Melhor caso : C(n) = 1
- Pior caso : C(n) = n
- Caso médio: C(n) = (n + 1) / 2

Pesquisa sem sucesso:


- C (n) = n + 1.

O algoritmo de pesquisa sequencial é a melhor escolha para o problema de pesquisa com n < 25.
Veremos um algoritmo óbvio, que examina um a um todos os elementos do vetor. Segue uma
implementação do algoritmo:

O valor de m muda a cada iteração, mas as relações m ≤ n e v[m-1] < x são invariantes: elas valem
no início de cada iteração. Mais precisamente, essas relações invariantes valem imediatamente antes de
cada comparação de m com n (ponto A do código). No começo da primeira iteração, a relação vale se
estivermos dispostos a imaginar que v[-1] é −∞.
A relação invariante vale, em particular, no início da última iteração, quando m ≥ n ou v[m] ≥ x. Se
m ≥ n, temos m == n e v[n-1] < x e a função devolve -1. Se m < n mas v[m] > x, a função devolve -1. Se
m < n e v[m] == x, a função devolve m. Nos três casos, a função devolve a resposta correta. Essa
discussão mostra que a função buscaSequencial está correta.
Quantas iterações a função faz? Ou melhor, quantas vezes a função compara x com elementos de v?
No pior caso, x é comparado com cada elemento do vetor, e portanto o número de comparações é n .
O consumo de tempo da função é proporcional ao número de comparações que envolvem x, e portanto
proporcional a n no pior caso. Assim, se uma busca consome T microssegundos quando o vetor tem N
elementos, consumirá 10T microssegundos quando o vetor tem 10N elementos.
É possível resolver o problema com menos comparações? É possível resolver o problema sem
comparar x com cada elemento do vetor? A resposta é afirmativa, como veremos a seguir.

Busca Binária
Pesquisa em tabela pode ser mais eficiente se registros forem mantidos em ordem
Para saber se uma chave está presente na tabela:
- Compare a chave com o registro que está na posição do meio da tabela.
- Se a chave é menor então o registro procurado está na primeira metade da tabela
- Se a chave é maior então o registro procurado está na segunda metade da tabela.
- Repita até que a chave seja encontrada ou que se constate que a chave não existe na tabela.

203
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Análise:
- A cada iteração do algoritmo, o tamanho da tabela é dividido ao meio.
- Logo: o número de vezes que o tamanho da tabela é dividido ao meio é cerca de log n.
- Ressalva: o custo para manter a tabela ordenada é alto: a cada inserção na posição p da tabela
implica no deslocamento dos registros a partir da posição p para as posições seguintes.
- Consequentemente, a pesquisa binária não deve ser usada em aplicações muito dinâmicas.

204
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Existe um algoritmo muito mais rápido que a busca sequencial. Ele é análogo ao método que se usa
para encontrar um nome em uma lista telefônica. É claro que essa ideia só funciona porque o vetor está
ordenado.

Os nomes das variáveis não foram escolhidos por acaso: e lembra esquerda, m lembra meio e d
lembra direita. O resultado da divisão por 2 na expressão (e+d)/2 é automaticamente truncado, pois as
variáveis são do tipo int. Por exemplo, se e vale 3 e d vale 6, então (e+d)/2 vale 4.

A ideia da busca binária (= binary search) é o ponto de partida de algoritmos eficientes para muitos
problemas.

A Função Busca Binaria está Correta?


Para entender a função buscaBinaria, basta verificar que no início de cada repetição do while,
imediatamente antes da comparação de e com d, vale a relação

v[e-1] < x < v[d+1]

Essa relação é, portanto, invariante. Para que o invariante valha no início da primeira iteração basta
imaginar que v[-1] vale −∞ e que v[n] vale +∞. Esse jogo de imaginar faz sentido porque o vetor é
crescente.
Se a execução da função termina na linha 5, o índice m é obviamente uma solução do problema.
Suponha agora que a execução termina na linha 9. Então a última iteração começou (na linha 3) com e >
d. Em virtude do invariante, temos v[e-1] < v[d+1] e portanto e-1 < d+1, uma vez que o vetor é crescente.
Logo, e == d+1. A relação v[e-1] < x < v[d+1] garante agora que x está estritamente entre dois elementos
consecutivos do vetor. Como o vetor é crescente, concluímos que x é diferente de todos os elementos do
vetor. Portanto, ao devolver -1 a função está se comportando como prometeu.
Resta verificar que a execução da função termina. Suponha que a execução não é interrompida na
linha 5. Como o valor da diferença d - e diminui a cada iteração, a diferença fica estritamente negativa
depois de algumas iterações e então o processo iterativo termina na linha 9.

Desempenho da Busca Binária


Quanto tempo a função buscaBinaria consome? No início da primeira iteração, d - e vale
aproximadamente n. No início da segunda, vale aproximadamente n/2. No início da terceira,
aproximadamente n/4. No início da (k+1)-ésima, aproximadamente n/2k. Quando k passar de log n , o
valor da expressão n/2k fica menor que 1 e a execução do algoritmo termina. Logo, o número de iterações
é aproximadamente

lg (n)

no pior caso, sendo lg (n) o piso de log n . Isso é muito menos que o número de iterações da busca
sequencial, pois log transforma multiplicações em somas. Por exemplo, se uma busca em um vetor de

205
1678859 E-book gerado especialmente para DANIEL CRISTIAN
tamanho N exige T iterações, então uma busca em um vetor de tamanho 2N fará apenas 1 + T iterações,
uma busca em um vetor de tamanho 8N fará apenas 3 + T iterações, e uma busca em um vetor de
tamanho 16N fará apenas 4 + T iterações.
O consumo de tempo da função buscaBinaria é proporcional ao número de iterações e portanto
proporcional a log n no pior caso.

Versão Recursiva da Busca Binária


Para formular uma versão recursiva da busca binária é preciso generalizar ligeiramente o problema,
trocando v[0..n-1] por v[e..d]. Para estabelecer uma ponte entre a formulação original e a generalizada,
vamos usar uma função-embalagem (= wrapper-function) buscaBinaria2, que repassa o serviço para a
função recursiva bb.

Qual a profundidade da recursão na função bb? Ou seja, quantas vezes bb chama a si mesma?
Resposta: cerca de log n vezes.

Métodos de Ordenação

Ordenar corresponde ao processo de rearranjar um conjunto de objetos em uma ordem específica.


O principal objetivo da ordenação é facilitar a recuperação posterior de elementos do conjunto
ordenado.
Exemplos:
– Listas telefônicas.
– Dicionários.
– Índices de livros.
– Tabelas e arquivos.

Os algoritmos trabalham sobre os registros de um arquivo.


Apenas uma parte do registro, chamada chave, é utilizada para controlar a ordenação.
Além da chave podem existir outros componentes em um registro, que não têm influência no processo
de ordenar, a não ser pelo fato de que permanecem com a mesma chave.
O tamanho dos outros componentes pode influenciar na escolha do método ou na forma de
implementação de um dado método.
A estrutura de dados registro é a indicada para representar os elementos a serem ordenados.
Um algoritmo que ordena uma conjunto, geralmente representada num vetor, é chamado de algoritmo
de ordenação. Algoritmo de ordenação em ciência da computação é um algoritmo que coloca os
elementos de uma dada sequência em uma certa ordem — em outras palavras, efetua sua ordenação
completa ou parcial. As ordens mais usadas são a numérica e a léxico-gráfica. Existem várias razões

206
1678859 E-book gerado especialmente para DANIEL CRISTIAN
para se ordenar uma sequência. Uma delas é a possibilidade se acessar seus dados de modo mais
eficiente.

Entre os mais importantes, podemos citar bubble sort (ou ordenação por flutuação), heap sort (ou
ordenação por heap), insertion sort (ou ordenação por inserção), merge sort (ou ordenação por mistura)
e o quicksort. Existem diversos outros.

Natureza dos Dados52


Para melhor escolha de um método de ordenação é preciso saber sobre a natureza dos dados que
serão processados. Entre elas destacam-se duas: Tempo de acesso a um elemento e a possibilidade de
acesso direto a um elemento.
O tempo de acesso a um elemento é a complexidade necessária para acessar um elemento em uma
estrutura, exemplos:
- Uma estante de livros com seu títulos bem visíveis.

A possibilidade de acesso direto é a capacidade ou impossibilidade de acessar um elemento


diretamente na estrutura.
- Uma pilha de livros dentro de uma caixa, onde precisamos tirar um a um para saber qual a sua
natureza.

Para classificarmos estes dois ambientes de atuação, costumamos utilizar o meio em que está
armazenado os dados. Em termos computacionais utiliza-se a designação Ordenação Interna, quando
queremos ordenar informações em memória. E Ordenação Externa, quando queremos ordenar
informações em arquivo.

Selection Sort (Ordenação por Seleção)


O Selection Sort utiliza um o conceito de "selecionar o elemento mais apto". Ele seleciona o menor ou
maior valor do vetor, por exemplo, e passa para a primeira (ou última posição dependendo da ordem
requerida), depois o segundo menor para a segunda posição e assim sucessivamente com (n-1)
elementos restantes até os dois últimos.

Vetor inicial:

Primeira passagem: Posição 0- compara 4 com 3.Como 3 é menor que 4 este é fixado como mínimo,
compara 3 com 1. Como este é menor do que 3 é fixado como mínimo. Compara 1 com 2. Como continua
sendo menor, é fixado. Ao chegar ao final do vetor, como 1 é o menor elemento em comparação com o
4, eles trocam de posição.

Segunda Passagem: Posição 1- como já temos 1 como o menor elemento do vetor, passamos para a
posição 1. Comparamos 3 com 4.Como é menor, 3 continua como mínimo. Compara com 2. Como 2 é
menor este é fixado como mínimo. Ao chegar ao final do vetor, como 2 é o menor elemento em
comparação com o 3, eles trocam de posição.

Terceira Passagem: Posição 2- pegamos o elemento da posição 2 (4) e comparamos com o 3. Como
3 é o último elemento do vetor e é menor do que 4, trocamos as posições. Como os dois elementos são
os últimos do vetor, o Selection Sort encerra-se.

52
https://pt.wikibooks.org/wiki/Algoritmos_e_Estruturas_de_Dados/Algoritmos_de_Ordena%C3%A7%C3%A3o

207
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O algoritmo normalmente é implementado por duas repetições iterando sobre a estrutura em questão.
Um exemplo de algoritmo é:

para i=0 até n-1


mínimo=i
para j=i+1 até N
se vetor[j]<vetor[mínimo]
mínimo=j
auxiliar=vetor[i]
vetor[i]=vetor[mínimo]
vetor[mínimo]=auxiliar

Bubble Sort (Ordenação Bolha)


O bubble sort, ou ordenação por flutuação (literalmente "por bolha"), é um algoritmo de ordenação dos
mais simples. A ideia é comparar dois elementos e trocá-los de posição, até que os elementos de maior
valor sejam levados para o final do vetor. O processo continua até a ordenação total do vetor lembrando
a forma como as bolhas em um tanque de água procuram seu próprio nível, e disso vem o nome do
algoritmo.
A complexidade desse algoritmo é de ordem quadrática (O(n²)). Por isso, ele não é recomendado para
programas que precisem de velocidade e operem com quantidade elevada de dados. Também é
necessária uma condição de parada, geralmente uma flag ou variável que armazena se houve troca ou
não na passagem. Se uma passagem chega ao seu final sem troca a ordenação cessa.
Vetor inicial:

Primeira Passagem: compara 4 com 2. 4 maior que 2.Mudam de posição.

Segunda Passagem: compara 4 com 5. 4 menor que 5.Permanece.

Terceira passagem: compara 5 com 1. 1 menor que 5.Mudam de posição.

Quarta passagem: compara 2 com 4. 2 menor que 4.Permanece.

Quinta passagem: compara 4 com 1. 1 menor que 4.Mudam de posição.

208
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Sexta passagem: compara 4 com 5. 4 menor que 5.Permanece.

Sétima passagem: compara 2 com 1. 1 menor que 2.Trocam de posição.

Oitava passagem: compara 2 com 4. 2 menor do que 4.Permanece.

Nona passagem: compara 4 com 5. 4 menor do que 5. Permanece.

Décima passagem: não há mudanças. Sai do laço.

O algoritmo pode ser descrito em pseudo-código como segue abaixo. V é um VETOR de elementos
que podem ser comparados e n é o tamanho desse vetor.
BUBBLESORT (V[], n) 1 houveTroca <- verdade # uma variável de controle
2 enquanto houveTroca for verdade faça
3 houveTroca <- falso
4 para i de 1 até n-1 faça
5 se V[i] vem depois de V[i + 1]
6 então troque V[i] e V[i + 1] de lugar e
7 houveTroca <- verdade

Ordenação por Inserção.


Este algoritmo é bastante eficiente quando aplicado a um pequeno número de elementos. Em termos
gerais, ele percorre um vetor de elementos da esquerda para a direita e à medida que avança vai deixando
os elementos mais à esquerda ordenados.

209
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Listas Encadeadas53

Uma lista encadeada é uma representação de uma sequência de objetos, todos do mesmo tipo, na
memória RAM (= random access memory) do computador. Cada elemento da sequência é armazenado
em uma célula da lista: o primeiro elemento na primeira célula, o segundo na segunda e assim por diante.

Estrutura de uma Lista Encadeada


Uma lista encadeada (= linked list = lista ligada) é uma sequência de células; cada célula contém um
objeto de algum tipo e o endereço da célula seguinte. Suporemos neste capítulo que os objetos
armazenados nas células são do tipo int. Cada célula é um registro que pode ser definido assim:

É conveniente tratar as células como um novo tipo-de-dados e atribuir um nome a esse novo tipo:

typedef struct reg celula; // célula

Uma célula c e um ponteiro p para uma célula podem ser declarados assim:

celula c;
celula *p;

Se c é uma célula então c.conteudo é o conteúdo da célula e c.prox é o endereço da próxima célula.
Se p é o endereço de uma célula, então p->conteudo é o conteúdo da célula e p->prox é o endereço
da próxima célula. Se p é o endereço da última célula da lista então p->prox vale NULL .

A figura pode dar a falsa impressão de que as células da lista ocupam posições consecutivas na
memória. Na realidade, as células estão tipicamente espalhadas pela memória de maneira imprevisível).

Endereço de uma Lista Encadeada.


O endereço de uma lista encadeada é o endereço de sua primeira célula. Se le é o endereço de uma
lista, convém dizer simplesmente

le é uma lista encadeada.

(Não confunda le com 1e). A lista está vazia (ou seja, não tem célula alguma) se e somente se le ==
NULL.
Listas são animais eminentemente recursivos. Para tornar isso evidente, basta fazer a seguinte
observação: se le é uma lista não vazia então le->prox também é uma lista. Muitos algoritmos sobre
listas encadeadas ficam mais simples quando escritos de maneira recursiva.
Exemplo. A seguinte função recursiva imprime o conteúdo de uma lista encadeada le:

53
https://www.ime.usp.br/

210
1678859 E-book gerado especialmente para DANIEL CRISTIAN
E aqui está a versão iterativa da mesma função:

Busca em uma Lista Encadeada


Veja como é fácil verificar se um objeto x pertence a uma lista encadeada, ou seja, se é igual ao
conteúdo de alguma célula da lista:

Eis uma versão recursiva da mesma função:

Árvore Binária54
Árvore binária é uma estrutura de dados caracterizada por:
- Ou não tem elemento algum (árvore vazia).
- Ou tem um elemento distinto, denominado raiz, com dois apontamentos para duas estruturas
diferentes, denominadas sub-árvore esquerda e sub-árvore direita.
Perceba que a definição é recursiva e, devido a isso, muitas operações sobre árvores binárias utilizam
recursão. É o tipo de árvore mais utilizado na computação. A principal utilização de árvores binárias são
as árvores de busca.
Implementação de um Nó de uma Árvore Binária
Um nó de uma árvore binária é composto por, pelo menos, três elementos, a sua chave e mais dois
ponteiros que apontarão um para a sub-árvore esquerda e outro para a sub-árvore direita.

54
https://pt.wikibooks.org/wiki/Algoritmos_e_Estruturas_de_Dados/%C3%81rvores_Bin%C3%A1rias

211
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Definições para Árvores Binárias
Os nós de uma árvore binária possuem graus zero, um ou dois. Um nó de grau zero é denominado
folha.
Uma árvore binária é considerada estritamente binária se cada nó da árvore possui grau zero ou dois.
A profundidade de um nó é a distância deste nó até a raiz. Um conjunto de nós com a mesma
profundidade é denominado nível da árvore. A maior profundidade de um nó, é a altura da árvore.
Uma árvore é dita completa se todas as folhas da árvore estão no mesmo nível da árvore.

Definições em Teoria dos Grafos


Em teoria dos grafos, uma árvore binária é definida como um grafo acíclico, conexo, dirigido e que
cada nó não tem grau maior que 3. Assim sendo, só existe um caminho entre dois nós distintos.
E cada ramo da árvore é um vértice dirigido, sem peso, que parte do pai e vai o filho.

Percursos em Árvore.
Existem três tipos de percursos: Percurso em InOrdem, PreOrdem e PosOrdem.

- InOrdem.

Para a árvore acima, o percurso seria: 2, 7, 5, 6, 11, 2, 5, 4 e 9.

- PreOrdem
O algoritmo desse percurso é:

212
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Para a árvore acima, o percurso seria: 2, 7, 2, 6, 5, 11, 5, 9 e 4.

- PosOrdem.
O algoritmo desse percurso é:

Para a árvore acima, o percurso seria: 2, 5, 11, 6, 7, 4, 9, 5 e 2.

Transformação de uma Árvore N-ária.


Uma árvore n-ária qualquer (árvore cujos nós possuem graus menores ou iguais a n) podem ser
representados por uma árvore binária.

Métodos para Representação de Árvores Binárias


Uma das maneiras mais simples de representar árvores binárias em linguagens de programação é por
meio de arranjos unidimensionais (vetores). Dado um nó de índice i qualquer, os seus filhos terão índices
2i + 1 e 2i + 2 e o seu pai terá índice piso((i - 1)/2). Essa implementação é utilizada para representar
árvores completas ou quase completas. Heaps, que são árvores binárias quase completas são
implementadas na forma de um vetor de uma maneira bastante eficiente.
Apesar da simplicidade, essa representação requer uma grande quantidade de memória contígua para
o armazenamento de árvores grandes, o crescimento desta é difícil de implementar e manter e pode
haver grande quantidade de desperdício de memória.

Em uma linguagem que possua suporte a estruturas e referências (por exemplo Pascal e C), as árvores
são implementadas a partir de nós, com um, ou mais, campos para a(s) informação(ões) principal(is) e
dois campos apontadores especiais, denominados esquerda e direita, que fazem referência às sub-
árvores esquerda e direita, respectivamente. Algumas vezes, há um apontador para o pai. Em um nó do
tipo folha, os campos apontadores possuem valores especiais (nil em Pascal e NULL em C).

Algoritmos de Aprendizado Supervisionado e Não-supervisionados55

Supõem a existência de um "Professor" que te ensina que tipo de comportamento você deve exibir em
cada situação. Na prática, imagine que você deseja classificar empresas saudáveis de não-saudáveis e
para fazer isso você tem uma amostra que associa cada empresa saudável uma série de variáveis. Então,

55
http://prorum.com/index.php/1673/diferenca-algoritmos-aprendizado-supervisionado-supervisionado

213
1678859 E-book gerado especialmente para DANIEL CRISTIAN
um algoritmo de aprendizagem supervisionado tentaria usar explicitamente essa informação para no
futuro ser hábil para separar empresas saudáveis de não-saudáveis. O exemplo mais simples de modelo
que é baseado nesse tipo de aprendizagem é o modelo de regressão linear, quando estimado usando
por exemplo a minimização dos erros quadráticos, isto é, a minimação do erro quadrático entre o valor da
variável predita pelo modelo e o valor da variável real.
Problemas de aprendizagem supervisionados são classificados em problemas de “regressão” e
“classificação”. Em um problema de regressão, estamos tentando prever os resultados em uma saída
contínua, o que significa que estamos a tentando mapear variáveis de entrada para alguma função
contínua. Em um problema de classificação, estamos tentando prever os resultados em uma saída
discreta. Em outras palavras, estamos tentando mapear variáveis de entrada em categorias distintas.
Exemplo:
(A) Regressão: dada uma imagem de homem/mulher, temos de prever sua idade com base em dados
da imagem.
(B) Classificação: dada um exemplo de tumor cancerígeno, temos de prever se ele é benigno ou
maligno através do seu tamanho e idade do paciente.

Outro exemplo de Classificação muito utilizado pelos bancos é a decisão do aceite do empréstimo para
algum cliente com base no seu histórico de crédito.
Muitos algoritmos são utilizados para criar aprendizes supervisionadas, sendo os mais comuns as
Redes Neurais, Máquinas de Vetor de Suporte (SVMs), e Classificadores Naive Bayes.
Por outro lado, algoritmos de aprendizagem não supervisionada não supõem a classificação entre as
empresas saudáveis e não saudáveis em sua base de dados. Eles simplesmente tentariam separar as
empresas em questão usando as variáveis associadas as empresas e não necessariamente separariam
as empresas em duas classes. Eles normalmente associam o seu aprendizado a métricas que devem ser
otimizadas.
Outras formas de aprendizagem não supervisionado são baseadas no objetivo de representar um
conjunto de dados através de um modelo de menor dimensão desse conjunto de dados. Um exemplo
interessante desse tipo de aplicação é fornecido pela Análise dos Componentes Principais.
Ainda é possível utilizar aprendizagem não supervisionada para representar um conjunto de dados
através de características interessantes do conjunto de dados.
Com aprendizagem não supervisionada não há feedback com base nos resultados da previsão, ou
seja, não há professor para corrigi-la.
Exemplo:
Não Clustering, é o “Algoritmo Cocktail Party”, que pode encontrar em uma estrutura de dados
desorganizada como identificar as vozes individuais e música.
Abordagens comuns de aprendizagem não supervisionada incluem armazenamento em Cluster K-
Médio, Hierárquico, e Mapas Auto-organizadores.

214
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Um exemplo de projeto que implementa aprendizado de máquina de uma maneira simplista é o Apache
Mahout que tem por objetivo a construção de um ambiente para a criação rápida de aplicações escaláveis
de aprendizado de máquina de alta performance.

6. Ferramentas e Linguagens de Programação para manipulação de dados:


Ansible;

AUTOMAÇÃO E PROVISIONAMENTO ÁGIL COM ANSIBLE

O que é o Ansible?

O Ansible é uma ferramenta open source de automação e provisionamento ágil desenvolvida,


inicialmente, por Michael DeHaan e atualmente mantida pela comunidade e pela Red Hat56. O Ansible é
de fácil aprendizagem e utiliza SSH para se comunicar com os clientes (nodes) não necessitando de
agent; e sua única dependência é ter Python2 instalado no(s) node(s), que já é nativo na maioria das
distribuições Linux.
Com Ansible é possível automatizar tarefas como:
- Instalação e atualização de pacotes;
- Configuração e deploy de aplicações;
- Gerenciamento de usuários;
- E muitas outras tarefas administrativas com ganho enorme de desempenho, velocidade e
produtividade.
A documentação oficial fica aqui.

A Estrutura do Ansible

Inventory: arquivo de inventário no qual serão declarados quais os nós ou hosts-alvos serão
gerenciados pelo Ansible-Server;
Modules: controlam os recursos (serviços, pacotes, arquivos etc) do(s) host(s) remoto(s);

56
https://imasters.com.br/devsecops/automacao-e-provisionamento-agil-com-ansible

215
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Tasks: tarefas que serão executadas no(s) host(s);
Playbooks: conjunto de tarefas escritas em YAML (chave:valor) que serão executadas no(s) host(s).

Características do Ansible

- Escrito em Python, linguagem interpretada de script, imperativa e orientada a objetos;


- Não necessita que agents sejam instalados nos hosts remotos;
- Utiliza SSH para se conectar aos hosts;
- Para os Playbooks utiliza a linguagem YAML, facilmente entendida pelos seres humanos;
- Utiliza linguagem Jinja2 para templates;
- Enorme quantidade de módulos e códigos no Github. Neste site ou pelo comando ansible-galaxy é
possível baixar e reutilizar playbooks escritos por outros usuários.

Instalando o Ansible

O que vamos precisar? Neste exemplo, usei duas máquinas virtuais com Sistema Operacional
Ubuntu16.04 para os nós gerenciados e minha máquina como Ansible-Server. A instalação é
relativamente simples, você precisa instalar somente na máquina que funcionará como ponto central.
Para a instalação do Ansible-Server você precisa seguir os seguintes passos:

APT-Ubuntu

YUM

Debian

PKG

216
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Homebrew

Gerenciando Servidores

O Inventário
O arquivo de hosts de inventário do Ansible é usado para listar e agrupar seus servidores e sua
localização default é /etc/ansible/hosts. Costumo fazer um backup do arquivo default para usá-lo como
referência mais tarde:

Depois de mover o arquivo de inventário de exemplo, crie um novo /etc/ansible/hosts e defina os


servidores que serão gerenciados. Caso queira ter seu arquivo de hosts Ansible em outro local particular,
você pode definir esta variável de ambiente:

Ou pode especificar o local de hosts Ansible ao executar comandos com a flag –inventory-file = (ou -
i):

Aqui vamos definir os dois servidores sob o rótulo “webservers” e um com “local”, para testes locais:

Conectando aos Servidores

Gere a chave no nó master sem ter que digitar uma senha ou para o caso de você ainda não tiver
autenticação via chave ssh, configurada para seus nós filhos:

Após o término das confirmações, teremos dois arquivos:

217
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Em seguida, copie sua chave pública para os servidores com o comando ssh-copy-id:

Executando Comandos

Comandos Ad-Hoc
Assim que tiver um inventário configurado, podemos começar a executar tarefas nos servidores
definidos.
O Ansible assumirá que você tem acesso SSH disponível para seus servidores, normalmente baseado
em SSH-Key. Como o Ansible usa SSH, o servidor em que ele está instalado precisa de acesso aos
servidores do inventário.

A saída que temos do Ansible é JSON, que nos diz se a tarefa fez alguma alteração e qual foi o seu
resultado. Se precisarmos definir o usuário e talvez algumas outras configurações para nos conectar ao
servidor, podemos fazer o uso das seguintes FLAGS:

Nas quais:
All: use todos os servidores definidos a partir do arquivo de inventário;
m ping: use o módulo “ping”, que simplesmente executa o comando e retorna os resultados;
s: use “sudo” para executar os comandos;
k: solicite uma senha ao invés de usar a autenticação baseada em chave;
u: utilize outro usuário.

Modules
Ansible usa “módulos” para realizar a maioria de suas tarefas, como instalar softwares, atualizar
pacotes, copiar arquivos etc.

Nota: perceba que usei o rótulo “local” para limitar a ação feita somente em minha máquina.
Acima, o comando sudo apt-get install nginx foi executado usando o módulo “shell”; o sinalizador -a é
usado para transmitir argumentos para o módulo. Utilizo -s para executar esse comando usando sudo e
se usarmos um módulo mais apropriado, podemos executar comandos com uma garantia de resultado.
Os módulos Ansible asseguram idempotência, ou seja, poderemos executar as mesmas tarefas sem
afetar o resultado final.

218
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Ele fará uso do módulo apt para instalar o Nginx (se não estiver instalado). O resultado da execução
da tarefa foi “changed”: false. Isso mostra que não houve mudanças, pois já havia instalado o Nginx nesta
máquina. Logo, posso executar este comando repetidamente sem me preocupar com ele afetando o
resultado desejado.
Algumas FLAGS:
All: executar em todos os hosts definidos a partir do arquivo de inventário;
s: executar usando o sudo;
m apt: use o módulo apt;
a ‘name=nginx state=instalado’: fornece os argumentos para o módulo apt, incluindo o nome do
pacote e o estado final desejado;
–ask-sudo: pede senha de sudo.

Podemos executar todas as nossas tarefas necessárias (por meio de módulos) da forma ad-hoc, mas
vamos tornar isso mais gerenciável. Vamos mover tudo isso para um Playbook, assim ele vai executar e
coordenar várias tarefas ao mesmo tempo.

Playbook Básico
Os Playbooks podem executar várias Tarefas (TASKS) e oferecer algumas funcionalidades mais
avançadas.
Segue o exemplo de um playbook no qual executo a instalação dos requisitos do Apache LAMP.

Perceba que neste playbook a primeira tarefa declarada foi instalar o php5, apache2, mysql etc. Na
segunda, ele executa o comando que habilita o module rewrite do Apache e por último, reinicia o serviço
Apache.
Para cada tarefa, você pode especificar o grupo-alvo de nós e o usuário remoto para executar a
operação:

219
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Se você precisar executar a tarefa com usuário diferentes, especifique da seguinte forma:

Executar com sudo:

As tarefas do playbook serão executadas em todos os nós declarados no grupo webservers, dentro do
arquivo de inventário.
Para executar o playbook, use o comando:

Dando continuidade ao exemplo anterior, vou colocar os comandos ad-hoc que executamos para
instalar o NGINX dentro de um playbook ordenado em Tasks:
Crie o arquivo nginx.yml com a configuração abaixo:

Esta tarefa faz exatamente o mesmo que o nosso comando ad-hoc, no entanto, escolhi especificar o
meu grupo “local” de servidores em vez de “all” ou “webservers”. Podemos executar o playbook com o
comando ansible-playbook:

220
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Usamos –ask-sudo dizendo ao Ansible para usar sudo pedindo senha e, em seguida, informei o
arquivo Playbook nginx.yml. Costumo adicionar -vvv para visualizar com mais detalhes a execução das
tarefas e seu resultado final. Como vemos acima, ele executou as tarefas com sucesso, porém nada foi
alterado, pois já tenho o Nginx instalado.
A sintaxe do comando para execução dos playbooks é:

Conclusão

Como vimos, o Ansible é uma ótima ferramenta de provisionamento de ambientes, rápida e eficiente.
Existem alguns pontos negativos, como a sintaxe exigente e a identação do seu playbook. No entanto, a
lógica é bastante simples, sua documentação é bem completa e há muitas opções que poderão te ajudar
a construir seus próprios módulos para os playbooks, de acordo com a necessidade.

Java (SE 11 e EE 8);

LINGUAGEM DE PROGRAMAÇÃO JAVA

Uma Breve História do Java57

A Sun criou um time (conhecido como Green Team) para desenvolver inovações tecnológicas em
1992. Esse time foi liderado por James Gosling, considerado o pai do Java. O time voltou com a ideia de
criar um interpretador (já era uma máquina virtual, veremos o que é isso mais a frente) para pequenos
dispositivos, facilitando a reescrita de software para aparelhos eletrônicos, como vídeo cassete, televisão
e aparelhos de TV a cabo.
A ideia não deu certo. Tentaram fechar diversos contratos com grandes fabricantes de eletrônicos,
como Panasonic, mas não houve êxito devido ao conflito de interesses e custos. Hoje, sabemos que o
Java domina o mercado de aplicações para celulares com mais de 2.5 bilhões de dispositivos compatíveis,
porém em 1994 ainda era muito cedo para isso.
Com o advento da web, a Sun percebeu que poderia utilizar a ideia criada em 1992 para rodar
pequenas aplicações dentro do browser. A semelhança era que na internet havia uma grande quantidade
de sistemas operacionais e browsers, e com isso seria grande vantagem poder programar numa única
linguagem, independente da plataforma. Foi aí que o Java 1.0 foi lançado: focado em transformar o
browser de apenas um cliente magro (thin client ou terminal burro) em uma aplicação que possa também
realizar operações avançadas, e não apenas renderizar html.
Os applets deixaram de ser o foco da Sun, e nem a Oracle nunca teve interesse. É curioso notar que
a tecnologia Java nasceu com um objetivo em mente, foi lançado com outro, mas, no final, decolou mesmo
no desenvolvimento de aplicações do lado do servidor. Sorte? Há hoje o Java FX, tentando dar força para
o Java não só no desktop, mas como aplicações ricas na web, mas muitos não acreditam que haja espaço
para tal, considerando o destino de tecnologias como Adobe Flex e Microsoft Silverlight.
Você pode ler a história da linguagem Java em: http://www.java.com/en/javahistory/
E um vídeo interessante: http://tinyurl.com/histjava

57
https://www.caelum.com.br/apostila-java-orientacao-objetos/

221
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Em 2009 a Oracle comprou a Sun, fortalecendo a marca. A Oracle sempre foi, junto com a IBM, uma
das empresas que mais investiram e fizeram negócios através do uso da plataforma Java. Em 2014 surge
a versão Java 8 com mudanças interessantes na linguagem.

Máquina Virtual

Em uma linguagem de programação como C e Pascal, temos a seguinte situação quando vamos
compilar um programa:

O código fonte é compilado para código de máquina específico de uma plataforma e sistema
operacional. Muitas vezes o próprio código fonte é desenvolvido visando uma única plataforma!
Esse código executável (binário) resultante será executado pelo sistema operacional e, por esse
motivo, ele deve saber conversar com o sistema operacional em questão.

Isto é, temos um código executável para cada sistema operacional. É necessário compilar uma vez
para Windows, outra para o Linux, e assim por diante, caso a gente queira que esse nosso software possa
ser utilizado em várias plataformas. Esse é o caso de aplicativos como o OpenOffice, Firefox e outros.
Como foi dito anteriormente, na maioria das vezes, a sua aplicação se utiliza das bibliotecas do sistema
operacional, como, por exemplo, a de interface gráfica para desenhar as "telas". A biblioteca de interface
gráfica do Windows é bem diferente das do Linux: como criar então uma aplicação que rode de forma
parecida nos dois sistemas operacionais?
Precisamos reescrever um mesmo pedaço da aplicação para diferentes sistemas operacionais, já que
eles não são compatíveis.
Já o Java utiliza do conceito de máquina virtual, onde existe, entre o sistema operacional e a aplicação,
uma camada extra responsável por "traduzir" - mas não apenas isso - o que sua aplicação deseja fazer
para as respectivas chamadas do sistema operacional onde ela está rodando no momento:

222
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Dessa forma, a maneira com a qual você abre uma janela no Linux ou no Windows é a mesma: você
ganha independência de sistema operacional. Ou, melhor ainda, independência de plataforma em geral:
não é preciso se preocupar em qual sistema operacional sua aplicação está rodando, nem em que tipo
de máquina, configurações, etc.
Repare que uma máquina virtual é um conceito bem mais amplo que o de um interpretador. Como o
próprio nome diz, uma máquina virtual é como um "computador de mentira": tem tudo que um computador
tem. Em outras palavras, ela é responsável por gerenciar memória, threads, a pilha de execução, etc.
Sua aplicação roda sem nenhum envolvimento com o sistema operacional! Sempre conversando
apenas com a Java Virtual Machine (JVM).
Essa característica é interessante: como tudo passa pela JVM, ela pode tirar métricas, decidir onde é
melhor alocar a memória, entre outros. Uma JVM isola totalmente a aplicação do sistema operacional. Se
uma JVM termina abruptamente, só as aplicações que estavam rodando nela irão terminar: isso não
afetará outras JVMs que estejam rodando no mesmo computador, nem afetará o sistema operacional.
Essa camada de isolamento também é interessante quando pensamos em um servidor que não pode
se sujeitar a rodar código que possa interferir na boa execução de outras aplicações.
Essa camada, a máquina virtual, não entende código Java, ela entende um código de máquina
específico. Esse código de máquina é gerado por um compilador Java, como o javac, e é conhecido por
"bytecode", pois existem menos de 256 códigos de operação dessa linguagem, e cada "opcode" gasta
um byte. O compilador Java gera esse bytecode que, diferente das linguagens sem máquina virtual, vai
servir para diferentes sistemas operacionais, já que ele vai ser "traduzido" pela JVM.
Write once, run anywhere
Esse era um slogan que a Sun usava para o Java, já que você não precisa reescrever partes da sua
aplicação toda vez que quiser mudar de sistema operacional.

Java Lento? Hotspot e JIT

Hotspot é a tecnologia que a JVM utiliza para detectar pontos quentes da sua aplicação: código que é
executado muito, provavelmente dentro de um ou mais loops. Quando a JVM julgar necessário, ela
vai compilar estes códigos para instruções realmente nativas da plataforma, tendo em vista que isso vai
provavelmente melhorar a performance da sua aplicação. Esse compilador é o JIT: Just inTime Compiler,
o compilador que aparece "bem na hora" que você precisa.
Você pode pensar então: porque a JVM não compila tudo antes de executar a aplicação? É que
teoricamente compilar dinamicamente, a medida do necessário, pode gerar uma performance melhor. O
motivo é simples: imagine um .exe gerado pelo VisualBasic, pelo gcc ou pelo Delphi; ele é estático. Ele
já foi otimizado baseado em heurísticas, o compilador pode ter tomado uma decisão não tão boa.
Já a JVM, por estar compilando dinamicamente durante a execução, pode perceber que um
determinado código não está com performance adequada e otimizar mais um pouco aquele trecho, ou
ainda mudar a estratégia de otimização. É por esse motivo que as JVMs mais recentes em alguns casos
chegam a ganhar de códigos C compilados com o GCC 3.x.

Versões do Java e a Confusão do Java2

Java 1.0 e 1.1 são as versões muito antigas do Java, mas já traziam bibliotecas importantes como o
JDBC e o java.io.
Com o Java 1.2 houve um aumento grande no tamanho da API, e foi nesse momento em que trocaram
a nomenclatura de Java para Java2, com o objetivo de diminuir a confusão que havia entre Java e
JavaScript. Mas lembre-se, não há versão "Java 2.0", o 2 foi incorporado ao nome, tornando-se Java2
1.2.
Depois vieram o Java2 1.3 e 1.4, e o Java 1.5 passou a se chamar Java 5, tanto por uma questão de
marketing e porque mudanças significativas na linguagem foram incluídas. É nesse momento que o "2"
do nome Java desaparece. Repare que para fins de desenvolvimento, o Java 5 ainda é referido como
Java 1.5.
Hoje a última versão disponível do Java é a 8.

JVM? JRE? JDK? O Que Devo Baixar?

O que gostaríamos de baixar no site da Oracle?


JVM = apenas a virtual machine, esse download não existe, ela sempre vem acompanhada.

223
1678859 E-book gerado especialmente para DANIEL CRISTIAN
JRE = Java Runtime Environment, ambiente de execução Java, formado pela JVM e bibliotecas, tudo
que você precisa para executar uma aplicação Java. Mas nós precisamos de mais.
JDK = Java Development Kit: Nós, desenvolvedores, faremos o download do JDK do Java SE
(Standard Edition). Ele é formado pela JRE somado a ferramentas, como o compilador.
Tanto o JRE e o JDK podem ser baixados do site http://www.oracle.com/technetwork/java/. Para
encontrá-los, acesse o link Java SE dentro dos top downloads. Consulte o apêndice de instalação do JDK
para maiores detalhes.

Onde Usar e os Objetivos do Java

É preciso ficar claro que a premissa do Java não é a de criar sistemas pequenos, onde temos um ou
dois desenvolvedores, mais rapidamente que linguagens como php, perl, e outras.
O foco da plataforma é outro: aplicações de médio a grande porte, onde o time de desenvolvedores
tem várias pessoas e sempre pode vir a mudar e crescer. Não tenha dúvidas que criar a primeira versão
de uma aplicação usando Java, mesmo utilizando IDEs e ferramentas poderosas, será mais trabalhoso
que muitas linguagens script ou de alta produtividade. Porém, com uma linguagem orientada a objetos e
madura como o Java, será extremamente mais fácil e rápido fazer alterações no sistema, desde que você
siga as boas práticas e recomendações sobre design orientado a objetos.
Além disso, a quantidade enorme de bibliotecas gratuitas para realizar os mais diversos trabalhos (tais
como relatórios, gráficos, sistemas de busca, geração de código de barra, manipulação de XML,
tocadores de vídeo, manipuladores de texto, persistência transparente, impressão, etc.) é um ponto
fortíssimo para adoção do Java: você pode criar uma aplicação sofisticada, usando diversos recursos,
sem precisar comprar um componente específico, que costuma ser caro. O ecossistema do Java é
enorme.
Cada linguagem tem seu espaço e seu melhor uso. O uso do Java é interessante em aplicações que
virão a crescer, em que a legibilidade do código é importante, onde temos muita conectividade e se há
muitas plataformas (ambientes e sistemas operacionais) heterogêneas (Linux, Unix, OSX e Windows
misturados).
Você pode ver isso pela quantidade enorme de ofertas de emprego procurando desenvolvedores Java
para trabalhar com sistemas web e aplicações de integração no servidor.
Apesar disto, a Sun empenhou-se em tentar popularizar o uso do Java em aplicações desktop, mesmo
com o fraco marketshare do Swing/AWT/SWT em relação às tecnologias concorrentes (em especial
Microsoft .NET). A atual tentativa é o Java FX, onde a Oracle tem investido bastante.

Especificação versus Implementação

Outro ponto importante: quando falamos de Java Virtual Machine, estamos falando de uma
especificação. Ela diz como o bytecode deve ser interpretado pela JVM. Quando fazemos o download no
site da Oracle, o que vem junto é a Oracle JVM. Em outras palavras, existem outras JVMs disponíveis,
como a JRockit da BEA (também adquirida pela Oracle), a J9 da IBM, entre outras.
Esse é outro ponto interessante para as empresas. Caso não estejam gostando de algum detalhe da
JVM da Oracle ou prefiram trabalhar com outra empresa, pagando por suporte, elas podem trocar de JVM
com a garantia absoluta de que todo o sistema continuará funcionando. Isso porque toda JVM deve ser
certificada pela Oracle, provando a sua compatibilidade. Não há nem necessidade de recompilar
nenhuma de suas classes.
Além de independência de hardware e sistema operacional, você tem a independência
de vender (fabricante): graças a ideia da JVM ser uma especificação e não um software.

Compilando o Primeiro Programa

Para mostrar uma linha, podemos fazer:


System.out.println("Minha primeira aplicação Java!");
Mas esse código não será aceito pelo compilador Java. O Java é uma linguagem bastante burocrática,
e precisa de mais do que isso para iniciar uma execução. Veremos os detalhes e os porquês durante os
próximos capítulos. O mínimo que precisaríamos escrever é algo como:

224
1678859 E-book gerado especialmente para DANIEL CRISTIAN
class MeuPrograma {
public static void main(String[] args) {
System.out.println("Minha primeira aplicação Java!");
}
}

A numeração das linhas não faz parte do código e não deve ser digitada; é apenas um recurso didático.
O Java é case sensitive: tome cuidado com maiúsculas e minúsculas.
Após digitar o código acima, grave-o como MeuPrograma.java em algum diretório. Para compilar, você
deve pedir para que o compilador de Java da Oracle, chamado javac, gere o bytecode correspondente
ao seu código Java.

Depois de compilar, o bytecode foi gerado. Quando o sistema operacional listar os arquivos contidos
no diretório atual, você poderá ver que um arquivo .class foi gerado, com o mesmo nome da sua classe
Java.
Preciso sempre programar usando o Notepad ou similar?
Não é necessário digitar sempre seu programa em um simples aplicativo como o Notepad. Você pode
usar um editor que tenha syntax highlighting e outros benefícios.
Mas, no começo, é interessante você usar algo que não possua ferramentas, para que você possa se
acostumar com os erros de compilação, sintaxe e outros. É sugerida a utilização do Eclipse
(http://www.eclipse.org), a IDE líder no mercado, e gratuita.
No Linux, recomendamos o uso do gedit, kate e vi. No Windows, você pode usar o Notepad++ ou o
TextPad. No Mac, TextMate, Sublime ou mesmo o vi.

Executando seu primeiro programa


Os procedimentos para executar seu programa são muito simples. O javac é o compilador Java, e o
Java é o responsável por invocar a máquina virtual para interpretar o seu programa.

Ao executar, pode ser que a acentuação resultante saia errada devido a algumas configurações que
deixamos de fazer. Sem problemas.

O que aconteceu?

class MeuPrograma {
public static void main(String[] args) {

// miolo do programa começa aqui!


System.out.println("Minha primeira aplicação Java!!");
// fim do miolo do programa

}
}

O miolo do programa é o que será executado quando chamamos a máquina virtual. Por enquanto,
todas as linhas anteriores, onde há a declaração de uma classe e a de um método, não importam para

225
1678859 E-book gerado especialmente para DANIEL CRISTIAN
nós nesse momento. Mas devemos saber que toda aplicação Java começa por um ponto de entrada, e
este ponto de entrada é o método main.
Ainda não sabemos o que é método, mas veremos no capítulo 4. Até lá, não se preocupe com essas
declarações. Sempre que um exercício for feito, o código que nos importa sempre estará nesse miolo.
No caso do nosso código, a linha do System.out.println faz com que o conteúdo entre aspas seja
colocado na tela.

Para saber mais: como é o bytecode?


O MeuPrograma.class gerado não é legível por seres humanos (não que seja impossível). Ele está
escrito no formato que a virtual machine sabe entender e que foi especificado que ela entendesse.
É como um assembly, escrito para esta máquina em específico. Podemos ler os mnemônicos utilizando
a ferramenta javap que acompanha o JDK:

javap -c MeuPrograma

E a saída:

MeuPrograma();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);


Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String Minha primeira aplicação Java!!
5: invokevirtual #4; //Method java/io/PrintStream.println:
(Ljava/lang/String;)V
8: return

É o código acima, que a JVM sabe ler. É o "código de máquina", da máquina virtual.
Um bytecode pode ser revertido para o .Java original (com perda de comentários e nomes de variáveis
locais). Caso seu software vá virar um produto de prateleira, é fundamental usar um ofuscador no seu
código, que vai embaralhar classes, métodos e um monte de outros recursos.

O que pode dar errado?


Muitos erros podem ocorrer no momento que você rodar seu primeiro código. Vamos ver alguns deles:

Código:

class X {
public static void main (String[] args) {
System.out.println("Falta ponto e vírgula")
}
}

Erro:
X.java:4: ';' expected
}
^
1 error

Esse é o erro de compilação mais comum: aquele onde um ponto e vírgula fora esquecido. Repare
que o compilador é explícito em dizer que a linha 4 é a com problemas. Outros erros de compilação podem
ocorrer se você escreveu palavras chaves (as que colocamos em negrito) em maiúsculas, esqueceu de
abrir e fechar as {}, etc.

226
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Durante a execução, outros erros podem aparecer:
- Se você declarar a classe como X, compilá-la e depois tentar usá-la como x minúsculo (java x), o
Java te avisa:

Exception in thread "main" java.lang.NoClassDefFoundError:


X (wrong name: x)

- Se tentar acessar uma classe no diretório ou classpath errado, ou se o nome estiver errado, ocorrerá
o seguinte erro:

Exception in thread "main" java.lang.NoClassDefFoundError: X


- Se esquecer de colocar static ou o argumento String[] args no método main:

Exception in thread "main" java.lang.NoSuchMethodError: main


Por exemplo:

class X {
public void main (String[] args) {
System.out.println("Faltou o static, tente executar!");
}
}

- Se não colocar o método main como public:

Main method not public.


Por exemplo:
class X {
static void main (String[] args) {
System.out.println("Faltou o public");
}
}

Variáveis Primitivas e Controle de Fluxo

Veremos agora os seguintes recursos da linguagem Java:


- Declaração, atribuição de valores, casting e comparação de variáveis;
- Controle de fluxo através de if e else;
- Instruções de laço for e while, controle de fluxo com break e continue.

Declarando e usando variáveis


Dentro de um bloco, podemos declarar variáveis e usá-las. Em Java, toda variável tem um tipo que
não pode ser mudado, uma vez que declarado:

tipoDaVariavel nomeDaVariavel;

Por exemplo, é possível ter uma idade que guarda um número inteiro:

int idade;

Com isso, você declara a variável idade, que passa a existir a partir daquela linha. Ela é do tipo int,
que guarda um número inteiro. A partir daí você pode usá-la, primeiramente atribuindo valores.
A linha a seguir é a tradução de: "idade deve valer quinze".

idade = 15;

227
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Comentários em Java
Para fazer um comentário em Java, você pode usar o // para comentar até o final da linha, ou então
usar o /* */ para comentar o que estiver entre eles.

/* comentário daqui,
ate aqui */

// uma linha de comentário sobre a idade


int idade;

Além de atribuir, você pode utilizar esse valor. O código a seguir declara novamente a
variável idade com valor 15 e imprime seu valor na saída padrão através da chamada
a System.out.println.

// declara a idade
int idade;
idade = 15;

// imprime a idade
System.out.println(idade);

Por fim, podemos utilizar o valor de uma variável para algum outro propósito, como alterar ou definir
uma segunda variável. O código a seguir cria uma variável chamada idadeNoAnoQueVem com valor
de idade mais um.

// calcula a idade no ano seguinte


int idadeNoAnoQueVem;
idadeNoAnoQueVem = idade + 1;

No mesmo momento que você declara uma variável, também é possível inicializá-la por praticidade:

int idade = 15;

Você pode usar os operadores +, -, / e * para operar com números, sendo eles responsáveis pela
adição, subtração, divisão e multiplicação, respectivamente. Além desses operadores básicos, há o
operador % (módulo) que nada mais é que o resto de uma divisão inteira. Veja alguns exemplos:

int quatro = 2 + 2;
int tres = 5 - 2;

int oito = 4 * 2;
int dezesseis = 64 / 4;

int um = 5 % 2; // 5 dividido por 2 dá 2 e tem resto 1;


// o operador % pega o resto da divisão inteira

Como rodar esses códigos?


Você deve colocar esses trechos de código dentro do bloco main que vimos no capítulo anterior. Isto
é, isso deve ficar no miolo do programa. Use bastante System.out.println, dessa forma você pode ver
algum resultado, caso contrário, ao executar a aplicação, nada aparecerá.
Por exemplo, para imprimir a idade e a idadeNoAnoQueVem podemos escrever o seguinte programa
de exemplo:

class TestaIdade {

public static void main(String[] args) {


// imprime a idade
int idade = 20;
System.out.println(idade);

228
1678859 E-book gerado especialmente para DANIEL CRISTIAN
// gera uma idade no ano seguinte
int idadeNoAnoQueVem;
idadeNoAnoQueVem = idade + 1;

// imprime a idade
System.out.println(idadeNoAnoQueVem);
}
}

Representar números inteiros é fácil, mas como guardar valores reais, tais como frações de números
inteiros e outros? Outro tipo de variável muito utilizado é o double, que armazena um número com ponto
flutuante (e que também pode armazenar um número inteiro).

double pi = 3.14;
double x = 5 * 10;

O tipo boolean armazena um valor verdadeiro ou falso, e só: nada de números, palavras ou endereços,
como em algumas outras linguagens.

boolean verdade = true;


true e false são palavras reservadas do Java. É comum que um booleanseja determinado através de
uma expressão booleana, isto é, um trecho de código que retorna um booleano, como o exemplo:
int idade = 30;
boolean menorDeIdade = idade < 18;

O tipo char guarda um, e apenas um, caractere. Esse caractere deve estar entre aspas simples. Não
se esqueça dessas duas características de uma variável do tipo char! Por exemplo, ela não pode guardar
um código como '' pois o vazio não é um caractere!

char letra = 'a';


System.out.println(letra);

Variáveis do tipo char são pouco usadas no dia a dia. Veremos mais à frente o uso das strings, que
usamos constantemente, porém estas não são definidas por um tipo primitivo.

Tipos primitivos e valores


Esses tipos de variáveis são tipos primitivos do Java: o valor que elas guardam são o real conteúdo da
variável. Quando você utilizar o operador de atribuição = o valor será copiado.

int i = 5; // i recebe uma cópia do valor 5


int j = i; // j recebe uma cópia do valor de i
i = i + 1; // i vira 6, j continua 5

Aqui, i fica com o valor de 6. Mas e j? Na segunda linha, j está valendo 5. Quando i passa a valer 6,
será que j também muda de valor? Não, pois o valor de um tipo primitivo sempre é copiado.
Apesar da linha 2 fazer j = i, a partir desse momento essas variáveis não tem relação nenhuma: o que
acontece com uma, não reflete em nada com a outra.

Outros tipos primitivos


Vimos aqui os tipos primitivos que mais aparecem. O Java tem outros, que são
o byte, short, long e float.
Cada tipo possui características especiais que, para um programador avançado, podem fazer muita
diferença.

Casting e promoção
Alguns valores são incompatíveis se você tentar fazer uma atribuição direta. Enquanto um número real
costuma ser representado em uma variável do tipo double, tentar atribuir ele a uma variável int não
funciona porque é um código que diz: "i deve valer d", mas não se sabe se d realmente é um número
inteiro ou não.

229
1678859 E-book gerado especialmente para DANIEL CRISTIAN
double d = 3.1415;
int i = d; // não compila

O mesmo ocorre no seguinte trecho:

int i = 3.14;

O mais interessante, é que nem mesmo o seguinte código compila:

double d = 5; // ok, o double pode conter um número inteiro


int i = d; // não compila

Apesar de 5 ser um bom valor para um int, o compilador não tem como saber que valor estará dentro
desse double no momento da execução. Esse valor pode ter sido digitado pelo usuário, e ninguém vai
garantir que essa conversão ocorra sem perda de valores.
Já no caso a seguir, é o contrário:

int i = 5;
double d2 = i;

O código acima compila sem problemas, já que um double pode guardar um número com ou sem
ponto flutuante. Todos os inteiros representados por uma variável do tipo int podem ser guardados em
uma variável double, então não existem problemas no código acima.
Às vezes, precisamos que um número quebrado seja arredondado e armazenado num número inteiro.
Para fazer isso sem que haja o erro de compilação, é preciso ordenar que o número quebrado
seja moldado (casted) como um número inteiro. Esse processo recebe o nome decasting.

double d3 = 3.14;
int i = (int) d3;

O casting foi feito para moldar a variável d3 como um int. O valor de iagora é 3.
O mesmo ocorre entre valores int e long.

long x = 10000;
int i = x; // não compila, pois pode estar perdendo informação
E, se quisermos realmente fazer isso, fazemos o casting:
long x = 10000;
int i = (int) x;

- Casos não tão comuns de casting e atribuição


Alguns castings aparecem também:

float x = 0.0;

O código acima não compila, pois, todos os literais com ponto flutuante são considerados double pelo
Java. E float não pode receber um double sem perda de informação, para fazer isso funcionar podemos
escrever o seguinte:

float x = 0.0f;

A letra f, que pode ser maiúscula ou minúscula, indica que aquele literal deve ser tratado como float.
Outro caso, que é mais comum:

double d = 5;
float f = 3;
float x = f + (float) d;

Você precisa do casting porque o Java faz as contas e vai armazenando sempre no maior tipo que
apareceu durante as operações, no caso o double.

230
1678859 E-book gerado especialmente para DANIEL CRISTIAN
E, uma observação: no mínimo, o Java armazena o resultado em um int, na hora de fazer as contas.
Até casting com variáveis do tipo char podem ocorrer. O único tipo primitivo que não pode ser atribuído
a nenhum outro tipo é o boolean.

- Castings possíveis
Abaixo estão relacionados todos os casts possíveis na linguagem Java, mostrando a conversão de um
valor para outro. A indicação Impl. quer dizer que aquele cast é implícito e automático, ou seja, você não
precisa indicar o cast explicitamente (lembrando que o tipo boolean não pode ser convertido para nenhum
outro tipo).

- Tamanho dos Tipos


Na tabela abaixo, estão os tamanhos de cada tipo primitivo do Java.

O if e o else
A sintaxe do if no Java é a seguinte:

if (condicaoBooleana) {
codigo;
}

Uma condição booleana é qualquer expressão que retorne true oufalse. Para isso, você pode usar os
operadores <, >, <=, >= e outros. Um exemplo:

int idade = 15;


if (idade < 18) {
System.out.println("Não pode entrar");
}

Além disso, você pode usar a cláusula else para indicar o comportamento que deve ser executado no
caso da expressão booleana ser falsa:

int idade = 15;


if (idade < 18) {
System.out.println("Não pode entrar");
} else {
System.out.println("Pode entrar");
}

231
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Você pode concatenar expressões booleanas através dos operadores lógicos "E" e "OU". O "E" é
representado pelo && e o "OU" é representado pelo ||.
Um exemplo seria verificar se ele tem menos de 18 anos e se ele não é amigo do dono:

int idade = 15;


boolean amigoDoDono = true;
if (idade < 18 && amigoDoDono == false) {
System.out.println("Não pode entrar");
}
else {
System.out.println("Pode entrar");
}

Esse código poderia ficar ainda mais legível, utilizando-se o operador de negação, o !. Esse operador
transforma o resultado de uma expressão booleana de false para true e vice versa.

int idade = 15;


boolean amigoDoDono = true;
if (idade < 18 && !amigoDoDono) {
System.out.println("Não pode entrar");
}
else {
System.out.println("Pode entrar");
}

Repare na linha 3 que o trecho amigoDoDono == false virou!amigoDoDono. Eles têm o mesmo valor.
Para comparar se uma variável tem o mesmo valor que outra variável ou valor, utilizamos o
operador ==. Repare que utilizar o operador =dentro de um if vai retornar um erro de compilação, já que
o operador = é o de atribuição.

int mes = 1;
if (mes == 1) {
System.out.println("Você deveria estar de férias");
}

O while
O while é um comando usado para fazer um laço (loop), isto é, repetir um trecho de código algumas
vezes. A ideia é que esse trecho de código seja repetido enquanto uma determinada condição
permanecer verdadeira.

int idade = 15;


while (idade < 18) {
System.out.println(idade);
idade = idade + 1;
}

O trecho dentro do bloco do while será executado até o momento em que a condição idade < 18 passe
a ser falsa. E isso ocorrerá exatamente no momento em que idade == 18, o que não o fará imprimir 18.

int i = 0;
while (i < 10) {
System.out.println(i);
i = i + 1;
}

Já o while acima imprime de 0 a 9.

232
1678859 E-book gerado especialmente para DANIEL CRISTIAN
O for
Outro comando de loop extremamente utilizado é o for. A ideia é a mesma do while: fazer um trecho
de código ser repetido enquanto uma condição continuar verdadeira. Mas além disso, o for isola também
um espaço para inicialização de variáveis e o modificador dessas variáveis. Isso faz com que fiquem mais
legíveis, as variáveis que são relacionadas ao loop:

for (inicializacao; condicao; incremento) {


codigo;
}

Um exemplo é o a seguir:

for (int i = 0; i < 10; i = i + 1) {


System.out.println("olá!");
}

Repare que esse for poderia ser trocado por:

int i = 0;
while (i < 10) {
System.out.println("olá!");
i = i + 1;
}

Porém, o código do for indica claramente que a variável i serve, em especial, para controlar a
quantidade de laços executados. Quando usar o for? Quando usar o while? Depende do gosto e da
ocasião.

Pós incremento ++
i = i + 1 pode realmente ser substituído por i++ quando isolado, porém, em alguns casos, temos essa
instrução envolvida em, por exemplo, uma atribuição:
int i = 5;
int x = i++;

Qual é o valor de x? O de i, após essa linha, é 6.


O operador ++, quando vem após a variável, retorna o valor antigo, e incrementa (pós incremento),
fazendo x valer 5.
Se você tivesse usado o ++ antes da variável (pré incremento), o resultado seria 6:

int i = 5;
int x = ++i; // aqui x valera 6

Controlando loops
Apesar de termos condições booleanas nos nossos laços, em algum momento, podemos decidir parar
o loop por algum motivo especial sem que o resto do laço seja executado.

for (int i = x; i < y; i++) {


if (i % 19 == 0) {
System.out.println("Achei um número divisível por 19 entre x e y");
break;
}
}

O código acima vai percorrer os números de x a y e parar quando encontrar um número divisível por
19, uma vez que foi utilizada a palavra chave break.
Da mesma maneira, é possível obrigar o loop a executar o próximo laço. Para isso usamos a palavra-
chave continue.

233
1678859 E-book gerado especialmente para DANIEL CRISTIAN
for (int i = 0; i < 100; i++) {
if (i > 50 && i < 60) {
continue;
}
System.out.println(i);
}

O código acima não vai imprimir alguns números. (Quais exatamente?)

Escopo das variáveis


No Java, podemos declarar variáveis a qualquer momento. Porém, dependendo de onde você as
declarou, ela vai valer de um determinado ponto a outro.

// aqui a variável i não existe


int i = 5;
// a partir daqui ela existe

O escopo da variável é o nome dado ao trecho de código em que aquela variável existe e onde é
possível acessá-la.
Quando abrimos um novo bloco com as chaves, as variáveis declaradas ali dentro só valem até o fim
daquele bloco.

// aqui a variável i não existe


int i = 5;
// a partir daqui ela existe
while (condicao) {
// o i ainda vale aqui
int j = 7;
// o j passa a existir
}
// aqui o j não existe mais, mas o i continua dentro do escopo

No bloco acima, a variável j para de existir quando termina o bloco onde ela foi declarada. Se você
tentar acessar uma variável fora de seu escopo, ocorrerá um erro de compilação.

O mesmo vale para um if:

if (algumBooleano) {
int i = 5;
}
else {
int i = 10;
}
System.out.println(i); // cuidado!

Aqui a variável i não existe fora do if e do else! Se você declarar a variável antes do if, vai haver outro
erro de compilação: dentro do if e do else a variável está sendo redeclarada! Então o código para compilar
e fazer sentido fica:

int i;
if (algumBooleano) {
i = 5;

234
1678859 E-book gerado especialmente para DANIEL CRISTIAN
}
else {
i = 10;
}
System.out.println(i);

Uma situação parecida pode ocorrer com o for:

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


System.out.println("olá!");
}
System.out.println(i); // cuidado!

Neste for, a variável i morre ao seu término, não podendo ser acessada de fora do for, gerando um
erro de compilação. Se você realmente quer acessar o contador depois do loop terminar, precisa de algo
como:

int i;
for (i = 0; i < 10; i++) {
System.out.println("olá!");
}
System.out.println(i);

Um bloco dentro do outro


Um bloco também pode ser declarado dentro de outro. Isto é, um if dentro de um for, ou um for dentro
de um for, algo como:
while (condicao) {
for (int i = 0; i < 10; i++) {
// código
}
}

Métodos58
Em contraste com a estática dos dados, os métodos definem as ações a serem tomadas em diversos
momentos da execução de um programa. Como em outras linguagens, como C, C++, Pascal, Fortran,
etc., os métodos correspondem aos conceitos comuns de funções, procedimentos ou sub-rotinas. Estes
são apenas conjuntos ordenados de declarações de dados, comandos e expressões. Em termos simples,
são os métodos que realizam todas as tarefas para as quais o programa foi escrito, por exemplo, realizar
cálculos, resumir informações de um arquivo, produzir um relatório, criar um gráfico, gerar um filme de
animação, etc.

Classes
Os métodos, assim como os dados, têm um local de residência, as classes. Mais adiante, vamos
estudar as classes em detalhes. Por hora, precisamos apenas de alguns poucos conceitos para poder
entender os métodos. Pensemos uma classe como sendo um conjunto de dados (variáveis) e métodos
(funções) da forma:

class [nome] {
[dados e métodos]
}

Onde [nome] é um identificador que define o nome da classe, e o par de chaves delimita uma região
para declaração de variáveis e métodos. A declaração de variáveis já foi vista anteriormente no capítulo
sobre tipos de dados. Uma classe pode ser privativa ou pública. Uma classe privativa é declarada como
no exemplo acima e é conhecida apenas no escopo delimitado pelo arquivo que a contém. Como um
programa Java pode ser quebrado em múltiplos arquivos de código fonte distintos, pode ser necessário
que as diversas partes integrantes do programa interajam, trocando dados entre si e chamando métodos

58
Fonte: http://www.dm.ufscar.br/

235
1678859 E-book gerado especialmente para DANIEL CRISTIAN
umas das outras. Isso torna-se possível através das classes públicas, as quais são conhecidas por
qualquer arquivo fonte que componha o programa. Para tornar uma classe pública, basta preceder sua
declaração pela palavra-chave public como no seguinte exemplo:

public class [nome da classe] {


[dados e métodos]
}

Há uma convenção em Java que estabelece que deve haver exatamente uma classe pública para cada
arquivo-fonte de que consiste um programa Java, e seu nome deve ser precisamente o nome do arquivo,
sem o sufixo Java. Desse modo, existe uma correspondência biunívoca entre as classes públicas e os
arquivos-fonte que as contém.
Podemos declarar uma classe a partir do chão, com todos os seus dados e métodos, ou podemos
declarar uma classe derivando-a a partir de uma outra já existente. Este é um recurso muito útil, pois
permite aproveitar um esforço de elaboração anterior, aumentando significativamente a produtividade da
programação, e diminuindo o tamanho do código objeto. Suponhamos por exemplo, que tenhamos
declarado previamente a seguinte classe:

class Polígono {
int cx, cy; // Coordenadas do centro do polígono
}

Esta classe define em linhas gerais o que é um polígono, guardando uma única característica comum
a qualquer polígono, isto é, as coordenadas de seu centro. Agora, suponhamos que desejamos criar uma
classe para guardar informações sobre um quadrado. Neste caso, não precisamos criar uma classe que
dê as coordenadas do centro do quadrado assim como as suas dimensões. Basta fazer simplesmente:

class Quadrado extends Polígono {


int lado; // Comprimento de um lado do quadrado
}

A classe quadrado declarada desse modo se diz uma classe derivada da classe Poligono, da qual
herda os dados (e os métodos) nela contidos. Esta declaração é equivalente a

class Quadrado {
int cx, cy; // Coordenadas do centro do polígono
int lado; // Comprimento de um lado do quadrado
}

Desejando fazer uma classe para representar um retângulo, bastaria fazer então:

class Retângulo extends Polígono {


int base, alt; // Base e altura do retângulo
}

Objetos
Uma particular instância de uma classe é chamada objeto. Para entender a diferença entre classes e
objetos, fazemos alusão à metáfora da fábrica de torradeiras. A fábrica de torradeiras não é uma
torradeira, mas define o tipo de produto que sai dela, isto é, as torradeiras. Do mesmo modo a torradeira
não é a fábrica, mas o produto feito por ela. Comparamos as classes às fabricas e os objetos aos produtos
feitos por elas. Grosso modo, podemos dizer que as classes não ocupam espaço na memória, por serem
abstrações, enquanto que, os objetos ocupam espaço de memória por serem concretizações dessas
abstrações.
Nas declarações acima, introduzimos algumas classes que permitem representar polígonos. Porém,
não instanciamos nenhuma das classes criando particulares objetos a partir dessas classes. Por exemplo,
a partir da classe quadrado, podemos fazer objetos representando quadrados de diversos comprimentos
laterais, ou retângulos de diferentes dimensões:

236
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Quadrado A, B, C;
Retângulo D;
A.lado = 1; // O quadrado A terá os lados de comprimento 1
B.lado = 2; // O quadrado B terá os lados de comprimento 2
C.lado = 7; // O quadrado C terá os lados de comprimento 7
D.base = 3; // O retangulo D terá base 3 e ...
D.alt = 4; // altura 4, e centrado na origem
D.cx = 0;
D.cy = 0;

As declarações acima são semelhantes às que vimos no capítulo anterior, com exceção de que no
lugar do nome que identifica o tipo de dado estamos usando o nome de uma classe. Neste exemplo, as
classesQuadrado e Retângulo foram empregadas para declarar os objetos (ou variáveis) A, B, C e D.
Em certo sentido as classes complementam os tipos de dados nativos da linguagem Java, com tipos
de dados complexos criados pelo programador. Esse fato, aliado à possibilidade de derivar classes,
tornam as linguagens orientadas a objetos extremamente producentes.

Chamando métodos
Um método entra em ação no momento em que é chamado. Isto pode ocorrer explicitamente ou
implicitamente. A chamada explícita se dá por ordem do programador através da execução de um
comando ou expressão contendo o nome do método. Em nosso programa AloPessoal fizemos uma
chamada explícita do método System.out.println para mostrar um texto na tela do computador. As
chamadas implícitas ocorrem quando o interpretador Java chama um método por sua própria deliberação.
A chamada do método main é um exemplo de chamada implícita. O interpretador Java chama esse
método para dar início à execução do programa.
Declarando métodos
A declaração mais simples que podemos fazer de um método (lembrando que isso deve ser feito dentro
de uma classe) é a seguinte:

void [nome do método] () {


[corpo do método]
}

onde o [nome do método] é um identificador que define o nome pelo qual o método é conhecido,
e [corpo do método] consiste de uma lista ordenada de declaração de variáveis, de expressões e de
comandos. A primeira palavra-chave, void, define o valor retornado pelo método, neste caso, nenhum.
Podemos usar qualquer tipo de dado válido como valor de retorno de um método. Nesse caso, ao
terminar, o método seria obrigado a devolver um dado do tipo especificado. Por exemplo,

class Numero {

double x = 1;
void print() {
System.out.println("O valor e " + x);
}

Define uma classe chamada Numero, a qual contém uma variável x, inicializada com 1, e um método
sem valor de retorno, print, que apenas escreve um texto e o valor de x, através da chamada do
métodoSystem.out.println.
O par de parênteses adiante do nome do método introduz uma lista (vazia, neste caso) de argumentos.
A chamada de um método pode ser acrescida de parâmetros, os quais são associados aos seus
respectivos argumentos.
Um exemplo de métodos que retornam valores é o seguinte:

237
1678859 E-book gerado especialmente para DANIEL CRISTIAN
class Calculador {
int Soma(int a, int b) {
return a + b;
}
double Produto(double a, double b) {
return a * b;
}
}

O primeiro método, Soma, realiza a adição de dois números inteiros fornecidos pelos
argumentos a e b, devolve a soma valor de retorno. O segundo método realiza a multiplicação de dois
números de ponto-flutuante a e b devolvendo seu produto.
A sintaxe completa para a declaração de um método é a seguinte:

[moderadores de acesso] [modificador] [tipo do valor de retorno] [nome] ([argumentos])


throws [lista de exceções]
{ [corpo]
}

Onde os termos em itálico são opcionais (ou acessórios). Neste capítulo, vamos analisar
detalhadamente cada um dos termos [moderadores de acesso], [modificador], [tipo do valor de retorno],
e [argumentos]. Vamos, porém, adiar um pouco as explicações sobre [lista de exceções] até o capítulo
sobre exceções. Uma exceção à esta sintaxe é a que se aplica aos métodos especiais, chamados
construtores, que serão vistos adiante no capítulo sobre classes.

Moderadores de acesso
Os moderadores de acesso são empregados para restringir o acesso a um método. Entretanto,
independentemente do moderador escolhido, um método é sempre acessível, isto é, pode ser chamado,
a partir de qualquer outro método contido na mesma classe. Os moderadores de acesso existentes em
Java são os seguintes:
public: o método declarado com este moderador é público e pode ser chamado a partir de métodos
contidos em qualquer outra classe. Esta é a condição de menor restrição possível.
protected: o método é protegido pode ser chamado por todas as classes que compõe um conjunto
maior chamado package. Veremos adiante que as packages são um modo conveniente de organizar
diversas classes que estão em estreito relacionamento.

Observação: é importante avisar que você pode ter problemas em identificar violações com respeito
à chamada de métodos protegidos. Isso se deve ao fato do compilador não sinalizar esse fato
precisamente, isto é, a tentativa de chamar um método protegido a partir de uma classe que não faz parte
do package. Ao invés disso a mensagem poderá se parecer com a seguinte:

No method matching funcao() found in class Matematica

friendly: uso do método é permitido dentro da classe que o contém, assim como dentro de qualquer
classe que tenha sido derivada dessa classe, ainda que esteja fora do package. Este é o moderador
default, isto é, aquele que é assumido se nenhum moderador for explicitamente especificado.
private: o método é privativo da classe que o contém e seu uso é vedado a qualquer outra classe.
private protected: o método é acessível pela classe que o contém, assim como por qualquer classe
que tenha sido derivada dessa classe. Porém, isso somente é permitido apenas dentro de um mesmo
arquivo-fonte.

Exemplo:

// classe de numero
class numero {
double x=1;
public void print1() {
System.out.println("O valor e "+x);
}

238
1678859 E-book gerado especialmente para DANIEL CRISTIAN
void print2() {
System.out.println("O valor e "+x);
}
}
// classe principal
public class PrintNum {
public static void main(String args[]) {
numero num = new numero();
num.print1(); // correto
num.print2(); // ilegal
}
}

O exemplo acima dará um erro, pois não pode acessar o print2. O métodoprint1 é definido
como public e, portanto, está disponível a qualquer classe, mas o print2 não tem especificação (logo, é
assumido como friendly) e, portanto, a classe principal PrintNum não pode acessa-lo.

Modificador do método
O modificador do método permite especificar algumas propriedades de um método, determinando
como classes derivadas podem ou não redefinir ou alterar o método, e de que forma esse método será
visível.
static: o método é compartilhado por todos os objetos instanciados a partir da mesma classe. Um
método static não pode acessar qualquer variável declarada dentro de uma classe (salvo se a variável
estiver declarada também como static, o que veremos mais adiante), pois não é capaz de discernir entre
os diferentes objetos que compartilham esse método.
abstract: podemos declarar um método sem, contudo, especificar seu corpo, dizendo-o abstrato. Isto
funciona como uma espécie de lembrete para que alguma classe derivada complete a declaração
fornecendo um corpo. Assim sendo, uma classe que contenha um método abstrato, ou que seja derivada
de alguma classe que contenha um método abstrato, mas não complete sua declaração, não pode ser
instanciada. O uso da palavra-chave abstract é opcional, isto é, as duas declarações seguintes são
equivalentes:

abstract void print();


void print();

final: especifica que nenhuma classe derivada pode alterar ou redefinir este método. Um método
declarado como final deve ser obrigatoriamente seguido de um corpo.
native: declara apenas o cabeçalho sem corpo, como no caso deabstract. Porém, esta especificação
designa um método que implementado em outra linguagem como C++, por exemplo.
synchronized: esta declaração é usada para desenvolver o programa de processamento concorrente.
Seu propósito é impedir que dois métodos executando concorrentemente acessem os dados de uma
classe ao mesmo tempo. synchromized especifica que se um método estiver acessando o dado, outros
que também desejem acessá-lo têm que esperar.

- Tipo de Valor de Retorno


O tipo de valor de retorno é especificado por uma palavra chave ou nome de classe na declaração do
método, estabelecendo o valor que ele pode devolver.

// Classe de números complexos


class Complexo {
double x, y; // parte real e complexo, respectivamnte
public double Re() { // retorna a parte real
return x;
}
public double Im() { /retorna a parte imaginaria
return y;
}
public Complexo Vezes(Complexo c) {

239
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Complexo resultado;
resultado.x = x * c.x - y * c.y;
resultado.y = x * c.y + y * c.x;
return resultado;
}
public void print() {
System.out.println("(" + x + " + " + y + "i)");
}
}
public class Teste {
public static void main(String args[]) {
Complexo z, w; z.x = 1;
z.y = 2;

System.out.print( "O valor de z é ");


z.print();
System.out.println( "A parte real de z é = " + z.Re() );
System.out.println( "A parte imaginária de z é = ", z.Im() );
System.out.print("O valor de z ao quadrado é ");
w = z.Vezes( z );
w.print();
}
}

Ao executar esse programa teríamos a resposta:

O valor de z é (1 + 2i)
A parte real de z é = 1
A parte imaginária de z é = 2
O valor de z ao quadrado é (-3 + 4i)

Um método que retorna valor, isto é, não declarado como void, deve conter a linha return ...; a qual
especifica o valor a ser retornado. Por exemplo, return x; especifica que o valor da variável x será
retornado.

- Lista de Argumentos:
A lista de argumentos é a lista de valores que o método vai precisar, obedecendo a sintaxe
[tipo 1] [nome 1], [tipo 2] [nome 2], ...
onde [tipo ?] é a especificação do tipo de dado e [nome ?] é um identificador pelo qual o parâmetro é
conhecido.
Exemplo:

// Classe de numeros complexos


class Complexo {
double x=1, y=1; /* parte real e complexo, respectivamnte
public double Re() { // retorna a parte real
return x;
}
public double Im() { /retorna a parte imaginaria
return y;
}
public set(double a, b){
x = a;
y = b;
}

240
1678859 E-book gerado especialmente para DANIEL CRISTIAN
public class Teste {

public static void main(String args[]) {


Complexo c = new Complexo();
c.set(3,2);
System.out.println("z = (" + c.Re() + " + " + c.Im() + "i)");
}
}

Uma observação importante é que Java trata o objeto por referência e por isso, se o método modificar
o objeto recebido como parâmetro, o objeto será modificado externamente. No entanto, se o parâmetro
recebido for tipo simples (int, double, char, float, etc.), visto no tipo de dados, o método pode alterar o
parâmetro a vontade que não influencia no valor externo.
Para ilustrar, veja o exemplo a seguir:

// classe de inteiro
class inteiro {
public int i;
}
// classe principal
public class TestPar {
// método que altera o valor do parametro int
// este metodo deve ser static, pois sera chamado
// pelo metodo main() que e static
static void MudaInt(int k) {
System.out.println("MudaInt: valor de k e " + k);
k += 2;
System.out.println("MudaInt: valor de k e apos incremento e " + k);
}
// método que altera o valor do parametro inteiro
// este metodo deve ser static, pois sera chamado
// pelo metodo main() que e static
static void MudaInteiro(inteiro k) {
System.out.println("MudaInteiro: valor de k e " + k.i);
k.i += 2;
System.out.println("MudaInteiro: valor de k e apos incremento e " + k.i);
}
// main() do TestPar
public static void main(String args[]) {
int i;
inteiro n = new inteiro();
i = 2;
n.i = 3;
System.out.println("Valores originais:");
System.out.println("Valor de i e "+i);
System.out.println("Valor de n e "+n.i);
MudaInt(i);
MudaInteiro(n);
System.out.println("Valores apos a chamada dos metodos:"):
System.out.println("Valor de i e "+i);
System.out.println("Valor de n e "+n.i);
}
}

A especificação public de uma variável dentro da classe, faz com que esta variável seja acessada de
qualquer lugar. Para especificar a variável i do objeto k, basta escrever k.i. Em geral, a
variável [var] dentro do objeto[obj] do tipo classe e referenciado por [obj].[var]. Note o uso de
especificação static para os métodos MudaInt() e MudaInteiro(). Esta especificação é necessária, pois o

241
1678859 E-book gerado especialmente para DANIEL CRISTIAN
método principal é static, e método static não pode chamar outros métodos da mesma classe que não
seja static.
O exemplo acima fornece a saída:

Valores originais:
Valor de i e 2
Valor de n e 3
Valores apos a chamada dos métodos:
Valor de i e 2
Valor de n e 5

Classes59
Usamos as classes para construir objetos, o que é chamado deinstanciação. E os objetos consistem
em a essência da programação orientada a objetos (ou OOP, do inglês Object-Oriented Programming).
Falando intuitivamente, as classes consistem de uma maneira de organizar um conjunto de dados, e
designar todos os métodos necessários para usar ou alterar esses dados.
O conjunto de todos os dados contidos em uma classe definem o estado de um objeto. Por exemplo,
se tivéssemos uma classe Semáforo contendo uma única variável chamada VermelhoVerdeAmarelo,
então o estado de Semaforo é determinado pelo valor da de VermelhoVerdeAmarelo.

public class Semaforo {


int VermelhoVerdeAmarelo = 0; // 0=vermelho,1=verde,2=amarelo
void Alternar() {
VermelhoVerdeAmarelo = ++VermelhoVerdeAmarelo % 3;
}
}

Os métodos de uma classe, por sua vez, determinam a utilidade que uma classe terá. No caso da
classe Semaforo, seu único método Alternar tem como função provocar a mudança da luz de vermelho a
verde, de verde a amarelo e de amarelo a vermelho, respectivamente, em cada nova chamada. Assim,
se o método Alternar for chamado em intervalos de tempo regulares, poderemos utilizar o estado da
classe Semaforo para controlar um semáforo com luzes reais.
Para distinguir entre variáveis declaradas em classes daquelas declaradas localmente dentro de
métodos, comumente nos referimos àquelas como campos. Assim, dizemos
que VermelhoVerdeAmareloé um campo da classe Semaforo.

Herança
Contudo, uma das maiores vantagens da OOP reside na possiblidade de haver herança entre classes.
Esta consiste na capacidade de construir novas classes a partir de outras existentes. Nesse processo, os
dados e métodos de uma classe existente, chamada parente (ou superclass), são herdados pela nova
classe, chamada subclasse, ou classe derivada.

Encapsulamento
Outro benefício importante da OOP reside no chamado encapsulamento. Este consiste na habilidade
de efetivamente isolar informações do restante do programa. Isto traz uma série de vantagens. Uma vez
concluída uma classe intricada, por exemplo, é virtualmente possível esquecermos suas complicações
internas, passando a tratá-la através de seus métodos. Ainda que mais tarde seja preciso realizar
mudanças significativas no interior de uma classe, não será necessário modificar o restante do programa.
Outro benefício desta prática é garantir que a informação não será corrompida acidentalmente pelo resto
do programa. Criamos, assim, programas mais robustos e confiáveis.

Polimorfismo
Finalmente, uma característica importante das classes reside no fato de que as subclasses de uma
dada classe são consideradas do mesmo tipo de seu parente. Isto é chamado polimorfismo. Este permite
a realização de uma mesma operação sobre diferentes tipos de classes, desde que mantenham algo em
comum. Por exemplo, considere a classe Polígono e suas derivadas Retângulo e Triângulo declaradas
abaixo. Apesar de retângulos e triângulos serem diferentes, eles ainda são considerados polígonos.

59
Fonte: http://www.dm.ufscar.br/

242
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Assim, qualquer coisa que fosse permitido fazer com uma instância (i.é, um objeto) da classe Polígono,
seria também permitida para a instâncias das classes Retângulo e Triângulo. O seguinte exemplo ilustra
o polimorfismo entre essas classes permitindo que se desenhe um polígono, independentemente do
prévio conhecimento de que se trata de um retângulo ou de um triângulo.

class Vértice {
public double x, y;
Vértice( double x, double y ) {
this.x = x;
this.y = y;
}
}

class Polígono {
int numVértices; // Quantidade de vértices do polígono
Vértice v[]; // Coordenadas de seus vértices
}

class Gráfico {
// Desenha o polígono no dispositivo gráfico
void Desenhar(Polígono p) {
SaídaGráfica g; // saída gráfica com método DesenhaLinha.
Vértice anterior;
anterior = p.v[0];
// Une os vértices do polígono desenhando uma linha entre eles
for(int i=1; i<numVértices; i++) {
g.DesenhaLinha( anterior, p.v[i] );
anterior = p.v[i];
}
}
}

class Retângulo extends Polígono {


Retângulo(Vértice v[]) {
this.v = new Vértice[4];
numVértices = 4;
for(int i=0; i<4; i++)
this.v[i] = v[i];
}
}

class Triângulo extends Polígono {


Triângulo(Vértice v[]) {
this.v = new Vértice[3];
numVértices=3;
for(int i=0; i<3; i++)
this.v[i] = v[i];
}
}

public class polimorfismo {


// Coordenadas dos vértices de um retângulo
static Vértice v[] = {
new Vértice(0.0,0.0), new Vértice(2.0,0.0),
new Vértice(2.0,1.0), new Vértice(0.0,1.0)
};
// Coordenadas dos vértices de um triângulo
static Vértice w[] = {

243
1678859 E-book gerado especialmente para DANIEL CRISTIAN
new Vértice(-1.0,0.0), new Vértice(1.0,0.0),
new Vértice(0.0,1.0)
};

public static void main(String args[]) {


Polígono r, t;
Gráfico g = new Gráfico();
r = new Retângulo(v); // Isto é válido, pois Retângulo é um Polígono
t = new Triângulo(w); // Isto é válido, pois Triângulo é um Polígono
// Desenha o retângulo
g.Desenhar( r );
// Desenha o triângulo
g.Desenhar( t );
}
}

Algumas partes deste código são novidade, como por exemplo os métodos declarados nas
classes Vértice, Retângulo e Polígono, que parecem não obedecer às regras estabelecidas no capítulo
sobre métodos.

Declarando uma classe


A forma geral da declaração de uma classe é a seguinte:

[modificadores] class [nome classe] extends [nome super]


implements [nome interface]

Onde as partes que aparecem em itálico são opcionais. Como podemos notar, há quatro diferentes
propriedades de uma classe definidas por essa declaração:
- Modificadores.
- Nome de classe.
- Super classes.
- Interfaces.

Vamos ver em detalhes cada uma destas propriedades a seguir.

- Modificadores
Os modificadores de uma classe determinam como uma classe será manipulada mais tarde no
decorrer do desenvolvimento do programa. Estes são muito parecidos com os moderadores de acesso
introduzidos anteriormente no capítulo sobre métodos.
Ao declarar uma nova classe, é possível especificar um dos seguintes
modificadores: public, final, abstract.
public: permite definir classes públicas. Estas classes são acessíveis a partir de qualquer objeto,
independentemente do package. Uma classe pública deve ser a única classe desse tipo no arquivo em
que está declarada e o nome do arquivo deve ser igual ao da classe.
friendly: se nenhum modificador de classe for especificado, então a classe será considerada friendly.
Apenas os objetos integrantes do mesmo package podem utilizar uma classe friendly.
final: uma classe final pode ser instanciada, mas não pode ser derivada, isto é, não pode ser
superclasse de nenhuma subclasse. Algumas classes predefinidas no ambiente Java têm esta
propriedade. Outras não, como as classes no java.awt, por exemplo.
abstract: classes abstratas são aquelas que contém ao menos um método incompleto. Desse modo
uma classe abstrata não pode ser instanciada, mas pode ser derivada. Neste caso, a subclasse deve
prover o corpo do método para que possa ser instanciada. Isto é muito útil quando desejamos definir em
uma classe regras gerais para o comportamento de uma parte do programa, para que, mais tarde, as
regras mais específicas sejam introduzidas por subclasses.

- Nome de classe
Como qualquer identificador em Java, o nome de uma classe deve obedecer às seguintes regras:
Iniciar com uma letra, ou um dos caracteres: '$', '_'.
Conter somente caracteres Unicode considerados letras, dígitos ou um dos dois caracteres acima.

244
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Não pode ser igual a uma palavra-chave reservada pela linguagem Java, tal
como void, int, for, while, etc.
Lembre-se: as letras maiúsculas e as minúsculas são consideradas diferentes.

- Super classes
Um dos aspectos mais importantes da OOP é a capacidade de usar campos e métodos de uma classe
previamente construída. Por meio da extensão de classes simples podemos construir classes maiores,
acrescentando àquelas mais campos e métodos, obtendo com isto mais funcionalidades. Neste processo,
há uma grande economia no esforço de codificação. Sem esse recurso, frequentemente seria necessário
recodificar grande parte dos programas para acrescentar-lhes funcionalidade ou fazer modificações
significativas.
Ao derivar uma classe, estamos primeiramente fazendo uma cópia da classe parente. É exatamente
isto que obtemos se deixarmos vazio o corpo da subclasse. Tal classe se comportaria exatamente como
sua superclasse. Entretanto, podemos acrescentar novos campos e métodos à subclasse, além de
sobrepor métodos existentes na superclasse, declarando-os exatamente como na superclasse, exceto
por dar um corpo diferente.
Não existe herança múltipla em Java. E contraste com a linguagem C++, em Java somente é possível
derivar uma classe a partir de uma outra, e não de várias.

- Construtores
Os construtores são métodos muito especiais, a começar pela sua sintaxe declarativa, e também por
suas propriedades e finalidade únicas. Por exemplo, o construtor da classe Vértice vista acima é o
seguinte:

Vértice( double x, double y ) {


this.x = x;
this.y = y;
}

Sua única finalidade é inicializar o objeto com um par de coordenadas fornecidas no momento da
instanciação. Aliás, esta é a principal finalidade dos construtores: atribuir a um objeto um estado inicial,
apropriado ao processamento subsequente.
Os construtores são métodos facilmente identificáveis pois têm o mesmo nome da classe. Além disso,
os construtores não especificam nenhum valor de retorno, mesmo que este seja void, uma vez que não
são chamados como os outros métodos. Os construtores somente podem ser chamados no momento da
instanciação. Por exemplo:

Vértice v = new Vértice(1.0, 2.0);

Temos neste trecho de código a instanciação da classe Vértice, que ocorre no momento em que
reservamos espaço para conter um novo objeto dessa classe. Nesse momento o construtor Vértice é
chamado com os argumentos 1.0 e 2.0.
É usual declarar os construtores como públicos. Isto porque, se eles tiverem um nível de acesso inferior
ao da classe propriamente dita, outra classe será capaz de declarar uma instância dessa classe, mas não
será capaz de realizar ela mesma a instanciação, isto é, não poderá usar o operador new para essa
classe. Há situações, porém, em que essa característica é desejável. Deixando seus construtores como
privativos, permite a outras classes usar métodos estáticos, sem permitir que elas criem instâncias dessa
classe.
Uma classe pode múltiplos construtores declarados, desde que cada um tenha lista de argumentos
distinta dos demais. Isto é muito útil, pois em determinados contextos do programa um objeto deve ser
inicializado de uma maneira particular em relação a outros contextos possíveis.
Quando nenhum construtor é declarado explicitamente, um construtor vazio é provido implicitamente.
Por exemplo, se não tivéssemos especificado um construtor na classe Vértice, este seria o construtor
default:

245
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Vértice() {
}

Os construtores não podem ser declarados com os modificadores: native, abstract, static, synchronized
ou final.

- Sobreposição
Não é permitido declarar em uma mesma classe dois métodos com o mesmo nome e mesma lista de
argumentos. De fato, isto parece não fazer nenhum sentido, pois os métodos são unicamente identificados
pelo nome e pela lista de argumentos que os acompanha. Se isso fosse permitido haveria uma grande
confusão, pois como é que se poderia determinar precisamente qual método chamar?
Entretanto, uma das finalidades de permitir a derivação de classes é atribuir a elas novas
funcionalidades. Isto é possível acrescentando-se novos métodos às subclasses. Mas também é possível
sobrepor qualquer dos métodos existentes na superclasse, declarando o novo método na subclasse
exatamente com o mesmo nome e lista de argumentos, como consta na superclasse. Por exemplo,
considere a classe Computador abaixo:

class Computador {
private boolean ligado = true;
public void Desligar() {
ligado = false;
}
}

Esta classe permite que o computador seja desligado, através da chamada do método Desligar.
Porém, isto pode não ser muito seguro, pois poderíamos desligar o computador mesmo quando ele estiver
executando algum programa. Nesse caso, podemos evitar uma catástrofe derivando a classe computador
do seguinte modo:

class ComputadorSeguro extends Computador {


private boolean executando = true;
public void Desligar() {
if ( executando )
System.out.println("Há programas rodando. Não desligue!");
else
ligado = false;
}
}

Agora, um objeto da classe ComputadorSeguro somente será desligado quando não tiver programas
rodando (exceto quando alguém acidentalmente chutar o fio da tomada!).
A sobreposição somente acontece quando o novo método é declarado com exatamente o mesmo
nome e lista de argumentos que o método existente na superclasse. Além disso, a sobreposição não
permite que o novo método tenha mais proteções do que o método original. No exemplo acima, como o
método Desligar foi declarado como public na superclasse, este não pode ser declarado private na
subclasse.

- Instanciando uma classe


Uma classe define um tipo de dado. Ela não pode ser usada a não ser que seja instanciada. A exemplo
dos tipos de dados primitivos os quais somente podem ser usados quando uma variável de um
determinado tipo é declarada, devemos criar uma instância. Uma instância é um objeto do tipo definido
pela classe. Qualquer classe (desde que não seja abstract) pode ser instanciada como qualquer outro
tipo de dado da linguagem Java. O trecho de código abaixo exibe uma classe chamada Geometria criando
um a instância da classe Vértice:

public class Geometria {


Vértice v = new Vértice(1.2, 3.5);
...
}

246
1678859 E-book gerado especialmente para DANIEL CRISTIAN
A diferença mais evidente entre a declaração de um objeto de uma classe e a declaração de um dado
primitivo reside na necessidade de reservar memória para o objeto através do uso do operador new. Na
verdade, esse operador realiza uma série de tarefas:
Reserva espaço para a instância da classe Vértice, o qual deve ser suficiente para conter seu estado,
isto é, os valores dos seus campos.
Realiza a chamada do método construtor.
Retorna uma referência para o novo objeto (o qual é atribuído à variável v).
Outra importante diferença entre objetos e dados de tipo primitivo é que estes são sempre
referenciados por valor, enquanto aqueles são sempre referenciados por meio de sua referência. Isto tem
impacto significativo na maneira como os objetos são passados como parâmetros na chamada de
métodos. Se o método realizar internamente alguma modificação no objeto que foi passado, essa
modificação refletirá no objeto original. Isto não ocorre com a passagem de dados de tipo primitivo.
Referindo-se às partes de uma classe
Após instanciar uma classe é desejável podermos acessar algum de seus campos ou então algum de
seus métodos. Dentro de uma classe os campos e métodos são acessíveis imediatamente pelo nome.
Repare como na classeComputador acima o método Desligar acessa diretamente o campo ligado,
simplesmente por meio do seu nome.
Entretanto, considere a seguinte classe chamada CPD a qual contém várias instâncias da
classe Computador:

public class CPD {


Computador Gauss = new Computador(),
Davinci = new Computador(),
Fermat = new Computador();
...
public void Fechar() {
Gauss.Desligar();
Davinci.Desligar();
Fermat.Desligar();
}
...
O método Fechar realiza o desligamento de cada particular instância da
classe Computador chamando seu método Desligar. Para indicar a qual objeto o método se refere,
devemos precedê-lo do nome do objeto seguido de um operador ponto '.'. A notação geral é:

[nome da instância].[nome do método ou variável]


Uma exceção a essa regra aplica-se à referência de campos ou métodos declarados como static. Tais
declarações são compartilhadas por todas as instâncias de uma classe, desse modo não fornecemos o
nome de uma particular instância, mas o nome da própria classe ao referenciá-los.

- A especificação this
Vimos acima como fazer referências a partes de classes. Mas, e se desejássemos fazer referência a
partes da própria classe? Isso parece evidente, porém, às vezes, o nome de um argumento ou variável
declarada por um método pode coincidir com o nome de um campo da classe. Veja o exemplo da
classe Vértice. Nessa classe o método construtor declara dois argumentos x e y, os quais têm o mesmo
nome dos campos x e y da classe. Esse método distingue os argumentos dos campos pelo uso da
especificação this. Assim this.x e this.y referem-se aos campos x e y declarados na classe,
enquanto x e y propriamente ditos referem-se aos argumentos do construtor. A palavra this substitui uma
referência à própria classe.
Há basicamente duas situações em que devemos empregar a palavra this:
- Quando houver duas variáveis com mesmo nome numa mesma classe - uma pertencendo à classe
e outra pertencendo a algum dos métodos da classe. Nesse caso, apenas esse método específico requer
o uso do this se quiser fazer referência ao campo da classe.
- Quando uma classe precisa passar uma referência de si própria a um método. Vamos ter a
oportunidade de explorar este aspecto quando estivermos estudando os applets.

247
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- A especificação super
A palavra super provê acesso a partes de uma superclasse a partir de uma subclasse. Isto é muito útil
quando estamos sobrepondo um método. Poderíamos reescrever o método Desligar da classe
ComputadorSeguro do seguinte modo:

class ComputadorSeguro extends Computador {


private boolean executando = true;
public void Desligar() {
if ( executando )
System.out.println("Há programas rodando. Não desligue!");
else
super.Desligar();
}
}

Note a chamada super.Desligar(). Esta corresponde a chamada do método Desligar declarado na


superclasse Compudador, o qual vai efetivamente ajustar o campo ligado para o valor false. Imaginando
que o método Desligar fosse muito mais complicado, não precisaríamos recodificá-lo completamente na
subclasse para acrescentar a funcionalidade que permite o desligamento apenas quando o computador
estiver desocupado. Basta chamá-lo da maneira prescrita.
E se o método que desejamos chamar é um construtor? Bem, nesse caso a chamada usando a
palavra super bem particular. Examinemos o seguinte exemplo de uma classe
chamada VérticeNumerado que estende a classeVértice, acrescentando às coordenadas do vértice um
rótulo numérico que o identifica visualmente:

class VérticeNumerado extends Vértice {


int numero;
VérticeNumerado( int numero, int x, int y ) {
this.numero = numero;
super(x, y);
}
}

Note que a chamada super(x, y) se traduz na chamada do construtorVértice(x,y) da superclasse. Com


isto, evitamos de ter que recodificar no novo construtor as tarefas contidas no construtor da superclasse:
basta chamá-lo. Vale observar que esse tipo de chamada também só é permitido de dentro de um
construtor.

- Campos e variáveis locais


Chegou o momento de discutirmos sobre a forma como as variáveis são declaradas dentro de um
programa. Podemos declarar variáveis de classe, chamadas campos, e variáveis de métodos,
ditas locais.
A esta altura, já devemos ter feito a seguinte indagação: se os campos são acessíveis por qualquer
dos métodos declarados em uma classe e eventualmente por métodos de classes derivadas, por que não
declararmos todos os dados empregados por uma classe como campos? Uma resposta imediata a essa
pergunta seria: isso provocaria um significativo desperdício de memória, pois os campos existem durante
todo período de existência de um objeto. Entretanto, os dados declarados localmente por um método
existem somente enquanto esse método estiver sendo executado, de modo que o espaço de memória
previamente ocupado por eles é reaproveitado quando o método termina sua execução.
A capacidade de acessar uma variável de uma classe depende fundamentalmente de duas coisas:
moderadores de acesso e localização da variável dentro da classe. As variáveis locais somente são
acessíveis pelo método que as declara, enquanto que os campos dependem dos moderadores. Apesar
de ser possível deixar todos os campos de uma classe publicamente acessíveis, isto não é recomendável.
Do contrário estaríamos desperdiçando o sofisticado mecanismo de proteção de dados fornecido pela
OOP, o qual permite escrever programas mais robustos e livres de erros (vulgarmente chamados bugs).
Os possíveis moderadores empregados na declaração de campos são os seguintes:
friendly: todos os campos são friendly por default. Isto significa que são acessíveis por outras classes
integrantes do mesmo package e não são acessíveis por nenhuma classe ou subclasse exterior ao
package.

248
1678859 E-book gerado especialmente para DANIEL CRISTIAN
public: idêntico ao moderador de acesso dos métodos. O campo é acessível a partir de qualquer outra
classe, independentemente do package.
protected: os campos protected podem ser acessados a partir de qualquer classe derivada da classe
atual, mas não são acessíveis de fora do package.
private: é o maior grau de proteção. Um campo private é acessível unicamente pela classe atual.
private protected: a combinação dos moderadores private e protected estabelece que o campo seja
acessível pela classe atual e por suas subclasses.
static: um campo static é compartilhado por todas as instâncias de uma classe, isto é, há um único
valor para esse campo, independentemente da quantidade de instâncias existentes, mesmo que não haja
nenhuma.
final: um modificador final precedendo um campo declara esse campo como uma constante. Seu valor
não pode mudar durante a execução do programa. Por isso, é necessário que haja uma inicialização de
campo. Por exemplo:

final int MaxDimen = 10;

O uso de constantes dentro de um programa torna-o mais facilmente legível e fácil de seguir. Para
economizar memória, é recomendável também declarar constantes como static.

- Classes Especiais
A linguagem Java provê algumas classes básicas para formar uma base sólida para todas as demais
classes, inclusive aquelas criadas pelo programador. Dentre essas classes, seguramente as mais
importantes são as classes Object, Class e String.

- A classe Object
A classe Object é uma classe que serve de superclasse para todas as classes existentes em Java.
Isso significa que ao criar uma classe, se não for especificada nenhuma superclasse após a
palavra extends, então a classe Object será assumida automaticamente como superclasse. Portanto toda
classe é subclasse de Object, e com isso herda alguns métodos automaticamente. Um método muito
interessante presente na classe Object é o equals. Suponha que haja duas instâncias de uma mesma
classe e desejamos testar se elas contêm a mesma informação. O operador == nos daria o valor true
apenas se seus operandos forem precisamente o mesmo objeto. Porém, o operador equals nos diria
quando os objetos contêm o mesmo estado, através da comparação campo-a-campo. Por exemplo, eu e
você podemos ter carro do mesmo modelo. Nesse caso meuCarro == seuCarro seria false pois embora
nossos carros sejam do mesmo modelo, são carros diferentes.
Entretanto, meuCarro.equals(seuCarro) poderia sertrue se os atributos de ambos os carros fossem
idênticos, por exemplo, mesmo ano, mesma cor, etc.
Um outro método interessante da classe Object é o método getClass, que retorna uma referência a
um objeto contendo informações sobre a classe a que é aplicado. Isto será visto logo abaixo.

- A classe Class
A classe Class contém informações que descrevem uma classe em Java. Toda classe em Java tem
uma correspondente instância da classe Class. É possível obter informações contidas nessas instâncias
por um dos seguintes meios:
Usar o método getClass de um objeto para obter uma referência à respectiva instância da
classe Class. Exemplo:

Vértice v = new Vértice(1.0, 2.0);


Class cv = v.getClass();

Usar o método estático forName de Class para obter uma instância de Class usando o nome da classe.
Por exemplo:

Class cv = Class.forName("Vértice");

De posse de uma instância da classe Class, podemos obter informações interessantes sobre a classe
da qual ela provém. Por exemplo:
Obter o nome da classe. Isto é muito útil quando lidamos com polimorfismo.

249
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Polígono p;
...
p = new Retângulo( ... );
...
System.out.println("O polígono é um " + p.getClass().getName() );
deverá exibir na tela a mensagem:
O poligono é um Retângulo
Obter o nome da superclasse imediatamente adjacente.
Retângulo r;
...
System.out.println("A classe parente do objeto é " +
r.getClass().getSuperClass().getName() );
deverá exibir na tela a mensagem:
A classe parente do objeto é Polígono
Outra possibilidade interessante do uso da classe Class está na instanciação dinâmica de objetos:
Polígono p;
String nome;
System.out.print("Qual o poligono que deseja criar?");
System.out.flush();
nome = System.in.read();
p = (Polígono) Class.forName(nome).newInstance();

Paralelizando Tarefas com os Diferentes Recursos do Java

A plataforma Java disponibiliza diversas APIs para implementar o paralelismo desde as suas primeiras
versões, e estas vem evoluindo até hoje, trazendo novos recursos e frameworks de alto nível que auxiliam
na programação60. É importante, também, conhecer os conceitos desse tipo de programação e boas
práticas no desenvolvimento voltado para esse cenário.
O processamento paralelo, ou concorrente, tem como base um hardware multicore, onde dispõe-se de
vários núcleos de processamento. Estas arquiteturas, no início do Java, não eram tão comuns. No
entanto, atualmente já se encontram amplamente difundidas, tanto no contexto comercial como
doméstico. Diante disso, para que não haja desperdício desses recursos de hardware e possamos extrair
mais desempenho do software desenvolvido, é recomendado que alguma técnica de paralelismo seja
utilizada.
Como sabemos, existem diversas formas de criar uma aplicação que implemente paralelismo, formas
estas que se diferem tanto em técnicas como em tecnologias empregadas. Em vista disso, no decorrer
deste artigo serão contextualizadas as principais APIs Java, desde as threads “clássicas” a modernos
frameworks de alto nível, visando otimizar a construção, a qualidade e o desempenho do software.

Arquitetura multicore
Uma arquitetura multicore consiste em uma CPU que possui mais de um núcleo de processamento.
Este tipo de hardware permite a execução de mais de uma tarefa simultaneamente, ao contrário das
CPUs singlecore, que eram constituídas por apenas um núcleo, o que significa, na prática, que nada era
executado efetivamente em paralelo.
A partir do momento em que se tornou inviável desenvolver CPUs com frequências (GHz) mais altas,
devido ao superaquecimento, partiu-se para outra abordagem: criar CPUs multicore, isto é, inserir vários
núcleos no mesmo chip, com a premissa base de dividir para conquistar.
Ao contrário do que muitos pensam, no entanto, os processadores multicore não somam a capacidade
de processamento, e sim possibilitam a divisão das tarefas entre si. Deste modo, um processador de dois
núcleos com clock de 2.0 GHz não equivale a um processador com um núcleo de 4.0 GHz. A tecnologia
multicore simplesmente permite a divisão de tarefas entre os núcleos de tal forma que efetivamente se
tenha um processamento paralelo e, com isso, seja alcançado o tão almejado ganho de performance.
Contudo, este ganho é possível apenas se o software implementar paralelismo. Neste contexto, os
Sistemas Operacionais, há anos, já possuem suporte a multicore, mas isso somente otimiza o
desempenho do próprio SO, o que não é suficiente. O ideal é cada software desenvolvido esteja apto a
usufruir de todos os recursos de hardware disponíveis para ele.

60
https://www.devmedia.com.br/threads-paralelizando-tarefas-com-os-diferentes-recursos-do-java/34309

250
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Ademais, considerando o fato de que hoje já nos deparamos com celulares com processadores de
quatro ou oito núcleos, os softwares a eles disponibilizados devem estar preparados para lidar com esta
arquitetura. Desde um simples projeto de robótica a um software massivamente paralelo para um
supercomputador de milhões de núcleos, a opção por paralelizar ou não, pode significar a diferença entre
passar dias processando uma determinada tarefa ou apenas alguns minutos.

Multitasking
O multitasking, ou multitarefa, é a capacidade que sistemas possuem de executar várias tarefas ou
processos ao mesmo tempo, compartilhando recursos de processamento como a CPU. Esta habilidade
permite ao sistema operacional intercalar rapidamente os processos ativos para ocuparem a CPU, dando
a impressão de que estão sendo executados simultaneamente, conforme a figura abaixo.
No caso de uma arquitetura singlecore, é possível executar apenas uma tarefa por vez. Mas com o
multitasking esse problema é contornado gerenciando as tarefas a serem executadas através de uma fila,
onde cada uma executa por um determinado tempo na CPU. Nos sistemas operacionais isto se chama
escalonamento de processos.

Processos executando em um núcleo.

Em arquiteturas multicore, efetivamente os processos podem ser executados simultaneamente,


conforme a Figura 2, mas ainda depende do escalonamento no sistema operacional, pois geralmente
temos mais processos ativos do que núcleos disponíveis para processar.

Arquitetura multicore executando processos.

Desta forma, mais núcleos de processamento significam que mais tarefas simultâneas podem ser
desempenhadas. Contudo, vale ressaltar que isto só é possível se o software que está sendo executado
sobre tal arquitetura implementa o processamento concorrente. De nada adianta um processador de oito
núcleos se o software utiliza apenas um.

Multithreading
De certo modo, podemos compreender multithreading como uma evolução do multitasking, mas em
nível de processo. Ele, basicamente, permite ao software subdividir suas tarefas em trechos de código

251
1678859 E-book gerado especialmente para DANIEL CRISTIAN
independentes e capazes de executar em paralelo, chamados de threads. Com isto, cada uma destas
tarefas pode ser executada em paralelo caso haja vários núcleos, conforme demonstra a figura abaixo.

Figura 3. Processo executando várias tarefas.

Diversos benefícios são adquiridos com este recurso, mas, sem dúvida, o mais procurado é o ganho
de performance. Além deste, no entanto, também é válido destacar o uso mais eficiente da CPU. Sabendo
dessa importância, nosso próximo passo é entender o que são as threads e como criá-las para subdividir
as tarefas do software.

Threads
Na plataforma Java, as threads são, de fato, o único mecanismo de concorrência suportado. De forma
simples, podemos entender esse recurso como trechos de código que operam independentemente da
sequência de execução principal. Como diferencial, enquanto os processos de software não dividem um
mesmo espaço de memória, as threads, sim, e isso lhes permite compartilhar dados e informações dentro
do contexto do software.
Cada objeto de thread possui um identificador único e inalterável, um nome, uma prioridade, um
estado, um gerenciador de exceções, um espaço para armazenamento local e uma série de estruturas
utilizadas pela JVM e pelo sistema operacional, salvando seu contexto enquanto ela permanece pausada
pelo escalonador.
Na JVM, as threads são escalonadas de forma preemptiva seguindo a metodologia “round-robin”. Isso
quer dizer que o escalonador pode pausá-las e dar espaço e tempo para outra thread ser executada,
conforme a figura abaixo. O tempo que cada thread recebe para processar se dá conforme a prioridade
que ela possui, ou seja, threads com prioridade mais alta ganham mais tempo para processar e são
escalonadas com mais frequência do que as outras.

Escalonamento de threads, modo round-robin.

Também é possível observar na Figura 4 que apenas uma thread é executada por vez. Isto
normalmente acontece em casos onde só há um núcleo de processamento, o software implementa um
sincronismo de threads que não as permite executar em paralelo ou quando o sistema não faz uso de

252
1678859 E-book gerado especialmente para DANIEL CRISTIAN
threads. Na figura abaixo, por outro lado, temos um cenário bem diferente, com várias threads executando
paralelamente e otimizando o uso da CPU.

Escalonamento de threads no modo round-robin implementando paralelismo.

Desde seu início a plataforma Java foi projetada para suportar programação concorrente. De lá para
cá, principalmente a partir da versão 5, foram incluídas APIs de alto nível que nos fornecem cada vez
mais recursos para a implementação de tarefas paralelas, como as APIs presentes nos pacotes
java.util.concurrent.*.
Saiba que toda aplicação Java possui, no mínimo, uma thread. Esta é criada e iniciada pela JVM
quando iniciamos a aplicação e sua tarefa é executar o método main() da classe principal. Ela, portanto,
executará sequencialmente os códigos contidos neste método até que termine, quando a thread encerrará
seu processamento e a aplicação poderá ser finalizada.
Em Java, existem basicamente duas maneiras de criar threads:
- Estender a classe Thread (java.lang.Thread); e
- Implementar a interface Runnable (java.lang.Runnable).

Na Listagem 1, de forma simples e objetiva, é apresentado um exemplo de como implementar uma


Thread para executar uma subtarefa em paralelo. Para isso, primeiramente é necessário codificar um
Runnable, o que pode ser feito diretamente na criação da Thread, como demonstrado na Listagem 1, ou
implementar uma classe própria que estenda Runnable. Posteriormente, basta executá-lo com um objeto
Thread através do método start().
Listagem 1. Exemplo de thread implementando a interface Runnable.

Neste exemplo pode-se observar também o código utilizado para buscar alguns dados da thread atual,
tais como ID, nome, prioridade, estado e até mesmo capturar o código que ela está executando. Além de
tais informações que podem ser capturadas, é possível manipular as threads utilizando alguns dos
seguintes métodos:
- O método estático Thread.sleep(), por exemplo, faz com que a thread em execução espere por um
período de tempo sem consumir muito (ou possivelmente nenhum) tempo de CPU;

253
1678859 E-book gerado especialmente para DANIEL CRISTIAN
- O método join() congela a execução da thread corrente e aguarda a conclusão da thread na qual
esse método foi invocado;
- Já o método wait() faz a thread aguardar até que outra invoque o método notify() ou notifyAll(); e
- O método interrupt() acorda uma thread que está dormindo devido a uma operação de sleep() ou
wait(), ou foi bloqueada por causa de um processamento longo de I/O.

A forma clássica de se criar uma thread é estendendo a classe Thread, como demonstrado na Listagem
2. Neste código, temos a classe Tarefa estendendo a Thread. A partir disso, basta sobrescrever o método
run(), o qual fica encarregado de executar o código da thread.
Na prática, nossa classe Tarefa é responsável por realizar o somatório do intervalo de valores recebido
no momento em que ela é criada e armazená-lo em uma variável para que possa ser lido posteriormente.
Listagem 2. Código da classe Tarefa estendendo a classe Thread.

Listagem 3. Código da classe Exemplo, utiliza a classe Tarefa.

254
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Para testarmos o paralelismo com a classe da Listagem 2, criamos a classe Exemplo com o método
main(), responsável por executar o programa (vide Listagem 3). Neste exemplo, após criar as threads,
chama-se o método start() de cada uma delas, para que iniciem suas tarefas. Logo após, em um bloco
try-catch, temos a invocação dos métodos join(). Este faz com que o programa aguarde a finalização de
cada thread para que depois possa ler o valor totalizado por cada tarefa.
Observe, na Listagem 3, que cada tarefa recebe seu intervalo de valores a calcular, sendo somado,
ao todo, de 0 a 3000, mas e se tivéssemos uma única lista de valores que gostaríamos de somar para
obter o valor total? Neste caso, as threads precisariam concorrer pela lista. Isso é o que chamamos de
concorrência de dados e geralmente traz consigo diversos problemas.

JAVA EE E FRAMEWORKS61

A linguagem Java apresenta uma série de conhecidos frameworks, que auxiliam no desenvolvimento
de sistemas. Dentre os mais conhecidos, estão o Struts, Hibernate, JUnit, entre outros.
Há inúmeras definições a respeito de framework. Podemos considerar um framework como uma
solução para um conjunto de problemas em comum, com uso de classes e interfaces, que disponibilizam
objetos com capacidade de capturar funcionalidades comuns a várias aplicações. Assim sendo, um
framework pode ser considerado sob certo ponto de vista como uma solução quase completa.
É muito comum haver uma certa confusão entre o que é um framework e uma "simples" biblioteca de
classes. Vale ressaltar que em uma biblioteca de classes, cada classe é única e independente de outras
classes. Já em um framework, existe uma certa dependência entre as classes, conhecido como modelo
de colaboração.
Também pode haver confusão junto a definição de Design Patterns. Apenas lembrando que em um
framework possuímos código em si, enquanto nos design patterns temos apenas um "modelo" ou
"exemplo" de uma solução para um conhecido problema.
Assim sendo, um framework deve ser extensível, bem documentado e, principalmente, reusável. Vale
lembrar que o uso de frameworks tem benefícios claros quando tratamos de redução de custos.
Também pode haver confusão junto a definição de Design Patterns. Apenas lembrando que em um
framework possuímos código em si, enquanto nos design patterns temos apenas um "modelo" ou
"exemplo" de uma solução para um conhecido problema.
Assim sendo, um framework deve ser extensível, bem documentado e, principalmente, reusável. Vale
lembrar que o uso de frameworks tem benefícios claros quando tratamos de redução de custos.

Introdução ao JSF

Durante muitos anos, os usuários se habituaram com aplicações Desktop. Este tipo de aplicação é
instalada no computador local e acessa diretamente um banco de dados ou gerenciador de arquivos. As
tecnologias típicas para criar uma aplicação Desktop são Delphi, VB (Visual Basic) ou, no mundo Java,
Swing.
Para o desenvolvedor, a aplicação Desktop é construída com uma série de componentes que a
plataforma de desenvolvimento oferece para cada sistema operacional. Esses componentes ricos e
muitas vezes sofisticados estão associados a eventos ou procedimentos que executam lógicas de
negócio.
Problemas de validação de dados são indicados na própria tela sem que qualquer informação do
formulário seja perdida. De uma forma natural, esses componentes lembram-se dos dados do usuário,
inclusive entre telas e ações diferentes.
Nesse tipo de desenvolvimento são utilizados diversos componentes ricos, como por exemplo,
calendários, menus diversos ou componentes drag and drop (arrastar e soltar). Eles ficam associados a
eventos, ou ações, e guardam automaticamente seu estado, já que mantêm os valores digitados pelo
usuário.
Esses componentes não estão, contudo, associados exclusivamente ao desenvolvimento de
aplicações Desktop. Podemos criar a mesma sensação confortável para o cliente em uma aplicação web,
também usando componentes ricos e reaproveitáveis.

61 Fonte: http://www.linhadecodigo.com.br/artigo/758/o-universo-dos-frameworks-java.aspx

255
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Desenvolvimento Desktop ou Web?
Existem algumas desvantagens no desenvolvimento desktop. Como cada usuário tem uma cópia
integral da aplicação, qualquer alteração precisaria ser propagada para todas as outras máquinas.
Estamos usando um cliente gordo, isto é, com muita responsabilidade no lado do cliente.
Note que, aqui, estamos chamando de cliente a aplicação que está rodando na máquina do usuário.
Para piorar, as regras de negócio rodam no computador do usuário. Isso faz com que seja muito mais
difícil depurar a aplicação, já que não costumamos ter acesso tão fácil à máquina onde a aplicação está
instalada. Em geral, enfrentamos problemas de manutenção e gerenciabilidade.

O Desenvolvimento Web e o Protocolo HTTP


Para resolver problemas como esse, surgiram as aplicações baseadas na web. Nessa abordagem há
um servidor central onde a aplicação é executada e processada e todos os usuários podem acessá-la
através de um cliente simples e do protocolo HTTP.
Um navegador web, como Firefox ou Chrome, que fará o papel da aplicação cliente, interpretando
HTML, CSS e JavaScript -- que são as tecnologias que ele entende.
Enquanto o usuário usa o sistema, o navegador envia requisições (requests) para o lado do servidor
(server side), que responde para o computador do cliente (client side). Em nenhum momento a aplicação
está salva no cliente: todas as regras da aplicação estão no lado do servidor. Por isso, essa abordagem
também foi chamada de cliente magro (thin client).
Isso facilita bastante a manutenção e a gerenciabilidade, pois temos um lugar central e acessível onde
a aplicação é executada. Contudo, note que será preciso conhecer HTML, CSS e JavaScript, para fazer
a interface com o usuário, e o protocolo HTTP para entender a comunicação pela web. E, mais importante
ainda, não há mais eventos, mas sim um modelo bem diferente orientado a requisições e respostas. Toda
essa base precisará ser conhecida pelo desenvolvedor.
Comparando as duas abordagens, podemos ver vantagens e desvantagens em ambas. No lado da
aplicação puramente Desktop, temos um estilo de desenvolvimento orientado a eventos, usando
componentes ricos, porém com problemas de manutenção e gerenciamento. Do outro lado, as aplicações
web são mais fáceis de gerenciar e manter, mas precisamos lidar com HTML, conhecer o protocolo HTTP
e seguir o modelo requisição/resposta.

Mesclando Desenvolvimento Desktop e Web


Em vez de desenvolver puramente para desktop, é uma tendência mesclar os dois estilos,
aproveitando as vantagens de cada um. Seria um desenvolvimento Desktop para a web, tanto central
quanto com componentes ricos, aproveitando o melhor dos dois mundos e abstraindo o protocolo de
comunicação. Essa é justamente a ideia dos frameworks web baseados em componentes.
No mundo Java há algumas opções como JavaServer Faces (JSF), Apache Wicket, Vaadin, Tapestry
ou GWT da Google. Todos eles são frameworks web baseados em componentes.

Características do JSF
JSF é uma tecnologia que nos permite criar aplicações Java para Web utilizando componentes visuais
pré-prontos, de forma que o desenvolvedor não se preocupe com Javascript e HTML. Basta adicionarmos
os componentes (calendários, tabelas, formulários) e eles serão renderizados e exibidos em formato html.

Guarda o Estado dos Componentes

256
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Além disso o estado dos componentes é sempre guardado automaticamente (como veremos mais à
frente), criando a característica Stateful. Isso nos permite, por exemplo, criar formulários de várias páginas
e navegar nos vários passos dele com o estado das telas sendo mantidos.

Separa as Camadas
Outra característica marcante na arquitetura do JSF é a separação que fazemos entre as camadas de
apresentação e de aplicação. Pensando no modelo MVC, o JSF possui uma camada de visualização bem
separada do conjunto de classes de modelo.

Especificação: Várias Implementações


O JSF ainda tem a vantagem de ser uma especificação do Java EE, isto é, todo servidor de aplicações
Java tem que vir com uma implementação dela e há diversas outras disponíveis.
A implementação mais famosa do JSF e também a implementação de referência, é a Oracle Mojarra
disponível em http://javaserverfaces.java.net/. Outra implementação famosa é a MyFaces da Apache
Software Foundation em http://myfaces.apache.org/.

JSF 1.x e 2.x


62

Desempenho
JSF sempre foi considerado um framework com problemas graves de desempenho desde suas
primeiras versões (RI 1.0/1.1). No entanto, após o lançamento do JSF1.2 muitos dos problemas de
performance foram resolvidos graças ao talentoso time de desenvolvedores da Mojarra (RI). Mas ainda
assim, devido a limitações de design na especificação do JSF1.2, o time de desenvolvedores sempre teve
dificuldades para escrever código performático. Somente com a especificação do JSF2 é que tornou-se
possível escrever código desde o ínicio com performance em mente.
A extensibilidade e as novas features (como Partial State Saving, Tree Visiting e Project Stage) do
JSF2 tem permitido melhorias significantes de desempenho a cada release. Melhorias como o menor
consumo de memória (até 4x menos) e cpu, menor latência, melhor gerenciamento do estado de cada
componente, otimização no algoritmo de busca de componentes e de serialização da árvore de
componentes, cache de EL expressions e outros.Indo mais além, é possível que o JSF2.2 (ou um futuro
release) adote uma configuração que vá em direção contrária à natureza Stateful do framework, o
Stateless Mode. Com este modo stateless não haverá a necessidade de criar a árvore de componentes
a cada request (teremos um cache de cada view), o que diminuirá o uso de memoria e cpu e trará um
ganho de até 30x no número de requisições por segundo em uma página complexa.

Melhorias na Especificação
São muitos os pontos de melhoria, mas gostaremos de ressaltar alguns, principalmente no que
concerne à evolução/futuro da plataforma. Ficamos muito tempo parados no JSF1.2 e não vimos
progresso na especificação. Contudo, parece que o jogo mudou. Alguns itens que ficaram em aberto no

62 Fonte: http://blog.caelum.com.br/10-razoes-para-migrar-sua-aplicacao-para-jsf-2/

257
1678859 E-book gerado especialmente para DANIEL CRISTIAN
JSF2.0, como a falta de integração do @ViewScope com a combinação JSF + CDI, já estão previstos
para serem resolvidos no JSF2.2, assim como melhorias no suporte ao HTML5, AJAX e navegação com
o Faces Flow e outros itens resolvidos já no JSF2.1.

Mais Componentes!
O número de componentes tem crescido a cada dia devido a facilidade de se implementar
componentes no JSF2. Os principais conjuntos de componentes (Primefaces, Richfaces, Trinidad e
Icefaces) já possuem um excelente suporte a nova especificação e cada um deles possui diferentes
sabores de componentes ricos para praticamente todos os tipos de aplicação. Devido a features como
Ajax Nativo e Resource Loading estes mesmos conjuntos de componentes tornaram-se compatíveis, o
que tem possibilitado integrá-los sem muitas dificuldades em um mesmo projeto – o que era quase
impossível com JSF1.2.
Outro ponto interessante é que com o sucesso do JSF2 as empresas mantenedoras tem diminuído o
suporte e a implementação de novos componentes JSF1.2, e, provavelmente, em médio prazo será ainda
mais raro obter correções e melhorias para esses componentes de forma gratuita.

Adoção do Primefaces 2.x e 3.x

No tópico anterior ressaltamos sobre a variedade de componentes, nesse ousamos afirmar que o
Primefaces (2.x/3.x) está tão interessante que seu uso por si só já é um motivo para migrar. Eles sairam
na frente do RichFaces como implementação de componentes compatível com JSF 2. Só esse aspecto
já seria o suficiente para ter uma adoção maior. Entretanto, o principal motivo foi a qualidade dos
componentes. Comparando as demos de ambos constate-se que o primeiro possui uma variedade muito
maior de elementos, além de suporte com componentes mobile. Ainda mais, ele usa o ThemeRoller,
facilitando a customização de acordo com sua necessidade.
Outro ponto que podemos citar é a evolução. Enquanto o Primefaces já está na 3.X (segunda geração
compatível com JSF 2), o RichFaces ainda está na 4.X (primeira geração compatível com a tecnologia).
Você pode conferir diversas comparações que o pessoal no mercado fez, inclusive destacando alguns
pontos de desempenho, para ajudar em suas conclusões.

Maturidade
A adoção do JavaEE 6 pode ser considerada um sucesso, fato esse pois discussões antigas sobre
JavaEE vs Spring voltaram, e antigamente a especificação perdia fácil (quem não se lembra daquela
MundoJ – EJB X Spring). Atualmente o Java EE tem uma boa relação com outros frameworks, como o
Spring.

Adoção do CDI
CDI é a especificação para Injeção de Dependência (JSR-299). Surgiu também com o JavaEE 6 e foi
prontamente adotada pela comunidade, inclusive integrando com linguagens como Ruby (projeto
TorqueBox). Já teve inclusive alguns estudos para identificar o desempenho de aplicações que usam a
API e concluem como a implementação de referência Weld evoluiu e ainda tem a evoluir. Com a
especificação conseguimos algumas manipulações bem avançadas como, por exemplo, injeção de
dependência para objetos genéricos.

Envolvimento da Comunidade
Há muito movimento ao redor do JSF2 e do CDI. Recentemente foi lançada pelo Bauke Scholtz (talvez
o developer mais proeminente na plataforma) o OmniFaces, que é uma biblioteca utilitária para consertar
vários problemas que ainda não foram melhorados na especificação. Temos também outros projetos
mainstream como o Seam3, CDISource e Apache CODI. Há um claro sombreamento de funcionalidades
entre os projetos, porém a cooperação dos times está tão grande que todos resolveram juntar forças em
um só projeto, o DeltaSpike. O projeto agora é o foco da comunidade, e para comprovar isso, Pete Muir
recentemente enviou um email na lista do seam-dev explicando que o projeto está a pleno vapor.

Suporte ao HTML5
Há muitas funcionalidades interessantes na especificação e ver o JSF tentar se alinhar mostra cada
vez mais a preocupação da tecnologia em melhorar a User Experience.

258
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Fim do Suporte à Versão Antiga
Aqui entra uma questão mais enterprise. Algumas empresas contratam Oracle, JBoss, IBM, justamente
pelo suporte que elas oferecem aos seus produtos. Portanto, é importante identificar a data de expiração
desses serviços. Uma outra preocupação é com relação a variedade de implementações para a
especificação JavaEE. Alguns vendors demoraram a soltar os seus releases compatíveis, mas temos até
alguns menos conhecidos que estão certificados, como Apache Geronimo e Caucho Resign. Há também
melhorias consideráveis de desempenho no JBoss 7 e GlassFish 3.1.

Enterprise Java Beans (EJB 3.x)


63

Enterprise JavaBeans são usados para o desenvolvimento e implantação de aplicações distribuídas


baseadas em componentes que são escaláveis, transacionais, e seguros. Um EJB normalmente contém
a lógica de negócio que atua sobre os dados de negócio. Essa afirmação não era verdadeira até a
introdução dos POJOs que também estão disponíveis nessa nova versão do EJB.
O EJB 3.2 também permite que toda parte envolvendo segurança e transação possa ser especificados
na forma de anotações de metadados, ou então separadamente no Deployment Descriptor que é o
arquivo web.xml.
Uma instância de um bean é gerenciado em tempo de execução por um container. O bean é acessado
no cliente e é intermediado pelo container que ele está implantado. O cliente deste bean também pode
estar no servidor na forma de um Manage Bean, bean CDI, ou um Servlet. Em qualquer um dos casos, o
container EJB fornece toda infra-estrutura necessária para essas aplicações empresariais. O container
permite que o desenvolvedor de aplicações foque na lógica de negócio e não se preocupe com transações
de baixo nível, detalhes de gerenciamento de status, comunicação remota, concorrência, multithreading,
pools de conexão ou outras APIs complexas de baixo nível.
Existem dois tipos de beans, são eles: os Session beans e Message-driven beans. Eles formam o
coração do EJB e serão mais detalhados no restante do artigo, assim como diversas outras funcionalidade
providas pelo EJB 3.2.
Entity beans foram marcados como “anulados” na versão anterior (EJB 3.1) da especificação do EJB
e agora está como opcional no EJB 3.2. Isso ocorre porque a Java Persistence API ganhou a sua própria
especificação e pode ser executada fora de um container.

Beans com Estado de Sessão (Stateful Session Beans)


Um bean com estado de sessão (stateful session bean) tem como característica manter o estado
conversacional para um cliente específico. O estado é armazenado nos valores das variáveis de instância
do bean e nos interceptadores associados.
Podemos definir um simples bean com estado de sessão usando a anotação @Stateful. Segue na
imagem abaixo um exemplo de como podemos definir um bean com estado de sessão.

Exemplo de um bean com estado de sessão utilizando anotações.

63 Fonte: http://www.devmedia.com.br/ejb-introducao-ao-novo-enterprise-javabeans-3-2/30807

259
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Acima temos um POJO marcado com a anotação @Stateful. Apenas anotando o nosso POJO já é o
suficiente para convertermos um POJO para um bean com estado de sessão. Lembrando que os POJOs
(Plain Old Java Objects ou Os Singelos Clássicos Objetos Java) são objetos Java que seguem um projeto
simplificado, sem definições rígidas de estrutura como, por exemplo, obrigar o programador a implementar
construtores com ou sem argumentos, definir métodos getters e setters para cada atributo, convenções
para nomear métodos, etc.
Todos os métodos públicos do bean exemplificados acima podem ser invocados por um cliente.
No código acima também verificamos que o método "remover" é anotado com a anotação @Remove.
Um cliente pode remover um bean com estado de sessão apenas invocando o método "remover".
Chamando este método temos como resultado uma chamado do container ao método com a anotação
@PreDestroy. Remover um bean com estado de sessão significa que o estado da instância específica
para aquele cliente não existe mais.
Este estilo de declaração de bean é chamado de visualização sem interface. Esse bean é apenas
localmente acessível aos clientes empacotados no mesmo arquivo de deploy (como um war). Se um bean
precisa ser remotamente acessível, ele deve definir uma interface de negócio separada anotada com
@Remote. Segue na imagem abaixo um exemplo de definição de uma interface remota.

Criando uma interface remota para o bean com estado de sessão criado anteriormente.

Agora o bean pode ser injetado através da sua interface, conforme mostra a imagem abaixo.

Injetando o bean através da interface.

Um cliente deste bean com estado de sessão pode acessar este bean da mesma forma que aparece
na imagem abaixo

Exemplo de como o cliente pode acessar o bean.

Podemos notar o quanto a injeção de dependência facilita a vida dos programadores que utilizam o
EJB 3.2. A injeção de dependência é um padrão de projeto que visa desacoplar os componentes da
aplicação. Dessa forma, os componentes são instanciados externamente à classe com um gerenciador
controlando essas instâncias. Esse gerenciador, através de uma configuração, liga os componentes de
forma a montar a aplicação. Isso facilita muito a vida dos desenvolvedores que não precisam instanciar e
nem configurar esses beans, basta injetá-lo na classe.
O EJB 3.2 tornou menos rígida as regras default para designar a implementação de interfaces como
local ou remota. A classe do bean deve implementar a interface ou a interface deve ser designada como
uma interface de negócio local ou remota por meio da anotação @Local ou @Remote ou ainda podemos
utilizar o deployment descriptor.
Se o bean está implementando duas interfaces que não possuem anotações, então eles estarão
expostos como visualização local do bean. Segue na imagem abaixo um exemplo.

260
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Bean implementando duas interfaces sem anotações, tornando-as assim como de acesso local.

Neste código, se CarrinhoDeCompras e Pagamento não possuem anotações explicitas indicando se


é local ou remota, então eles estão expostos como visualização local do bean. Porém, os beans também
podem ser explicitamente marcados com @Local, conforme mostra o exemplo da imagem abaixo.

Definindo de forma explicita o bean como local.

Os dois fragmentos de código são semanticamente equivalentes.


Também podemos marcar o bean como remoto através da anotação @Remote, conforme o exemplo
a imagem abaixo.

Definindo o bean como remoto através da anotação @Remote.

Dessa forma, CarrinhoDeCompras e Pagamento são visualizações remotas.


Se uma das interfaces é marcada como @Local ou @Remote, então cada interface que precisar ser
exposta deve ser marcada explicitamente; caso contrário, ele será ignorado. Segue na imagem abaixo
um exemplo onde definimos a anotação em apenas uma das implementações.

Exemplo definindo uma interface anotada.

Neste código o Bean apenas expõe uma interface remota que é "CarrinhoDeCompras".
Os beans com estado de sessão também possuem acesso aos métodos de callback do ciclo de vida,
são eles: "PostConstruct" e "PreDestroy". Além disso, um container EJB pode decidir por passivar um
bean com estado de sessão para alguma forma de armazenamento secundário e então ativa-lo
novamente. O container se encarrega de salvar e restaurar o estado do bean. No entanto, se há objetos
que não são serializáveis como soquetes abertos ou conexões JDBC, eles precisam ser explicitamente

261
1678859 E-book gerado especialmente para DANIEL CRISTIAN
fechados e restaurados como parte desse processo. Assim, @PrePassivate é invocado para limpar os
recursos antes do bean ser passivado, e @PostActivate que é invocado para restaurar os recursos.
Porém, uma das novidades do EJB 3.2 é a capacidade de optar por não passivação o que pode evitar
exceções em tempo de execução e a degradação de desempenho do aplicativo. Segue na Listagem 9
um exemplo de como desabilitar essa funcionalidade.

Desabilitando a passivação num determinado bean.

Stateless Session Beans


Um bean sem estado de sessão não contém qualquer estado conversacional para um cliente
específico.
Todas as instâncias de um bean sem estado de sessão são equivalentes, portanto o container pode
escolher delegar um método invocado por um cliente para qualquer instância disponível no pool do
container. Visto que os beans sem estado de sessão não possuem qualquer estado, eles não precisam
ser passivados.
Podemos definir um simples bean sem estado de sessão utilizando a anotação @Stateless, conforme
exemplificado no código da imagem abaixo.

Exemplo de um bean sem estado de sessão.

O código acima mostra um POJO anotado com @Stateless. Isto é tudo que precisamos para converter
um POJO para um bean sem estado de sessão. Todos os métodos públicos do bean podem ser invocados
por um cliente.
Podemos acessar este bean sem estado de sessão usando a anotação @EJB, conforme podemos
verificar no exemplo da imagem abaixo.

Acessando um bean sem estado de sessão utilizando @EJB.

262
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Este estilo de declaração de bean também é chamado de uma visualização sem interface, conforme
verificamos anteriormente também. Este bean é acessível apenas localmente para os clientes
empacotados no mesmo arquivo de deploy. Se o bean precisa ser acessível remotamente, devemos
definir uma interface de negócio separado com a anotação @Remote, conforme verificamos na imagem
abaixo.

Criando uma interface remota para o bean sem estado de sessão.

Por fim, o cliente injeta o bean através da interface, conforme exemplificado na imagem abaixo.

Injetando o bean Conta através da anotação @EJB.

Os beans sem estado de sessão suportam dois métodos de callback do ciclo de vida, são eles:
"PostConstruct" e "PreDestroy".
O método "PostConstruct" é invocado depois que o construtor sem argumentos é invocado e todas as
dependências foram injetadas, e antes que o primeiro método de negócio é invocado no bean. Este
método é geralmente onde todos os recursos necessários para o bean são inicializados.
O método de callback "PreDestroy" é chamado antes da instância ser removida pelo container. Este
método é geralmente onde todos os recursos adquiridos durante o PostConstruct são liberados.
Como os beans sem estado de sessão não armazenam qualquer estado, o container pode fazer um
pool de instâncias, e todos eles são tratados da mesma forma a partir de uma perspectiva do cliente.
Qualquer instância do bean pode ser usada para atender a solicitação do cliente. Com essa simples
afirmação podemos verificar que os beans sem estado de sessão são muito mais rápidos que os beans
com estado de sessão.

Singleton Session Beans


Um bean de sessão Singleton é um componente que é instanciado uma única vez por aplicação e
fornece um acesso bastante facilitado ao estado compartilhado. Se o container for distribuído em múltiplas
JVM, cada aplicação terá uma instância do Singleton para cada JVM. Um bean de sessão Singleton é
explicitamente projetado para ser compartilhado e suportar concorrência.
Podemos definir um simples bean de sessão Singleton usando a anotação @Singleton, conforme
podemos verificar no exemplo da Listagem imagem abaixo.

263
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplo de um bean de sessão Singleton utilizando a anotação @Singleton

O Container é responsável por quando inicializar uma instância de um bean de sessão Singleton.
Contudo, podemos opcionalmente marcar o bean para inicializar antes através da anotação @Startup,
conforme exemplificado na imagem abaixo.

Exemplo de como inicializar um Singleton antes da aplicação estar disponível.

O Container agora inicializará todos esses Singletons em tempo de inicialização, executando o código
que estiver anotado com @PostConstruct, antes da aplicação tornar-se disponível e antes que qualquer
solicitação do cliente seja atendida.
Podemos especificar uma inicialização explicita de beans de sessão Singleton usando @DependsOn.
O exemplo da imagem abaixo ilustra melhor como podemos utilizar essa funcionalidade:

Exemplo de como utilizar a anotação @DependsOn para criar dependências entre os beans.

O container assegura que o bean DesligaAlarme é inicializado antes do bean AbrePortaoGaragem.


Um bean Singleton suporta os métodos de callback do ciclo de vida "PostConstruct" e "PreDestroy".
Além disso, o Singleton também suporta acesso concorrente.
Por padrão, um bean Singleton é marcado para ter concorrência gerenciada pelo container, mas
opcionalmente pode ser marcado para ter concorrência gerenciada por bean.
Concorrência gerenciada por Container é baseada em metadados com bloqueio a nível de método
onde cada método é associado com um bloqueio do tipo Read (compartilhado) ou Write (exclusivo).
Um bloqueio de leitura (Read) permite chamadas simultâneas do método. Um bloqueio de leitura
(Write) aguarda o processamento de uma invocação completar antes de permitir que a próxima invocação
prossiga.
As anotações @Lock(LockType.READ) e @Lock(LockType.WRITE) são utilizadas para especificar o
tipo de bloqueio. Mas, por padrão, um bloqueio Write é associado com cada método do bean.
Essas anotações podem ser especificadas na classe, um método de negócio da classe, ou ambos.
Um valor especificado no método sobrescreve um valor especificado na classe. A concorrência
gerenciada por bean requer que o desenvolvedor gerencie a concorrência utilizando primitivas de
sincronização da linguagem Java como “synchronized” e “volatile”.

264
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Eventos do Ciclo de Vida
Métodos interceptadores do ciclo de vida podem ser definidos tanto para beans de sessão quanto para
beans de mensagens.
As anotações @AroundConstruct, @PostConstruct, @PreDestroy, @PostActivate, e @PrePassivate
são utilizados para definir métodos interceptadores para eventos do ciclo de vida dos beans.
A anotação @AroundConstruct pode ser definida apenas numa classe interceptadora, enquanto que
todas as outras anotações podem ser definidos em uma classe interceptadora e/ou diretamente numa
classe bean.
Nas próximas seções veremos melhor cada uma dessas anotações.

Interceptador @AroundConstruct
A anotação @AroundConstruct designa um método interceptor que recebe uma chamada de retorno
quando o construtor da classe de destino é invocado.
Este método interceptor pode ser definido apenas em classes de interceptoras e/ou superclasses de
classes de interceptores e não pode ser definido na classe alvo.
Um interceptador pode ser definido como o da figura abaixo.

Definindo um AroundConstructor.

Um interceptador vinculado (Interceptor binding) pode ser definido como a imagem abaixo.

Exemplo de como definir um interceptor vinculado.

E finalmente, o interceptador pode ser especificado no bean como a imagem abaixo.

Definindo um interceptor no bean.

O método validateConstructor é chamado toda vez que o construtor do bean MeuBean é chamado.

Interceptador @PostConstruct
A anotação @PostConstruct é utilizada em um método que precisa ser executado após a injeção de
dependência ser realizada. Assim podemos executar qualquer inicialização, visto que utilizando o

265
1678859 E-book gerado especialmente para DANIEL CRISTIAN
PostConstruct temos certeza que o método será executado antes da primeira invocação do método de
negócio na instância do bean.
O método anotado com PostConstruct é invocado mesmo se a classe não solicitar quaisquer recursos
a serem injetados. Apenas um método pode ser anotado com @PostConstruct. Segue na Listagem 20
um exemplo.

Anotando um método do bean com @PostConstruct.

Neste código o construtor default do bean é chamado primeiro e após isso o método
configuraRecursos() é chamado antes que qualquer método de negócio possa ser chamado.
Este método interceptador do ciclo de vida serve para diferentes tipos de beans de sessão e ocorre
nos seguintes contextos de transação:
Para um bean sem estado de sessão, ele executa em um contexto de transação não especificado.
Para um bean com estado de sessão, ele executa em um contexto de transação determinado pelo
atributo de transação do método de retorno de chamada.
Para um bean Singleton, ele executa em um contexto de transação determinado pelo tipo de
gerenciamento de transação do bean e qualquer atributo de transação aplicável.

Interceptador @PreDestroy
A anotação @PreDestroy é utilizada como uma forma de notificação para sinalizar que a instância está
no processo de ser removida do container. O método anotado com “PreDestroy” é normalmente utilizado
para liberar recursos que tem sido utilizados na aplicação. Segue na imagem abaixo um exemplo de como
utilizá-la.

Anotando um método do bean com @PreDestroy.

Neste código, o método liberaRecursos() é chamado antes que a instância seja removida pelo
container.
Este método interceptador é utilizado em diferentes tipos de beans de sessão e ocorre nos seguintes
contextos de transação:
Para um bean sem estado de sessão, ele executa em um contexto de transação não especificado.

266
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Para um bean com estado de sessão, ele executa em um contexto de transação determinado pelo
atributo de transação do método de retorno de chamada.
Para um bean Singleton, ele executa em um contexto de transação determinado pelo tipo de
gerenciamento de transação do bean e qualquer atributo de transação aplicável.

Interceptador @PrePassivate
A anotação @PrePassivate pode ser usada apenas em um bean com estado de sessão. Esta anotação
designa um método para receber uma chamada de retorno antes de um bean com estado de sessão ser
passivado. Segue na imagem abaixo um exemplo.

Anotando um método do bean com @PrePassivate.

Esses métodos são ignorados por beans sem estado de sessão ou Singleton.
Este método interceptador executa em um contexto de transação determinado pelo atributo de
transação do método de retorno de chamada.

Interceptador @PostActivate
A anotação @PostActivate pode apenas ser usada em um bean com estado de sessão. Esta anotação
designa um método para receber um retorno de chamada após um bean com estado de sessão ser
ativado. Segue na imagem abaixo um exemplo.

Anotando um método do bean com @PostActivate.

Esses métodos são ignorados por beans sem estado de sessão e Singleton.
Este método interceptador executa em um contexto de transação determinado pelo atributo de
transação do método de retorno de chamada do ciclo de vida.
Métodos interceptadores do ciclo de vida podem lançar exceções em tempo de execução, mas não
exceções de aplicação.

267
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Message-Driven Beans
Um Message-Driven Bean (MDB) é um bean gerenciado por container que é usado para processar
mensagens de forma assíncrona. Um MDB pode implementar qualquer tipo de mensagem, mas é mais
comum usá-lo para processar mensagens Java Message Service (JMS). Esses beans são caracterizados
por serem sem estado de sessão e são invocados pelo container quando uma mensagem JMS chega ao
destino.
Um bean de sessão pode receber uma mensagem JMS de forma síncrona, mas um Message-Driven
Bean pode receber uma mensagem de forma assíncrona.
Podemos converter um POJO para um Message-Driven Bean usando a anotação @MessageDriven.
Segue na imagem abaixo um exemplo.

Criando um MDB utilizando a anotação @MessageDriven.

Neste código, @MessageDriven define o bean como sendo um Message-Driven Bean. O atributo
mappedName especifica o nome do JNDI do JMS de destino para o qual o bean consumirá a mensagem.
O bean deve implementar a interface MessageListener, na qual fornece apenas um método,
"onMessage". Este método é chamado pelo Container sempre que a mensagem é recebida pelo
Message-Driven Bean. Este método conterá a lógica de negócio específica de aplicação.
O código a imagem abaixo mostra como uma mensagem de texto é recebida pelo método onMessage
e como o corpo da mensagem pode ser recuperada e exibida.

Recebendo e recuperando uma mensagem de texto.

Apesar de um Message-Driven Bean não poder ser invocado diretamente por um bean de sessão, ele
ainda pode invocar outros beans de sessão. Um Message-Driven Bean também pode enviar mensagens
JMS.
A anotação @MessageDriven também pode ter atributos adicionais para configurar o bean. Por
exemplo, a propriedade “activationConfig” pode ter um array de ActivationConfigProperty que fornece
informações para o publicador sobre as configurações do bean neste ambiente operacional.
A Tabela abaixo define o conjunto padrão de propriedades de configuração que são suportados pelo
@MessageDriven.

268
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Nome da Propriedade Descrição
acknowledgeMode Especifica o modo de confirmação do JMS para a
mensagem entregue quando a demarcação de
transação do bean gerenciado é usada. Os valores
suportados são Auto_acknowledge (default) ou
Dups_ok_acknowledge.
messageSelector Especifica o seletor de mensagem JMS para ser usado
na determinação de quais mensagens um MDB
recebe.
destinationType Especifica se o MDB é para ser usado com um Queue
ou Topic. Os valores suportados são javax.jms.Queue
ou javax.jms.Topic.
subscriptionDurability Se MDB é usado com um Topic, especifica se uma
inscrição durável ou não durável é utilizada. Os valores
suportados são Durable ou NonDurable.
Propriedades suportadas pelo @MessageDriven.

Um Message-Driven Bean é capaz de processar mensagens de múltiplos clientes simultaneamente.


Assim como beans sem estado de sessão, o container pode fazer um pool de instâncias e alocar
instâncias de bean suficiente para lidar com o número de mensagens em um determinado momento.
Todas as instâncias do bean são tratadas igualmente.
Como uma mensagem é entregue para um Message-Driven Bean dentro de um contexto transacional,
todas as operações do método "onMessage" fazem parte de uma única transação. O contexto de
transação é propagado para os outros métodos invocados dentro do onMessage.
O MessageDrivenContext pode ser injetado em um Message-Driven Bean. Esta funcionalidade fornece
acesso ao contexto do Message-Driven Bean em tempo de execução que está associado com a sua
instância. Segue na imagem abaixo um exemplo de como fazer uma reversão (rollback) na transação
usando o MessageDrivenContext.

Revertendo uma transação com MessageDrivenContext.

Java Persistence API (JPA 2.x)


64

JPA é um framework leve, baseado em POJOS (Plain Old Java Objects) para persistir objetos Java. A
Java Persistence API, diferente do que muitos imaginam, não é apenas um framework para Mapeamento
Objeto-Relacional (ORM - Object-Relational Mapping), ela também oferece diversas funcionalidades
essenciais em qualquer aplicação corporativa.
Atualmente temos que praticamente todas as aplicações de grande porte utilizam JPA para persistir
objetos Java. JPA provê diversas funcionalidades para os programadores, como será mais
detalhadamente visto nas próximas seções. Inicialmente será visto a história por trás da JPA, a qual
passou por algumas versões até chegar na sua versão atual.

64 Fonte: http://www.devmedia.com.br/introducao-a-jpa-java-persistence-api/28173

269
1678859 E-book gerado especialmente para DANIEL CRISTIAN
História da Especificação
Após diversos anos de reclamações sobre a complexidade na construção de aplicações com Java, a
especificação Java EE 5 teve como principal objetivo a facilidade para desenvolver aplicações JEE 5. O
EJB 3 foi o grande percursor para essa mudança fazendo os Enterprise JavaBeans mais fáceis e mais
produtivos de usar.
No caso dos Session Beans e Message-Driven Beans, a solução para questões de usabilidade foram
alcançadas simplesmente removendo alguns dos mais onerosos requisitos de implementação e
permitindo que os componentes sejam como Plain Java Objects ou POJOS.
Já os Entity Beans eram um problema muito mais sério. A solução foi começar do zero. Deixou-se os
Entity Beans sozinhos e introduziu-se um novo modelo de persistência. A versão atual da JPA nasceu
através das necessidades dos profissionais da área e das soluções proprietárias que já existiam para
resolver os problemas com persistência. Com a ajuda dos desenvolvedores e de profissionais experientes
que criaram outras ferramentas de persistência, chegou a uma versão muito melhor que é a que os
desenvolvedores Java conhecem atualmente.
Dessa forma os líderes das soluções de mapeamento objetos-relacionais deram um passo adiante e
padronizaram também os seus produtos. Hibernate e TopLink foram os primeiros a firmar com os
fornecedores EJB.
O resultado final da especificação EJB finalizou com três documentos separados, sendo que o terceiro
era o Java Persistence API. Essa especificação descrevia o modelo de persistência em ambos os
ambientes Java SE e Java EE.

JPA 2.0
No momento em que a primeira versão do JPA foi iniciada, outros modelos de persistência ORM já
haviam evoluído. Mesmo assim muitas características foram adicionadas nesta versão e outras foram
deixadas para uma próxima versão.
A versão JPA 2.0 incluiu um grande número de características que não estavam na primeira versão,
especialmente as mais requisitadas pelos usuários, entre elas a capacidade adicional de mapeamento,
expansões para a Java Persistence Query Language (JPQL), a API Criteria para criação de consultas
dinâmicas, entre outras características.
Entre as principais inclusões na JPA destacam-se:
- POJOS Persistentes: talvez o aspecto mais importante da JPA seja o fato que os objetos são POJOs
(Plain Old Java Object ou Velho e Simples Objeto Java), significando que os objetos possuem design
simples que não dependem da herança de interfaces ou classes de frameworks externos. Qualquer objeto
com um construtor default pode ser feito persistente sem nenhuma alteração numa linha de código.
Mapeamento Objeto-Relacional com JPA é inteiramente dirigido a metadados. Isto pode ser feito através
de anotações no código ou através de um XML definido externamente.
- Consultas em Objetos: as consultas podem ser realizadas através da Java Persistence Query
Language (JPQL), uma linguagem de consulta que é derivada do EJB QL e transformada depois para
SQL. As consultas usam um esquema abstraído que é baseado no modelo de entidade como oposto às
colunas na qual a entidade é armazenada.
- Configurações Simples: existe um grande número de características de persistência que a
especificação oferece, todas são configuráveis através de anotações, XML ou uma combinação das duas.
Anotações são simples de usar, convenientes para escrever e fácil de ler. Além disso, JPA oferece
diversos valores defaults, portanto para já sair usando JPA é simples, bastando algumas anotações.
- Integração e Teste: atualmente as aplicações normalmente rodam num Servidor de aplicação, sendo
um padrão do mercado hoje. Testes em servidores de aplicação são um grande desafio e normalmente
impraticáveis. Efetuar teste de unidade e teste caixa branca em servidores de aplicação não é uma tarefa
tão trivial. Porém, isto é resolvido com uma API que trabalha fora do servidor de aplicação. Isto permite
que a JPA possa ser utilizada sem a existência de um servidor de aplicação. Dessa forma, testes unitários
podem ser executados mais facilmente.

JAX-RS (2.x)
65

Quando foi lançado em 2008 pelo JSR-311, o JAX-RS 1.0 se tornou um dos primeiros frameworks
Java baseados em classes POJO e anotações, para criação de aplicações web robustas.
Agora, cinco anos depois da primeira versão, o JAX-RS 2.0 é lançado no Java EE 7. A nova versão é
baseada na JSR-339. Apresentaremos aqui algumas das novas funcionalidades que fazem com que a

65 Fonte: http://www.infoq.com/br/news/2013/07/novidade-jax-rs-2.0

270
1678859 E-book gerado especialmente para DANIEL CRISTIAN
mudança para a versão 2.0 valha a pena. Mas antes disso, relembramos um pouco a JAX-RS 1.0, através
de um exemplo:

Nessa listagem, a linha 1 mapeia um recurso (portfólios) para o caminho relativo /portfolios. Na linha 4
é especificado que uma requisição GET deve retornar todos os portfólios existentes. As linhas 9 e 10
indicam que, em uma requisição GET a uma URI como /portfolios/123, o valor que sucede a URI do
recurso deve ser extraído como um parâmetro (no caso, 123) e atribuído à variável "id", para que o
portfólio associado a esse identificador seja então retornado pelo serviço.
A sintaxe usada no exemplo permanece idêntica no JAX-RS 2.0.
De forma consistente com o tema principal do Java EE 7, o JAX-RS 2.0 adiciona algumas
funcionalidades há muito aguardadas, a maioria delas focada no que a Oracle chama de "API
simplificada". Essas novas funcionalidades podem ser categorizadas como:
- API cliente
- Suporte a chamadas assíncronas
- HATEOAS (hipermídia)
- Anotações
- Validação
- Filtros e manipuladores
- Negociação de conteúdo

Falaremos a seguir um pouco sobre cada uma dessas categorias:

API Cliente
A API do JAX-RS 1.0 era voltada estritamente para o lado servidor. Algumas implementações
forneceram vários graus de suporte para clientes, mas geralmente o desenvolvedor teve que escolher
entre algumas soluções ad hoc, como o HttpClient do projeto Jakarta Commons da Apache, ou o REST
Client da WizTools.
O JAX-RS 2.0 adiciona construtores ("builders") para invocar um web service REST a partir do cliente.
Veja um exemplo:

Note que esse método primeiramente obtém um cliente e então utiliza o pattern Builder para construir
todos os parâmetros da URL, permitindo que o desenvolvedor formule a URL sem lidar com as
particularidades de construção de URL.

271
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Essa abordagem é especialmente útil para invocações que contenham um corpo HTTP, como em um
POST ou PUT HTTP. No exemplo a seguir é realizada uma chamada POST fictícia para venda de 100
ações da IBM:

Chamadas Assíncronas
No JAX-RS 1.0 o cliente tinha que esperar o servidor responder suas chamadas, pois a API suportava
apenas requisições síncronas. A versão 2.0 introduz o suporte assíncrono. Isso permite que um cliente
faça uma chamada RESTfull e, opcionalmente, obtenha uma referência de Future ou um
InvocationCallback, para ser notificada quando a resposta estiver completa.

HATEOAS (Hipermídia)
De acordo com a ortodoxia do REST, se a aplicação não estiver usando HATEOAS, então ela não é
REST! O HATEOAS (Hypermedia as the Engine of Application State, ou Hipermídia como Motor de
Estado da Aplicação) requer que os produtores e consumidores RESTful concordem com um conjunto de
links a serem retornados em cada chamada e usados pelo cliente para navegação. Se o REST for
pensado como a versão "aplicação" de uma página web, então o HATEOAS pode ser visto como os links
contidos dentro dessa página web.
O JAX-RS 2.0 fornece as classes Link e Target para introduzir hiperlinks dentro da resposta fornecida
pelo servidor e reagir a elas no lado cliente.

Anotações
Novas anotações foram introduzidas, como para fornecer suporte de novos tipos de injeção.

Bean Validation
O framework Bean Validation está integrado ao novo JAX-RS, agindo como facilitador para especificar
metadados de validação de parâmetros. Por exemplo, a anotação @NotNull indica que o parâmetro
anotado não pode ser nulo. É possível também utilizar anotações customizadas para garantir formatos
de dados (CEP, telefone, etc).

Filtros e Manipuladores (Handlers)


A API de filtros fornece a habilidade de encadear filtros de Servlets, de acordo ao padrão Chain of
Responsibility. Isso é útil para implementar funcionalidades ortogonais, como o clássico exemplo de
logging. Qualquer filtro pode decidir se deve continuar ou terminar a cadeia ao invocar FilterAction.NEXT
ou FilterAction.STOP, respectivamente.
Os handlers (manipuladores) são similares aos filtros, exceto pela capacidade de "embrulhar" (wrap)
uma invocação de método em um ponto de extensão especificado, para manipular e enriquecer os dados
do corpo da mensagem.

Negociação de Conteúdo
Anotações mais ricas agora aceitam construções que permitem priorizar os formatos de
request/response.
O JAX-RS ainda carece de um bom modelo de segurança, o que o torna difícil utilizar em aplicações
empresariais, que são expostas a clientes externos. Esperamos que a próxima versão forneça suporte
para autenticação ponto-a-ponto.
O JAX-RS 2.0 ainda estão em vias de ser suportado pelos principais servidores Java EE. Enquanto
isso não acontece, é possível aproveitá-lo imediatamente em sua aplicação, utilizando a última versão do
Jersey. A Oracle publicou os passos para fazer isso no WebLogic 12c.

272
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Hibernate

O Hibernate é um framework ORM - Object Relational Mapping. É uma ferramenta que nos ajuda a
persistir objetos Java em um banco de dados relacional. O trabalho do desenvolvedor é definir como os
objetos são mapeados nas tabelas do banco e o Hibernate faz todo o acesso ao banco, gerando inclusive
os comandos SQL necessários.
O Hibernate é um projeto opensource do grupo JBoss com muitos anos de história e liderança no
mercado Java. Recentemente, boa parte das idéias do Hibernate e outros frameworks ORM foram
padronizadas em uma especifição oficial do Java, a JPA - Java Persistence API. A JPA é uma
especificação do JCP e possui várias implementações (o Hibernate, o Oracle Toplink, EclipseLink,
OpenJPA etc).

Preparando o Hibernate66
Para preparar o Hibernate, será necessário baixar dois ZIPs do site do Hibernate. Cada ZIP representa
um projeto diferente.
O primeiro será o Hibernate Core, que se chama hibernate-distribution-XXX.zip. O segundo ZIP será
do projeto Hibernate Annotations, já que queremos configurar nosso projeto com as anotações da JPA.
Esse ZIP chama-se hibernate-annotations-XXX.zip. Faça os downloads diretamente em:

http://www.hibernate.org

Depois de fazer o download desses dois zips, basta descompactá-los e utilizar os JAR's que estão
dentro de cada projeto. No exercício abaixo veremos quais JARs vamos precisar. A partir do Hibernate
3.5, esses jars todos vem numa única distribuição, a core.

Configurando o Mecanismo de logging


O Hibernate utiliza o SL4J - Simple Logging Facade for Java - para o mecanismo de logging. Ele serve
apenas como uma fachada para vários frameworks de logging, por exemplo o Log4J e o java.util.logging.
No nosso projeto utilizaremos o Log4J, então precisamos baixar mais dois zips. O primeiro é o Log4J,
do grupo Apache. O segundo é o próprio SL4J, que contêm o jar que faz o binding do SL4J com o Log4J.

Configurando o Driver do MySQL


Apesar de toda facilidade que o Hibernate nos trará, por baixo dos panos ele faz chamadas ao JDBC.
Portanto, para que nossa aplicação se conecte no banco de dados, precisamos do driver do MySQL no
classpath. O driver é a implementação JDBC que será utilizada. Cada banco de dados possui a sua
implementação, e ela deve ser baixada do site do próprio banco de dados. Faça o download do mysql
jdbc driver.

Configurando o Hibernate
Para configurar o Hibernate, podemos utilizar ou um arquivo .properties ou um arquivo XML.
O arquivo de properties é mais simples, mais fácil de se criar, mas uma das desvantagens é que ele
não consegue configurar tudo que queremos, por exemplo as entidades. As entidades têm que ser
configuradas no código, e as outras informações no arquivo de properties.
Já o XML, por mais que seja um pouco mais difícil em relação ao properties, permite que toda a
configuração seja feita nele. Por isso faremos nossa configuração no XML. O arquivo XML que o
Hibernate procurará será o hibernate.cfg.xml e ele deve estar no classpath.
Para nosso caso, vamos seguir a convenção e criar o arquivo hibernate.cfg.xml na pasta src, dentro
do nosso projeto. O conteúdo do arquivo será esse:

66 Fonte/; http://www.caelum.com.br/apostila-vraptor-hibernate/persistindo-os-dados-com-o-hibernate/#4-3-preparando-o-hibernate

273
1678859 E-book gerado especialmente para DANIEL CRISTIAN
As configurações que passamos nesse arquivo são parecidas quando queremos nos conectar a um
banco de dados. Para conectar em um banco de dados, precisamos informar qual é o usuário, a senha,
algumas informações do banco, como host, porta, etc.
Um detalhe importante da nossa configuração é o banco de dados que foi passado. Na configuração
hibernate.connection.url foi passado o nome do database que utilizaremos. Para esse caso escolhemos
o database fj28.
Abaixo segue a descrição de todas as configurações que usamos:
- hibernate.connection.username - usuário do banco de dados
- hibernate.connection.password - senha do usuário
- hibernate.connection.url - chamada de URL ou string de conexão, deve ser configurada de acordo
com documentação do banco de dados
- hibernate.connection.driver_class - driver que deve ser utilizado
- hibernate.hbm2ddl.auto - como o hibernate vai se comportar em relação às tabelas do banco. Com o
valor update ele vai criar ou modificar tabelas sempre que necessário.
- hibernate.dialect - dialeto a ser utilizado para a comunicação com o banco de dados
- show_sql - flag que indica se os SQLs gerados devem ser impressos
- format_sql - flag que indica se os SQLs devem ser formatados

Existem muitas outras configurações possíveis. Para maiores detalhes, acesse a documentação do
Hibernate.

Spring Framework (4.x) e Spring Security


67

O Spring é uma tecnologia que surgiu para resolver o problema da complexidade encontrada nos
Enterprise Java Beans (EJBs), sendo um framework que implementa os padrões de Inversão de Controle
e Injeção de Dependência, juntamente os Patterns de Setter Injection e Constructor Injection, além de
prover técnicas de boa programação fornecendo um modelo unificado e simples de programação, isto o

67 Fonte: http://www.matera.com/br/2015/04/08/trabalhando-com-jse-e-spring-framework/

274
1678859 E-book gerado especialmente para DANIEL CRISTIAN
tornou um sucesso em meio à comunidade de programadores, sucesso este, foi tão grande que na versão
mais nova do JEE, diversas melhorias foram criadas baseadas no Spring Framework.
O Spring Framework implementa dois padrões de grande sucesso na comunidade de programadores
que são de Inversão de Controle e Injeção de Dependência, provendo sempre das boas técnicas de
programação, sendo estas, baixo acoplamento e alta coesão. Com sua arquitetura baseada em Plain Old
Java Object (POJOs), o principal objetivo do Spring Framework é tornar as tecnologias existentes
atualmente no mercado fáceis de serem utilizadas.
Como visto, devido ao sucesso do Spring, hoje é possível ter aplicações JEE com arquiteturas bastante
simplificadas, uma vez que a partir da mudança do J2EE para o JEE a complexidade em se criar
aplicações Java Web foi muito reduzida fazendo com que muitas empresas que tinham suas aplicações
baseadas na plataforma J2SE migrassem para o JEE. Essa mudança fez o mercado de software ser o
que é hoje, onde encontramos aplicações cada vez mais inovadoras e altamente conectadas.
O Spring foi criado com o intuito simplificar a programação em Java, possibilitando construir aplicações
que antes só era possível utilizando EJB’s.
O Spring atualmente possui diversos módulos como Spring Data (trata da persistência), Spring Security
(trata da segurança da aplicação) entre outros módulos. Mas o principal (core) pode ser utilizado em
qualquer aplicação Java, as principais funcionalidades são a injeção de dependência (CDI) e a
programação orientada a aspectos (AOP), cabe ao desenvolvedor dizer ao Spring o que quer usar. O que
faz dele uma poderosa ferramenta, pois não existe a necessidade de se arrastar todas as ferramentas do
framework para criar uma aplicação simples.

Configurando o Spring68
Para esse artigo vamos utilizar o Eclipse e o Tomcat 7, certifique-se de tê-los instalados antes de
continuar.
Primeiramente criaremos um “Dynamic Web Project” com o nome de “hello-spring”, como mostra a
figura a seguir.

Criando o projeto.

Agora vem a parte mais “complicada”, configura o Spring, a princípio algo bastante complexo para
aqueles que ainda não possuem muita pratica, lembrando que todo framework é um bicho de sete
cabeças a primeira vista.

68 Fonte: http://www.devmedia.com.br/introducao-ao-spring-framework/26212

275
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Para o Spring funcionar, vamos precisar de suas libs, acessando os links abaixo, vamos encontrar tudo
o que precisamos.
http://www.springsource.org/spring-framework
http://commons.apache.org/logging/download_logging.cgi

No canto direito da tela teremos as últimas versões lançadas, usaremos a mais atual. Após baixar o
zip do framework, vamos descomprimi-lo, e acessar a pasta libs do framework, não se assuste, não
usaremos todos esses jar’s , vamos copiar “spring-webmvc-X-X.jar” , “spring-web- X-X.jar”, “spring-
expression-X-X.jar”, “spring-core- X-X.jar”, “spring-context- X-X.jar”, “spring-beans- X-X.jar” e colar na
pasta “WEB-INF/lib”. Também é necessária a biblioteca “commons-logging-1.1.1.jar” para nosso projeto,
ver figura a seguir.
X-X trata-se da versão do framework que foi baixada.

Monstrando JAR’s do prejeto.

Com posse da biblioteca do framework, agora vamos criar uma pasta chamada “spring” dentro do
diretório WEB-INF, e dentro da pasta criaremos um arquivo xml chamado “application-context.xml”,
lembrando que o Spring é um framework “container-based”, ou seja, ele vai conter e carregar o que você
informá-lo. A raiz do nosso xml é tag <beans></beans> e dentro conterá toda a configuração do Spring
Observação: não é obrigatória a configuração do Spring em apenas um “.xml”, é possível separar
vários arquivos de configuração, exemplo: persistence-context.xml mvc-context.xml etc.

276
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Arquivo de configuração bem simples não? Claro, é uma aplicação simples, e a medida que sua
aplicação cresce, basta ir acrescentando módulos de acordo com sua necessidade, evitando assim um
consumo gigantesco de memória com coisas que você nunca vai utilizar na sua aplicação.
Agora vamos dizer a nossa aplicação Web para carregar o Spring, mas como? O bom e velho arquivo
“web.xml”, será necessária apenas a configuração do Servlet do Spring, nada sobrenatural, veja como
deve ficar o nosso arquivo.

Arquivo web.xml.

Exibindo uma Pagina


Então é só isso? Podemos rodar a aplicação? Calma, ainda não existe nenhuma página, então
criaremos uma dentro da pasta “WEB-INF/view” com o nome de "helloworld.jsp”. Veja que é o mesmo
nome o qual configuramos como página inicial no “application-context.xml”.

Definindo a página inicial.

Nossa página ficará assim.

Página helloword.jsp.

277
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Agora sim, você já pode rodar o projeto no tomcat, veja o resultado na figura abaixo.

Controllers

Exibir uma página é muito simples, mas qual a diferença do Spring? Vamos falar sobre o controle de
navegação da aplicação. O Spring utiliza os chamados Controllers, que são classes mapeadas através
de annotations que servem para dizer à aplicação o que exibir quando for requisitada uma página, ou
envio de informações, uma espécie de Servlet do JSP, mas bem mais fácil de se trabalhar.
Vamos a um exemplo, criaremos uma classe chamada “HelloController” no pacote
“br.com.devmedia.controllers”, se o pacote não existir, crie-o. Vamos anotar essa classe com o
@Controller, que diz ao Spring que essa classe vai funcionar como uma espécie de Servlet para a
aplicação, ou seja, vai receber requisições tratá-las e responder ao usuário.

Anotação @Controller.

Espera ai, mas como minha aplicação vai saber qual requisição deve ser tratada pelo meu
@Controller? Simples, através da anotação @RequestMapping, ela vai verificar qual url está sendo
solicitada e enviar para o @Controller que contém a anotação com a url específica, veja.

Método bemVindo.

Explicando o código, o @RequestMapping diz que quando for requisitada a url /bemvindo, será
executado o método “bemVindo()” da classe “HelloController”. Mas o que é esse “Model” e de onde ele
vem? Por que ele está ali? O Model não é um parâmetro obrigatório, e com um pouco mais de experiência
verá que muita coisa não é. O “Model” vai servir para adicionar atributos para serem usados na tela, não
se preocupe com detalhes, o Spring vai fazer isso por você. Devem ter notado também o “ModelAndView”
que está sendo retornado, ele na verdade vai dizer ao Spring que página ele vai exibir, no caso a

278
1678859 E-book gerado especialmente para DANIEL CRISTIAN
“bemvindo.jsp”, mas poderia ser qualquer página que você tiver dentro da pasta “WEB-INF/views” que é
o diretório que foi mapeado para conter nossas “views”.

Mapeamento do diretório das views.

Agora que temos o @Controller que vai tratar uma requisição, precisamos também da página que vai
ser exibida, no caso a “bemvindo.jsp”. Criaremos ela dentro de “WEB-INF/views”, e nessa página
exibiremos o atributo passado pelo nosso @Controller, veja o código da JSP.

Código da página jsp.

Rode novamente a aplicação e veja o resultado, como na figura abaixo.

Apresentando pagina com atributo do @Controller.

279
1678859 E-book gerado especialmente para DANIEL CRISTIAN
TypeScript 4.0;

TYPESCRIPT 4.069

O TypeScript é uma linguagem baseada no JavaScript, adicionando sintaxe para tipos estáticos. Utiliza
uma verificação de tipos para informá-lo de erros antes de executar o código, e então, usa-se o compilador
TypeScript para remoção de tipos e deixar com um JavaScript limpo e legível.

Você pode utilizar o TypeScript obtendo-o por npm:

npm install -D typescript

O TypeScript 4.0:
Olhando para a versão 3.0 e anteriores, há um número grande de mudanças. Unificar os tipos de tuple
e listas de parâmetros foi um grande destaque. Este lançamento 3.0 também apresentou referências de
projeto para auxiliar o dimensionamento, organização e compartilhamento entre bases de código.
No TypeScript 3.7, foram apresentadas uma rica combinação de novos recursos de sistema de tipos
com ECMAScript. Vimos referências de tipo recursiva e suporte para funções de estilo de asserção. As
atualizações 3.8 e 3.9 trouxeram importações e exportações de tipo, juntamente com recursos do
ECMAScript como campos privados, nível superior await em módulos e novas sintaxes.

E o que há de novo na versão 4.0?

Na versão 4.0, foram adicionados:


- Tipos de tupla variável;
- Elementos de tupla rotulados;
- Classe de inferência de propriedade de construtores;
- Operadores de atribuição de curto-circuito;
- unknown nas cláusulas catch;
- JSX customizado;
- Melhorias de velocidade no build;
–incremental com –noEmit;
- Algumas melhorias no Editor.

Importações automáticas mais inteligentes:

A importação automática torna a codificação mais fácil, mas sempre que ela parece não funcionar,
confunde os usuários. As importações automáticas só funcionam em pacotes que seu projeto já inclui.
Tudo isso leva a uma experiência de início péssima para quando você está tentando importar
automaticamente algo que acabou de instalar, mas ainda não usou.

O TypeScript 4.0 faz um pouco de trabalho extra, incluindo os pacotes que você listou em seus campos.
As informações desses pacotes são usadas apenas para melhorar as importações automáticas e não
alteram mais nada, como a verificação de tipo.

69
https://www.mundojs.com.br/2020/08/26/novidades-typescript-4-0/

280
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Python 3.9.X aplicada para IA/ML

PYTHON70

Python é uma linguagem de programação poderosa e de fácil aprendizado. Possui estruturas de dados
de alto nível eficientes, bem como adota uma abordagem simples e efetiva para a programação orientada
a objetos. Sua sintaxe elegante e tipagem dinâmica, além de sua natureza interpretada, tornam Python
ideal para scripting e para o desenvolvimento rápido de aplicações em diversas áreas e na maioria das
plataformas.
O interpretador Python e sua extensa biblioteca padrão estão disponíveis na forma de código fonte ou
binário para a maioria das plataformas a partir do site, http://www.python.org/, e podem ser distribuídos
livremente. No mesmo sítio estão disponíveis distribuições e referências para diversos módulos,
programas, ferramentas e documentação adicional, contribuídos por terceiros.
O interpretador Python é facilmente extensível incorporando novas funções e tipos de dados
implementados em C ou C++ (ou qualquer outra linguagem acessível a partir de C). Python também se
adequa como linguagem de extensão para customizar aplicações.
Python é uma linguagem de programação interpretada, de código-fonte aberto e disponível para vários
sistemas operacionais. Ser uma linguagem interpretada significa dizer que ao se escrever um programa,
este não será compilado (traduzido para uma linguagem de máquina), mas sim “lido” por um outro
programa (chamado de interpretador) que traduzirá para a máquina o que seu programa quer dizer. O
interpretador para Python é interativo, ou seja, é possível executá-lo sem fornecer um script (programa)
para ele. Ao invés disso, o interpretador disponibilizará uma interface interativa onde é possível inserir os
comandos desejados um por um e ver o efeito de cada um deles.
Os objetivos do projeto da linguagem eram: produtividade e legibilidade. Em outras palavras, Python
é uma linguagem que foi criada para produzir código bom e fácil de manter de maneira rápida. Entre as
características da linguagem que ressaltam esses objetivos estão:
- Baixo uso de caracteres especiais, o que torna a linguagem muito parecida com pseudo-código
executável;
- O uso de identação para marcar blocos;
- Quase nenhum uso de palavras-chave voltadas para a compilação;
- Coletor de lixo para gerenciar automaticamente o uso da memória;

Além disso, Python suporta múltiplos paradigmas de programação. A programação procedimental


pode ser usada para programas simples e rápidos, mas estruturas de dados complexas, como tuplas,
listas e dicionários, estão disponíveis para facilitar o desenvolvimento de algoritmos complexos. Grandes
projetos podem ser feitos usando técnicas de orientação a objetos, que é completamente suportada em
Python (inclusive sobrecarga de operadores e herança múltipla). Um suporte modesto para programação
funcional existe, o que torna a linguagem extremamente expressiva: é fácil fazer muita coisa com poucas
linhas de comando. E também possui inúmeras capacidades de meta-programação: técnicas simples
para alterar o comportamento de comportamentos da linguagem, permitindo a criação de linguagens de
domínio específico.
Python tem uma biblioteca padrão imensa, que contém classes, métodos e funções para realizar
essencialmente qualquer tarefa, desde acesso a bancos de dados a interfaces gráficas com o usuário. E,
logicamente, já que esse é o objetivo deste grupo, existem muitas ferramentas para lidar com dados
científicos. Essa característica da linguagem é comumente chamado baterias inclusas, significando que
tudo que você precisa para rodar um programa está — na maior parte das vezes — presente na instalação
básica.
Por fim, e não menos importante, Python é uma linguagem livre e multiplataforma. Isso significa que
os programas escritos em uma plataforma serão executados sem nenhum problema na maioria das
plataformas existentes sem nenhuma modificação. E, caso a plataforma objetivo não tenha uma versão
de Python, desenvolvedores têm a liberdade de estudar e modificar o código da linguagem para fazer
com que ela rode onde quer que seja.

70
http://www.telecom.uff.br/pet/petws/downloads/tutoriais/python/tut_python_2k100127.pdf

281
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Por que Python?

É fácil ver que a linguagem tem facilidades incríveis para uso geral. A pergunta é: por que Python é a
linguagem ideal para aplicações científicas? As respostas são muitas, mas podemos resumir algumas
aqui. A primeira razão, e provavelmente a principal, é: Python é uma linguagem expressiva, em que é
fácil traduzir o raciocínio em um algoritmo. Em aplicações científicas, o raciocínio é essencialmente
complicado — essa é a natureza das ciências. É um problema adicional para o cientista ter que se
preocupar com, além do assunto básico de sua pesquisa, a correção do programa em detalhes pouco
relevantes: alocação de memória, gerenciamento de recursos, etc. Python faz isso tudo automaticamente
de maneira muito eficiente, permitindo ao cientista se concentrar exclusivamente no problema sendo
estudado.
Python é extremamente legível. Isso significa que é muito fácil compreender programas escritos há
algum tempo. É muito comum que os programas em atividades científicas sejam criados a partir da
evolução de algoritmos anteriores. Portanto, é extremamente importante ser capaz de entender o que foi
feito antes. Uma vez que as palavras-chave da linguagem Python são voltadas para a estruturação dos
programas (e não para indicar ao computador como compilar ou interpretar trechos de código), não
existem trechos de código que são inúteis para o raciocínio.
Python tem uma comunidade ativa e vibrante, espalhada por todo o mundo. E, sendo uma linguagem
livre, todos os seus usuários estão dispostos a contribuir. Isso faz com que a documentação seja
abundante e existam módulos para executar virtualmente qualquer tarefa necessária. Isso é importante:
não há tempo para reinventar a roda, então poder contar com módulos prontos é ótimo. Mas, mais que
isso, uma vez que os programas em Python são distribuídos na forma de código-fonte, qualquer pessoa
pode alterar, corrigir e melhorar os algoritmos. Isso faz com que os módulos sejam maduros e seguros,
testados contra diversas situações e diversas vezes. A robustez alcançada é um fator importante.
Python é, além disso, uma linguagem de propósito geral. Muitas vezes, é necessário lidar com tarefas
laterais: buscar dados em um banco de dados remoto, ler uma página na internet, exibir graficamente os
resultados, criar uma planilha, etc. Linguagens de cunho especificamente científico têm um sério
problema aí, mas, uma vez que Python é utilizada em praticamente todo tipo de tarefa, encontram-se
módulos prontos para realizar essas tarefas que podem ser tornar complicadas. Novamente, é uma
preocupação a menos para quem está desenvolvendo aplicações científicas.
Por esses e ainda outros motivos, Python tem conquistado uma grande popularidade entre a
comunidade científica. É uma linguagem simples que dá conta do recado e não fica entre o cientista e a
resolução do seu problema. Essa frase provavelmente resume todos os motivos necessários para sua
utilização.

Obtenção e Instalação

Caso o usuário esteja utilizando um sistema Linux ou OS X (Apple), o interpretador para Python já vem
instalado por padrão, sendo apenas necessário escrever o comando “python” 2 no seu programa de
terminal favorito. Para usuários do sistema operacional Windows, o interpretador para Python deve ser
baixado através do site http://www.python.org e instalado. Neste último sistema o usuário encontra um
utilitário para fazer o papel de terminal (e editor de python) no Windows, denominado IDLE.
Variáveis são formas de se armazenar dados para uso posterior. Estas podem ser classificadas em 3
tipos básicos (existem outros, que serão abordados mais a frente):
- int - Um número inteiro;
- foat - Um ponto futuante;
- string - Uma sequência de caracteres.

Ao contrário da maioria das outras linguagens, em Python, não é necessário declarar as variáveis que
serão usadas, tampouco definir seu tipo. A própria sintaxe do dado a ser armazenado identifica o tipo da
variável para armazená-lo. Por exemplo, caso desejasse atribuir o valor 3 à variável A, basta digitar A=3.
Python saberá que A é um inteiro(tipo “int"). Por outro lado, se o valor a ser armazenado fosse 3,2 que é
um dado do tipo “ponto flutuante”, este deveria ser expresso como A=3.2. Observe que, para Python, A=3
e B=3.0 são variáveis de tipos diferentes e isto deve ser levado em conta ao se realizar certos tipos de
manipulações de dados.

282
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Strings

String é um tipo de objeto formado por uma sequência imutável de caracteres que nos permite trabalhar
com textos. Exemplo:

>>> a = “Bom Dia”


>>> print a
Bom Dia

Percebemos que elas são delimitadas por aspas, podemos utilizar tanto aspas duplas como as
simples. Se utilizarmos aspas duplas, como o mostrado no exemplo acima, podemos usar as simples
para aplicações dentro do texto que estamos escrevendo, o contrário também é verdadeiro.
Exemplo:

>>> b = 'O lema do governo JK era:\n “Cinquenta anos em cinco.”


>>> print b

O lema do governo JK era:


“Cinquenta anos em cinco.”

No exemplo acima utilizamos um outro artifício para trabalharmos com strings, o \n. Este por sua vez,
tem a função de pular uma linhae escrever o texto, que está depois do \n, nessa nova linha. Tanto isso é
verdade que no mesmo exemplo, quando usamos o comando print é mostrada a parte do texto que diz:
“Cinquenta anos em cinco.”, impresso na linha seguinte.
Outro artifício parecido com \n, é o \t que tem o objetivo de acrescentar ao texto que vem após, um
espaço de tabulação.
Há também outra aplicação para as aspas, que consiste na delimitação do texto por três aspas (duplas
ou simples).
Exemplo:

>>> a = “Quatro times do Rio de Janeiro:


Botafogo
Vasco
Fluminense
Flamengo”
>>> print a

Quatro times do Rio de Janeiro:


Botafogo
Vasco
Fluminense
Flamengo

Nessa aplicação para as aspas, quando usamos o comando print, tudo o que estiver entre as três
aspas será impresso exatamente da mesma forma como foi escrito.

Manipulando Strings.

Pelo fato de uma string ser uma sequência imutável, isso nos dá a possibilidade de manipularmos essa
sequência, consideremos o exemplo abaixo:

>>> a = 'matemática'
>>> a[2]+a[-5]+a[-4:]
'tática'

Isso nos mostra que uma string segue uma determinada indexação onde cada caractere assume um
endereço que, pode ser acessado colocando o nome da variável, que contém a string, e após, entre os
colchetes, o endereço da célula que contém o caractere desejado.

283
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Existe também um recurso conhecido como operador %, que serve para formatar as strings,
basicamente são três os tipos de formatação que temos:

- %s - Serve para substituir string;


- %d - Serve para substituir números inteiros em uma frase destinada a um print;
- %f - Serve substituir floats (números em aritmética de ponto flutuante).

Observação: as três formatações acima relacionadas são normalmente para aplicações em uma frase
destinada a um print.
Exemplo:
>>> compra= 'maçã'
>>> tipo='verde'
>>> quilos = 1,5
>>> print 'Maria comprou %f quilos de %s %s .' %(quilos,compra,tipo)

Maria comprou 1,5 quilos de maça verde.

Observação: com vimos acima o operador % pode ser utilizado para formatação de números também.
Com ele é possível também determinar a quantidade de números, após a vírgula de um float.
Exemplo:

>>> num=245.47876749
>>> print '%.2f' %(num)

245.48

Percebemos que Python fez uma aproximação do número real, possibilitando que o resultado de uma
futura operação seja o mais preciso possível.

Operações Matemáticas

Além de ser uma poderosa linguagem de programação, Python sabe lidar bem com matemática. Suas
capacidades matemáticas se extendem desde operações básicas até operações com números
complexos.
Abaixo, vamos começar aprendendo as 4 operações matemáticas básicas. Lembre-se que “>>>”
significa a digitação de um comando no interpretador.

Soma
>>>2+3
5

Subtração
>>>2-3
-1

Multiplicação
>>>2*3
6

Divisão
>>>2/3
0

Na divisão, pode-se optar por resultados como números inteiros (ou o inteiro mais próximo) ou
resultado exato (um ponto flutuante). Para isso, deve-se fornecer um inteiro (para se obter um inteiro
como resultado) ou um ponto flutuante (para se obter o resultado exato).

284
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Divisão
>>>2.0/3.0
0.66666666666666663

A operação aritmética de potenciação também pode ser feita, assim como a sua inversa, a radiciação.

Potenciação
>>> 2**3

Não existe um comando específico para a radiciação em Python (exceto para a raiz quadrada).
Entretanto, para obter a raiz n-ésima de um número basta elevá-lo por 1/n onde “n” é o índice da raiz. As
regras para a obtenção de números inteiros ou pontos flututante também se aplicam a este caso.

Radiciação
>>>8**(1.0/3.0)
2.0
8

Questão

(TER/CE - Técnico Judiciário - FCC). Sobre Python é correto afirmar:

(A) É uma linguagem compilada, ou seja, o código-fonte de um programa é lido pelo compilador, que
cria um arquivo binário, executável diretamente pelo hardware.
(B) É uma linguagem fortemente tipada, ou seja, é preciso declarar variáveis e seus tipos.
(C) Suporta funcionalidades comuns na orientação a objetos: herança, herança múltipla, polimorfismo,
reflexão e introspecção.
(D) Uma lista em Python é um conjunto de valores acessados por um índice numérico, inteiro,
começando em 1. Assim como em outras linguagens, a lista pode armazenar apenas valores de um
mesmo tipo
(E) Uma String Python á uma sequência imutável, alocada estaticamente, com restrição de tamanho.

Gabarito

Comentário

Resposta: C
Quanto ao Phyton:
1- Suporta manipulação de exceções;
2 - Eficiente gerenciamento de memória;
3 - É uma linguagem interpretada;
4- Free;
5- Traz consigo inúmeras bibliotecas, além de possibilitar integração com outras;
6- Utiliza uma linha em branco como definição de blocos;
7- Fácil entendimento quanto a projeção de: construtores, loops, estruturas condicionais e arrays,
dentre outros recursos;
8- Implementada em C e extensível as suas bibliotecas.

Analytics (bibliotecas Pandas, NumPy, SciPy, Matplotlib e Scikit-learn).

Existem muitas bibliotecas disponíveis para realizar a análise de dados em Python. Algumas são de
grande importância para a maioria das tarefas de análise de dados.
Estas bibliotecas fornecem apoio para a matemática, ciência, estatística e engenharia. Entre as
principais bibliotecas estão: Pandas, NumPy, SciPy, Matplotlib, e Scikit-learn.

285
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Pandas
Pandas é uma biblioteca de alto desempenho que fornece suporte para estruturas de dados e
ferramentas de análise de dados71. A biblioteca é otimizada para executar tarefas de Ciência de Dados
de forma rápida e eficiente. O princípio básico do Pandas é fornecer análise de dados e suporte a
modelagem para Python de forma semelhante a outras línguas, como o R.

Quadro de dados
Em uma simples definição, DataFrame é como se fosse uma planilha de Excel ou uma tabela de banco
de dados72. É composto por colunas, linhas e índice. Quando nós lemos algum arquivo de dados, ele se
torna um DataFrame para o Pandas.

Instalando Pandas
# Celula do jupyter
! Pip instalar pandas
# Terminal
pip install pandas

Importando a biblioteca
importar pandas como pd

Lendo arquivo de dados


Existem muitas funções nativas para ler seus dados, normalmente iniciando por pd.read_[extensão]

CSV
df = pd.read_csv('caminho_arquivo.csv', sep='separador')
df = pd.read_csv ('vendas_202005.csv', set = ';')

Excel
df = pd.read_excel('caminho_arquivo.xlsx', sheet_name='guia')
df = pd.read_excel ('vendas_202005.xlsx', sheet_name = 'Jan')
Conhecendo seus dados

Head
df.head ()

T (Transposição)
df.T

71
https://www.cienciaedados.com/usando-python-em-data-science
72
RIBEIRO, Lucas. Introdução a Biblioteca Pandas.

286
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Visualizações as colunas em formato de linhas.

Dimensões do DF
Retorna a quantidade de linhas e colunas.
df.shape

Informações
df.info ()

Vermelho: retorna número de linhas não nulas.


Amarelo: Tipo de cada coluna.
Verde: Memória Usada.

Estatísticas Descritivas
Retorna medidas de tendência central.
df.describe ()

Quantidade, média, desvio padrão, valor mínimo, 25% da coluna, 50% da coluna (média), 75% da coluna e valor máximo.

Trabalhando com colunas

Adicionando uma nova coluna


df['nome_da_coluna'] = valor ou condição que retorna um valor
df ['nm_mes'] = df ['data']. dt.month_name ()

287
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Excluindo uma coluna
# Tome cuidado, pois irá apagar do seu DataFrame.
#Não apaga do arquivo.
# Se apagar sem querer, só ler novamente o arquivo inicial.
del df ['nome_da_coluna']

Filtrando o DataFrame
#Uma Condição
df[ df['nm_coluna' == 'XPTO' ] ]
#Múltiplas Condições
df[ (condição 1) & (condição 2) ... ]
#Exemplo
df [(df ['date']> = '2020-05-01') & (df ['date'] <= '2020-05-31']]

Pivotando ou agrupando

Pivotando
pd.pivot_table (df #Nome do DataFrame
, índice = "dia" #Linhas
, colunas = "month_nm" #Colunas
, values = "price" #Valor
, aggfunc = "mean" # Função de Agregação

Agrupamento
df.groupby (['month_nm', 'day']). agg (
{'price': 'mean'
, 'order_id': 'count'
}
) .reset_index ()

288
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Visualização
Geralmente adicionamos gráficos em uma variável para usar outras propriedades como título, nome
do eixo y, nome eixo x, legenda e cores.

BoxPlot
ax = df.boxplot (coluna = ['preço'])

Gráfico de barras
ax = df.plot.bar (x = 'mês', y = 'preço', figsize = (16,5), rot = 0)

Gráfico de linhas
ax = df.plot.line (x = 'data', y = 'preço', figsize = (16,5), marcador = 'o', legenda = ['preço'])
ax.set_xlabel ('Data')
ax.set_ylabel ('Price')
ax.set_title ('dia após dia x preço total de venda')
ax

Gráfico de Pizza
ax = df.plot.pie (x = 'month_nm', y = 'preço', figsize = (8,8))

289
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Histograma
ax = df ['preço']. hist (figsize = (10,5))

NumPy
NumPy é o pacote fundamental para computação científica com Python. Ele permite, entre outras
coisas:
- Modelos de Machine Learning: ao escrever algoritmos de Machine Learning, supõe-se que se
realize vários cálculos numéricos em Array73. Por exemplo, multiplicação de Arrays, transposição, adição,
etc. O NumPy fornece uma excelente biblioteca para cálculos fáceis (em termos de escrita de código) e
rápidos (em termos de velocidade). Os Arrays NumPy são usados para armazenar os dados de
treinamento, bem como os parâmetros dos modelos de Machine Learning.
- Processamento de Imagem e Computação Gráfica: imagens no computador são representadas
como Arrays Multidimensionais de números. NumPy torna-se a escolha mais natural para o mesmo. O
NumPy, na verdade, fornece algumas excelentes funções de biblioteca para rápida manipulação de
imagens. Alguns exemplos são o espelhamento de uma imagem, a rotação de uma imagem por um
determinado ângulo etc.
- Tarefas matemáticas: NumPy é bastante útil para executar várias tarefas matemáticas como
integração numérica, diferenciação, interpolação, extrapolação e muitas outras. O NumPy possui também
funções incorporadas para álgebra linear e geração de números aleatórios. É uma biblioteca que pode
ser usada em conjunto do SciPy e Matplotlib. Substituindo o MATLAB quando se trata de tarefas
matemáticas.
- Manipulação de matriz n-dimensional (uma matriz multidimensional rápida e eficiente que permite a
vetorização de operações aritméticas), que é fundamental para o trabalho em Ciência de Dados.
- Ferramentas para a integração de código C / C++ e Fortran, permitindo transferir dados para
bibliotecas externas escritas nestas linguagens.

Além das suas utilizações científicas óbvias, NumPy também pode ser utilizado como um recipiente
multidimensional de dados genéricos. Tipos de dados arbitrários podem ser definidos. Isso permite que
NumPy, de forma transparente e rápida, se integre com uma ampla variedade de bancos de dados.

73
JR, Luiz Santiago. Entendendo a biblioteca NumPy.

290
1678859 E-book gerado especialmente para DANIEL CRISTIAN
NumPy não fornece a funcionalidade de análise de dados de alto nível, mas fornece operações com
matrizes, que tornam o trabalho de análise de dados com o Pandas, muito mais eficiente.

Arrays NumPy
A estrutura de dados mais importante que o NumPy fornece é um objeto poderoso, um tipo de Array,
chamada ndarray. O objeto ndarray consiste em um segmento unidimensional contíguo da memória do
computador, combinado com um esquema de indexação que mapeia cada item para um local no bloco
de memória. O bloco de memória contém os elementos em uma ordem principal de linha (estilo a
linguagem C) ou uma ordem de coluna principal (estilo FORTRAN ou MATLAB), mas não vamos nos
aprofundar tanto nesses detalhes. Essa Array NumPy é uma tabela de elementos (geralmente números),
todos do mesmo tipo, indexados por uma tupla de inteiros positivos. No NumPy, as dimensões são
chamadas de eixos. Por exemplo, as coordenadas de um ponto no espaço 3D: [1, 2, 1] têm um eixo. Esse
eixo tem 3 elementos, então dizemos que ele tem um comprimento de 3. No exemplo mostrado abaixo,
a Array tem 2 eixos. O primeiro eixo tem um comprimento de 2, o segundo eixo tem um comprimento de
3:
Exemplo sobre a estrutura da tabela de elementos na Array NumPy:
[[ 1., 0., 0.]
[ 0., 1., 2.]]
Exemplificando, vamos imaginar que os eixos sejam como uma tabela que fazemos no Excel por
exemplo, onde temos linhas e colunas. No exemplo acima, temos 2 linhas e 3 colunas por assim dizer.

Ou seja, no exemplo da estrutura de elementos que visualizamos anteriormente tem o primeiro eixo
com o comprimento de 2, sendo essas as linhas. O segundo eixo com o comprimento de 3 sendo assim
as colunas. Olhando dessa maneira e mentalizando essa ideia, fica bem mais simples de aplica um
‘slicing’ ou fatiamento em uma Array Numpy.
Um Array NumPy é uma extensão de um Array Python, que lida apenas com Arrays Unidimensionais
e oferece menos funcionalidade. Os Arrays NumPy são equipados com um grande número de funções e
operadores que ajudam a escrever rapidamente códigos de alto desempenho para vários tipos de cálculos
que discutimos acima. Vamos ver a seguir como podemos definir rapidamente um Array NumPy
Unidimensional.

Arrays NumPy Unidimensional


Vamos começar com o seguinte código:
import numpy as np
my_array = np.array([1, 2, 3, 4, 5])
print(my_array)

No exemplo acima, nós importamos a biblioteca NumPy usando import numpy as np, veja que essa
forma de importar e nomear como np é uma forma padrão muito utilizada. Então seguindo adiante nós
criamos um Array NumPy simples de 5 números inteiros e depois o imprimimos. Vá em frente e
experimente em sua própria máquina. Use as etapas na seção “Instalação da biblioteca NumPy”, descrita
acima para certificar-se de que você instalou a biblioteca NumPy em sua máquina. Vamos ver agora o
que podemos fazer com esse Array NumPy em particular.
my_array = np.array([1, 2, 3, 4, 5])

291
1678859 E-book gerado especialmente para DANIEL CRISTIAN
print(my_array.shape)

Isso imprimirá a forma da array que criamos tendo como resultado:


(5,)

Isso indica que my_array é uma Array com 5 elementos. O atributo shape retorna uma tupla que
consiste nas dimensões da Array, mostrando quantas linha e colunas temos para exemplificar.
Percebemos que na saída mostrou apenas 5 elementos, por conta de existir apenas uma linha ou por ser
uma única dimensão por assim dizer. Caso fosse uma Array Bidimensional por exemplo seria algo como:

Input:
my_array = np.array([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]])

print(my_array)
Output:
(2, 5)

Agora ele retorna mostrando que temos 2 dimensões com 5 elementos.


Também podemos usar o atributo reshape para redimensionar a Array NumPy:
a = np.arange(6).reshape((3, 2))
print(a)

Output:
array([[0, 1],
[2, 3],
[4, 5]])

Podemos também imprimir os elementos individuais. Assim como um Array Python, os Arrays NumPy
são indexados começando do 0.
print(my_array[0])
print(my_array[1])

Os comandos acima imprimirão 1 e 2 respectivamente no terminal. Falaremos mais sobre indexação


logo adiante.
Também podemos modificar os elementos de um Array. Por exemplo, suponha que escrevamos o
seguinte comando:
my_array[0] = -1
print(my_array)

Output:
[-1, 2, 3, 4, 5]

Agora, suponha que queremos criar uma Array NumPy vazia? podemos usar a função empty, Ele cria
uma Array não inicializada e dtype especificados:
np.empty(shape, dtype = float, order = 'C')

O código a seguir mostra um exemplo de uma Array NumPy vazia:


import numpy as np
x = np.empty([3,2], dtype = int)
print(x)

Output:
[[22649312 1701344351]
[1818321759 1885959276]
[16779776 156368896]]

Os elementos acima mostram valores aleatórios, pois não são inicializados.


Agora digamos que queremos criar um Array NumPy que retorne todos os valores 0?
A função zeros torna isso simples e fácil, vejamos um exemplo a seguir:

292
1678859 E-book gerado especialmente para DANIEL CRISTIAN
my_new_array = np.zeros((5))
print(my_new_array)

Output:
[0, 0, 0, 0, 0]

Semelhante a função zeros nós também temos a função ones e de forma semelhante retornaremos:
[1, 1, 1, 1, 1]

E se quisermos criar uma Array de valores aleatórios?


my_random_array = np.random.random((5))
print(my_random_array)

Output:
[0.48691037 0.70332792 0.8123029 0.18101876 0.82146451]

Vamos imprimir números aleatórios que esteja entre 0 e 10:


a = np.floor(10*np.random.random((3,4)))
print(a)

Output:
[[7. 9. 0. 3.]
[4. 0. 9. 1.]
[1. 7. 9. 6.]]

É claro que a saída que você terá pode variar já que estamos usando uma função aleatória que atribui
a cada elemento um valor aleatório.
Vamos voltar a falar um pouco mais sobre indexação.
my_array2 = np.array([3, 2, 8, 22, 127])
my_array2[3]

Output:
(22,)

Elemento que se encontra no índice 3.


Podemos ir mais a fundo por fatiar mais elementos dentro da Array, por exemplo:
my_array2 = np.array([3, 2, 8, 22, 127])
print(my_array2[1:])

Output:
array([ 2, 8, 22, 127])

Acima fatiamos todos os elementos a partir do índice 1, o mesmo pode ser feito de com o exemplo a
seguir:
my_array2 = np.array([3, 2, 8, 22, 127])
print(my_array2[2:4])

Output:
array([ 8, 22])

Agora fatiamos do índice 2 ao 4, porém o elemento 4 não será incluído como podemos ver na saída
acima.

Arrays NumPy Bidimensionais


Criando uma Array Numpy bidimensionais:
my_array3 = np.zeros((2, 3))
print(my_array3)

293
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Output:
[[0. 0. 0.]
[0. 0. 0.]]

Agora vamos usar a função ones em nosso array bidimensionais:


my_array4 = np.ones((2, 4))
print(my_array4)

Output:
[[1. 1. 1. 1.]
[1. 1. 1. 1.]]

Vamos prosseguir:
my_array = np.array([[4, 5], [6, 1]])
print(my_array[0][1])

A saída do trecho de código acima é 5, uma vez que é o elemento presente na coluna 0 do índice e
na coluna do índice 1.
Você também pode imprimir a forma de my_array da seguinte maneira:
print(my_array.shape)

A saída é (2, 2), indicando que há 2 linhas e 2 colunas na matriz.


O NumPy fornece uma maneira poderosa de extrair linhas/colunas de um array multidimensional. Por
exemplo, considere o exemplo my_array que definimos acima.
my_array = np.array([[4, 5], [6, 1]])
print(my_array)

Output:
[[4 5]
[6 1]]

Suponha que queremos extrair todos os elementos da segunda coluna (índice 1) dela. Aqui, como
pode ser visto, a segunda coluna é composta de dois elementos: 5 e 1. Para fazer isso, podemos fazer o
seguinte:
my_array_column_2 = my_array[:, 1]
print(my_array_column_2)

Observe que, em vez de um número de linha, fornecemos dois-pontos ( : ) e, para o número da coluna,
usamos o valor 1. A saída será: [5, 1].
Podemos similarmente extrair uma linha de um array NumPy multidimensional. Agora, vamos ver o
poder que o NumPy oferece quando se trata de realizar cálculos em vários arrays.

Manipulando Arrays NumPy


Usando o NumPy, você pode facilmente executar cálculos matemáticos em Arrays. Por exemplo, você
pode somar Arrays NumPy, subtraí-los, multiplicá-los e até dividi-los. Aqui estão alguns exemplos disso:
import numpy as np
a = np.array([[1.0, 2.0], [3.0, 4.0]])
b = np.array([[5.0, 6.0], [7.0, 8.0]])
sum = a + b # Soma
difference = a - b # Subtração
product = a * b # Multiplicação
quotient = a / b # Divisão
print('Sum = \n', + sum)
print('\n')
print('Difference = \n', + difference)
print('\n')
print('Product = \n', + product)
print('\n')
print('Quotient = \n', + quotient)

294
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Output:
Sum =
[[ 6. 8.]
[10. 12.]]
Difference =
[[-4. -4.]
[-4. -4.]]
Product =
[[ 5. 12.]
[21. 32.]]
Quotient =
[[0.2 0.33333333]
[0.42857143 0.5 ]]

Como você pode ver, o operador de multiplicação executa a multiplicação por elementos em vez da
multiplicação de arrays. Para executar a multiplicação de arrays, você pode fazer o seguinte:
matrix_product = a.dot(b)

print('Matrix Product = \n', + matrix_product)

Output:
Matrix Product =
[[19. 22.]
[43. 50.]]

A função dot executa a multiplicação de Arrays.

SciPy
A biblioteca SciPy depende da biblioteca NumPy, que fornece manipulação de matriz n-dimensional
de forma prática e rápida74. A biblioteca SciPy foi construída para trabalhar com matrizes e fornece muitas
rotinas numéricas de fácil utilização e eficientes, como rotinas de integração e otimização numérica. SciPy
possui módulos para otimização, álgebra linear, integração e outras tarefas comuns na Ciência de Dados.
O SciPy pode ser comparado a outras bibliotecas de computação científica padrão, como a GSL (GNU
Scientific Library para C e C + +), ou caixas de ferramentas do Matlab75. O SciPy é o pacote principal de
rotinas científicas em Python, que se destina a operar de forma eficiente em matrizes numpy, de modo
que numpy e scipy trabalhem lado a lado.
Antes de implementar uma rotina, vale a pena verificar se o processamento de dados desejado não
está implementado em SciPy. Como os programadores não-profissionais, os cientistas tendem a
reinventar a roda, o que leva a códigos com bugs, não ideais, e difíceis de compartilhar e insustentáveis.
Por outro lado, as rotinas de scipy são otimizados e testadas, e devem, portanto, ser usadas sempre que
possível.
Os módulos do SciPy abordados são:
- File input/output: scipy.io
- Special functions: scipy.special
- Linear algebra operations: scipy.linalg
- Fast Fourier transforms: scipy.fftpack
- Optimization and fit: scipy.optimize
- Statistics and random numbers: scipy.stats
- Interpolation: scipy.interpolate
- Numerical integration: scipy.integrate
- Signal processing: scipy.signal
- Image processing: scipy.ndimage

O SciPy é composto de submódulos para tarefas específicas como:

74
https://www.guru99.com/scipy-tutorial.html
75
http://www.estruturas.ufpr.br/disciplinas/pos-graduacao/introducao-a-computacao-cientifica-com-python/introducao-python/capitulo-4-scipy/

295
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Todos dependem do Numpy, mas são em grande parte independentes uns dos outros. A forma padrão
de importar Numpy e estes módulos SciPy é:
import numpy as np
from scipy import stats # o mesmo para outros submódulos

Antes de aprender SciPy Python, é preciso conhecer a funcionalidade básica, bem como os diferentes
tipos de um array de NumPy
A maneira padrão de importar módulos SciPy e Numpy:
from scipy import especial #same para outros módulos
importar numpy como np

Pacote de entrada/saída de arquivo:


Scipy, pacote de E / S, possui uma ampla gama de funções para trabalhar com diferentes formatos de
arquivos que são Matlab, Arff, Wave, Matrix Market, IDL, NetCDF, TXT, CSV e formato binário.
Tomemos um exemplo de formato de arquivo Python SciPy que é usado regularmente no MatLab:
importar numpy como np
de importação de scipy como sio
array = np.ones ((4, 4))
sio.savemat ('example.mat', {'ar': array})
data = sio.loadmat ('example.mat', struct_as_record = True)
dados ['ar']

Resultado:
array ([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])

Explicação do código
Linha 1 e 2: importe a biblioteca SciPy essencial em Python com pacote de E / S e Numpy.
Linha 3: Crie uma matriz de dimensão 4 x 4
Linha 4: Armazene a matriz no arquivo example.mat .
Linha 5: Obtenha dados do arquivo example.mat
Linha 6: saída de impressão.

296
1678859 E-book gerado especialmente para DANIEL CRISTIAN
ajuda (scipy.special)
Resultado :
NOME
scipy.special

DESCRIÇÃO
==============================================
Funções especiais (: mod: `scipy.special`)
==============================================

.. module :: scipy.special

Álgebra Linear com SciPy


A Álgebra Linear do SciPy é uma implementação das bibliotecas BLAS e ATLAS LAPACK.
O desempenho da Álgebra Linear é muito rápido em comparação com BLAS e LAPACK.
A rotina de álgebra linear aceita objetos de matriz bidimensional e a saída também é uma matriz
bidimensional.
Agora vamos fazer alguns testes com scipy.linalg,
Calculando o determinante de uma matriz bidimensional:
de importação scipy linalg
importar numpy como np
# define matriz quadrada
two_d_array = np.array ([[4,5], [3,2]])
#passar valores para a função det ()
linalg.det (two_d_array)
Saída: -7,0

Matriz Inversa
scipy.linalg.inv ()

Inverse Matrix of Scipy calcula o inverso de qualquer matriz quadrada.


de importação scipy linalg
importar numpy como np
# define matriz quadrada
two_d_array = np.array ([[4,5], [3,2]])
#pass value to function inv ()
linalg.inv (two_d_array)

Resultado:
matriz ([[-0,28571429, 0,71428571],
[0,42857143, -0,57142857]])

Valores próprios e vetor próprio


scipy.linalg.eig ()

O problema mais comum em álgebra linear são os autovalores e o autovetor, que podem ser facilmente
resolvidos usando a função eig () .
Agora vamos encontrar o valor próprio de ( X ) e corresponder ao vetor próprio de uma matriz quadrada
bidimensional.
Exemplo:
de importação scipy linalg
importar numpy como np
# define array bidimensional
arr = np.array ([[5,4], [6,3]])
#pass value into function
eg_val, eg_vect = linalg.eig (arr)
#get eigenvalues
imprimir (eg_val)
#get eigenvectors

297
1678859 E-book gerado especialmente para DANIEL CRISTIAN
imprimir (eg_vect)

Resultado:
[9. + 0.j -1. + 0.j] # valores próprios
[[0.70710678 -0.5547002] #eigenvetores
[0,70710678 0,83205029]]

Matplotlib
Matlplotlib é um módulo Python para visualização de dados. Matplotlib permite que você crie facilmente
gráfico, histogramas e outras figuras profissionais. Usando Matplotlib você pode personalizar cada
aspecto de uma figura. Quando usado no IPython, Matplotlib tem recursos interativos, como zoom e visão
panorâmica. Ele possui suporte em todos os sistemas operacionais e também pode exportar gráficos para
vetor comum e formatos gráficos: pdf, svg, jpg, png, bmp, gif, etc.
Matplotlib é uma biblioteca Python de plotagem que auxilia a biblioteca matemática NumPy76.
Pode ser usada em scripts Python, no shell Python e IPython, em servidores de aplicação web e outras
ferramentas de interface gráfica.
A seguir, mostramos como plotar alguns gráficos usando matplotlib.
#importando a classe, da forma mais usual
import matplotlib.pyplot as plt
#definindo variáveis que vamos usar nos exemplos
x = [1, 2, 3]
y = [10, 20, 30]
y2 = [15, 10, 40]
y3 = [20, 10, 35]
yBar = [3, 10, 7, 5, 3, 4.5, 6, 8.1]
z = [i * 1.5 for i in yBar]
xBar = range(len(yBar))
azul = "blue"
verde = "green"
preto = "black"

Desenhando uma linha:


def plota_linha_1():
plt.plot(x, y)

Adicionando rótulos e título


def plota_linha_2():
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Título do gráfico')
plt.plot(x, y)

76
SCHÖNHOFEN, R. D. Guia básico para plotar gráficos usando o matplotlib do Python

298
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Gráfico com mais de uma linha
def plota_linhas_3():
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Título do gráfico')
plt.plot(x, y)
plt.plot(x, y2)
plt.plot(x, y3)

Gráfico de barras
def plota_barra_1():
plt.bar(xBar, yBar, color=azul)

Gráfico de barras com linha


plt.bar(xBar, yBar, color=preto, align='center')
plt.plot(xBar, z, color=verde, marker='o', linestyle='--', linewidth=2)

299
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Como plotar gráficos usando matplotlib
#importando as classes da forma mais usual
import matplotlib.pyplot as plt
import numpy as np

#definindo variáveis usadas nos exemplos


pop = {"São Paulo": 12000000, "Rio": 7000000, "Salvador": 4000000, "Recife": 3500000, "Porto
Alegre": 2500000}
cidades = [i for i in pop.keys()]
populacao = [j for j in pop.values()]
popPos = np.arange(len(cidades))
azul = "blue"
verde = "green"
preto = "black"

Desenhando um gráfico de barras horizontal


def plota_barra_h_1():
plt.barh(popPos, populacao, align='center', color=azul)
plt.yticks(popPos, cidades)
plt.xlabel('População')
plt.title('População de cidades brasileiras')

Desenhando um gráfico de duas barras


def plota_bar_dupla_1():
grupos = 5
media_chico = (9.1, 5.4, 4.0, 7.5, 7.0)
media_joao = (8.6, 6.3, 5.5, 3.5, 5.6)
fig, ax = plt.subplots()
indice = np.arange(grupos)

300
1678859 E-book gerado especialmente para DANIEL CRISTIAN
bar_larg = 0.4
transp = 0.7
plt.bar(indice, media_chico, bar_larg, alpha=transp, color=azul, label='Chico')
plt.bar(indice + bar_larg, media_joao, bar_larg, alpha=transp, color=verde, label='João')

plt.xlabel('Matéria')
plt.ylabel('Notas')
plt.title('Notas por pessoa')
plt.xticks(indice + bar_larg, ('Matemática', 'Português', 'Biologia', 'Física', 'Química'))
plt.legend()
plt.tight_layout()
plt.show()

Desenhando um gráfico de pizza


def plota_pizza_1():
labels = ['Corinthias', 'Palmeiras', 'Santos', 'São Paulo']
titulos = [27, 22, 22, 22]
cores = ['lightblue', 'green', 'white', 'red']
explode = (0.1, 0, 0, 0) # somente explode primeiro pedaço
total = sum(titulos)
plt.pie(titulos, explode=explode, labels=labels, colors=cores, autopct=lambda p: '{:.0f}'.format(p *
total / 100), shadow=True, startangle=90)

# Determina que as proporções sejam iguais ('equal') de modo a desenhar o círculo


plt.axis('equal')
plt.show()

301
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Desenhando um gráfico de dispersão (Scatter Plot)
def plota_dispersao_1():
fig = plt.figure()
ax1 = fig.add_subplot(121)

#gera dados aleatórios


N=1000
x = np.random.randn(N)
y = np.random.randn(N)
ax1.scatter(x,y,color=azul,s=5,edgecolor='none')
#deixa os eixos quadrados
ax1.set_aspect(1./ax1.get_data_ratio())
plt.show()

Gráfico de barras com linha


def plota_barra_linha_1():
plt.bar(xBar, yBar, color=preto, align='center')
plt.plot(xBar, z, color=verde, marker='o', linestyle='--', linewidth=2)

Scikit-learn
Scikit-learn é um módulo Python para Machine Learning. Ele fornece um conjunto de algoritmos de
aprendizagem de máquina comum aos usuários através de uma interface consistente. Scikit-learn ajuda
a implementar rapidamente algoritmos em seu conjunto de dados.
Esta biblioteca dispõe de ferramentas simples e eficientes para análise preditiva de dados, é reutilizável
em diferentes situações, possui código aberto, sendo acessível a todos e foi construída sobre os pacotes
NumPy, SciPy e Matplotilib77.

77
https://didatica.tech/a-biblioteca-scikit-learn-pyhton-para-machine-learning/

302
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Principais aplicações
O sklearn está organizado em muitos módulos, cada um desenvolvido para uma finalidade específica.
Nestes módulos encontraremos funções para as mais diferentes aplicações. Analisando estas diferentes
aplicações entenderemos a organização da biblioteca, e como encontrar o que buscamos.
- Pré-processamento: normalmente esta é a etapa mais trabalhosa no desenvolvimento de um
modelo de machine learning. Como já vimos, o NumPy e o Pandas são largamente utilizados nesta etapa,
mas também teremos funções para esta finalidade no sklearn, pensadas especialmente para tratamento
de dados que alimentarão algoritmos de machine learning.
- Classificação: desenvolvimento de modelos capazes de detectar a qual categoria pré-determinada
um elemento pertence. Podemos identificar se um aluno foi reprovado ou aprovado, se uma pessoa
possui ou não determinada doença, ou ainda qual doença uma pessoa pode ter dentre várias possíveis,
dentre muitas outras possibilidades.
- Regressão: desenvolvimento de modelos que podem atribuir um valor contínuo a um elemento.
Podemos prever o preço de um imóvel, altura de uma pessoa, quantidade de vendas de um produto, e
assim por diante.
- Clusterização: desenvolvimento de modelos para detecção automática de grupos com
características similares em seus integrantes. Podemos identificar clientes com comportamentos
parecidos, grupos de risco de determinada doença, verificar padrões entre moradores de uma cidade, e
muitos outros agrupamentos.
- Redução de dimensionalidade: reduzir o número de variáveis em um problema. Com esta redução
podemos diminuir consideravelmente a quantidade de cálculos necessários em um modelo, aumentando
a eficiência, com uma perde mínima de assertividade.
- Ajuste de parâmetros: comparar, validar e escolher parâmetros e modelos, de maneira
automatizada. Podemos facilmente comparar diferentes parâmetros no ajuste de um modelo,
encontrando assim a melhor configuração para a aplicação em questão.

Modelo de Regressão linear com sklearn


Para entender o funcionamento da biblioteca na prática, vamos utilizar algumas funções simples para
criar uma massa de dados, e um modelo de regressão linear.
Nosso primeiro passo será gerar os dados. Para essa finalidade temos a função make_regression(),
que faz parte do módulo datasets do sklearn.
Neste exemplo, iremos criar uma massa de dados com 200 observações, com apenas uma variável
preditora, que será a variável x e a variável target, que será a y. Para isso indicamos os parâmetros
n_samples = 200 e n_features = 1. O parâmetro noise define o quão dispersos os dados estarão um dos
outros. Quanto maior este valor, maior a dispersão. Fiquem a vontade para testarem valores maiores,
observando o efeito na criação da regressão.
from sklearn.datasets import make_regression
# gerando uma massa de dados:
x, y = make_regression(n_samples=200, n_features=1, noise=30)

Neste momento a melhor forma para visualizarmos os dados existentes nas variáveis x e y que
acabamos de criar é através de um gráfico. Para isso utilizaremos o pacote matplotlib, com o módulo
pyplot e a função scatter(), que criará o gráfico, e função show() que o exibirá na tela.
import matplotlib.pyplot as plt
# mostrando no gráfico:
plt.scatter(x,y)
plt.show()

303
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Com os dados gerados, já podemos iniciar a criação de nosso modelo de machine learning. Para
isso utilizaremos o módulo linear_model, e a função LinearRegression().
from sklearn.linear_model import LinearRegression
# Criação do modelo
modelo = LinearRegression()

Após esta execução, o objeto modelo que acabamos de criar está pronto para receber os dados que
darão origem ao modelo. Como não indicamos nenhum parâmetro específico na função, estamos
utilizando suas configurações padrão.
Agora precisamos apenas apresentar os dados ao modelo, e para isso temos o método fit(). Na
documentação da função podemos conferir todos os métodos que ela possui.
modelo.fit(x,y)

Após esta etapa, o modelo de machine learning está pronto e podemos utilizá-lo para prever dados
desconhecidos. Simplificando este primeiro entendimento, vamos apenas visualizar a reta de regressão
linear que o modelo gera, com os mesmos dados que criaram o modelo. Para isso iremos utilizar o método
predict(), indicando que queremos aplicar a previsão nos valores de x. O resultado do método será uma
previsão de y para cada valor de x apresentado.
modelo.predict(x)

Como podemos ver, estes valores soltos não nos dizem muito. Precisamos analisá-los de outra forma.
Neste caso, uma boa opção é através da reta de regressão que eles geram. Poderíamos utilizar os
atributos coef_ e intercept_ do modelo, sendo eles respectivamente o coeficiente angular e linear de nossa
reta, e com estes valores visualizá-la em um gráfico, porém existe uma opção ainda mais simples.
A função plot() do pacote pyplot gera uma reta com os dados apresentados. Como já temos os dados
de x e y, basta indicá-los na função. Assim, primeiramente montamos novamente o gráfico de x e y original
com a função scatter(), e somamos a ele a reta de regressão criada a partir dos valores de x, e dos valores
previstos de y. Para melhorar a visualização estamos indicando que a cor da reta será vermelha, e
aumentando sua espessura.
plt.scatter(x,y)
plt.plot(x, modelo.predict(x), color='red', linewidth=3)
plt.show()

304
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Esta é a reta que melhor se ajusta aos dados existentes no gráfico, e foi gerada através do modelo de
regressão linear que criamos. Com ela podemos prever qualquer valor de y sabendo o valor x.

Questões

01. (TER/SP - Técnico Judiciário - FCC) Considere o programa Python, abaixo.

import as b

import matplotlib.pyplot as a

x = b.linspace(0, 3, 20)

y = b.linspace(0, 9, 20)

a.plot(x, y)

a.plot(x, y, 'o')

a.show()

A lacuna I deve ser preenchida corretamente com


(A) numpy.
(B) matrix.
(C) mathlab.
(D) numberplot.
(E) array.

02. São exemplos de bibliotecas disponíveis para a análise de dados em Python, EXCETO:
(A) Pandas
(B) JAR
(C) SciPy
(D) Scikit-learn
(E) NumPy

03. A biblioteca Python para visualização de dados que permite que criar gráficos e histogramas
facilmente é:
(A) SciPy
(B) Pandas
(C) NumPy
(D) Scikit-learn
(E) Matplotlib

Gabarito
01.A / 02.B / 03.E

305
1678859 E-book gerado especialmente para DANIEL CRISTIAN

You might also like