Professional Documents
Culture Documents
07 Tecnologia Da Informacao
07 Tecnologia Da Informacao
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.
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).
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.
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:
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.
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
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.
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.
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.
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.
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 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.
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.
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.
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.
NOSQL
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.
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.
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.
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:
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
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.
Para a criação uma tabela é necessário além de utilizar os comandos básicos incluir os atributos da
tabela:
Veja um exemplo para criação de uma tabela para armazenar informações de cliente:
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)
);
Ou
Insert into clientes values (1,'Luis Alexandre', 'Tupã', 'M','le.boyago@gmail.com');
Ou
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.
Operadores Lógicos
= igual, > maior, >= maior ou igual, < menor, <= menor ou igual
Ex.:
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:
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
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;
Sum
Este comando calcula a somatória de todos os valores de uma coluna.
Ex: select sum(cli_salario) as valor_total from clientes;
Min
Esta função sql retorna o valor mínimo encontrado em uma coluna.
Ex: select min(cli_salario) as valor_minimo from clientes;
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
);
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;
Auto Incremento
Preenche automaticamente o conteúdo de um campo inteiro, toda vez que um novo registro for inserido
na tabela.
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.
Insert into clientes (cli_codigo, cli_nome, cli_cidade, cli_sexo, cli_email) values (1,'Luis Alexandre',
'Tupã', 'M','le.boyago@gmail.com');
Ou
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>.
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.
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:
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.
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
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
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.
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:
- 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.
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 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
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
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
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.
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.
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.
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.
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.
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
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:
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.
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 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.
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:
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.
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:
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.
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:
Após refinar as colunas de dados de cada tabela, você estará pronto para escolher a chave primária
de cada 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.
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.
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.
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.
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:
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.
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.
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).
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:
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
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
- 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.
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.
MySQL Workbench
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.
54
1678859 E-book gerado especialmente para DANIEL CRISTIAN
As ferramentas:
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
(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
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.
Gabarito
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.
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.
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 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”.
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.
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
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.
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.
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.
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.
63
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Questão
(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.
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).
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.
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.
- 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.
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.
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.
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:
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:
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:
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:
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
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:
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:
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.
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:
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.
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:
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:
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.
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:
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
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
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.
Figura 6 – CR Works_In
Para o exemplo do CR Works_In ilustrado na Figura 6, o seguinte comando SQL será usado no
mapeamento:
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
Figura 8 – CR Manages.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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 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.
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.
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.
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.
- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 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).
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.
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.
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
- 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 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.
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.
- 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.
- 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.
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
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.
Gabarito
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
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.
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)
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.
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.
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:
95
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplos:
SELECT *
FROM pai p, filho f
WHERE p.codpai = f.codpai
FOR SHARE;
4 - Uso de NOWAIT
BEGIN;
SELECT * FROM pai FOR UPDATE;
UPDATE pai SET nomepai = nomepai ' Father';
COMMIT;
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.
"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.
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');
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.
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.
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.
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.
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
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.
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;
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:
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
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).
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
- 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)
}
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;
- 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.
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.
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.
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.
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>) {
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.
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.
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 “..>”.
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)
Questões
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 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:
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:
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.
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.
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
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
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.
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.
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
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.
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”
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.
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 de Dados
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
Variável Conteúdo
Nome “Luis Alexandre Boaygo dos
Santos”
Idade 32
Peso 80,00
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:
2+3*8
Total := 4 *( 2 / valor ) + 3
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
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.
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:
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.
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.
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.
Orientação a Objetos
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)
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.
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.
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
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
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.
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;
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.
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.
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.
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
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
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
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.
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.
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.:
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
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
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);
}
relacionais lógicos
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çã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.
Ex.:
main()
{
printf("Este é o numero dois: %d",2);
printf("%s está a %d milhões de milhas\ndo sol","Vênus",67);
}
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);
}
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().
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:
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);
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",¬as[i]);
}
soma=0;
for(i=0;i<5;i++) soma=soma+notas[i];
printf("Media das notas: %d.",soma/5);
}
Matriz Multidimensional
Sintaxe:
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]);
}
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);
}
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);
}
main()
{
int x,*p,**q; x=10; p=&x;
q=&p;
printf("%d",**q);
}
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.
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 }
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');
}
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));
}
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 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]);
}
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]);
}
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]);
}
}
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.:
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.
Ex.:
#include <stdio.h>
main ()
{
FILE *fp; char ch;int nu,*pn; pn=ν
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
Variável Conteúdo
Nome “Luis Alexandre Boaygo dos Santos”
Idade 32
Peso 80,00
𝑣𝑎𝑙𝑜𝑟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:
2+3*8
Total := 4 *( 2 / valor ) + 3
Exemplos de utilização de operadores relacionais:
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.
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:
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.
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
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)
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.
185
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Exemplo da utilização de variáveis
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.
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
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
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.
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;
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.
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.
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.
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.
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
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
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
(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.
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.
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
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
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.
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.
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.
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
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.
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.
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 é:
208
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Sexta passagem: compara 4 com 5. 4 menor que 5.Permanece.
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
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.
É conveniente tratar as células como um novo tipo-de-dados e atribuir um nome a esse novo tipo:
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).
(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:
Á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.
Percursos em Árvore.
Existem três tipos de percursos: Percurso em InOrdem, PreOrdem e PosOrdem.
- InOrdem.
- 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 é:
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).
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.
O que é o Ansible?
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
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:
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:
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:
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:
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.
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.
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.
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.
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.
É 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.
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.
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.
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) {
}
}
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.
javap -c MeuPrograma
E a saída:
MeuPrograma();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: 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.
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:
- Se tentar acessar uma classe no diretório ou classpath errado, ou se o nome estiver errado, ocorrerá
o seguinte erro:
class X {
public void main (String[] args) {
System.out.println("Faltou o static, tente executar!");
}
}
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 */
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.
No mesmo momento que você declara uma variável, também é possível inicializá-la por praticidade:
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;
class TestaIdade {
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.
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!
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.
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.
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
int i = 3.14;
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;
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).
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:
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:
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:
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.
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.
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;
}
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:
Um exemplo é o a seguir:
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++;
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.
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 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.
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.
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);
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);
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:
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:
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:
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:
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:
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:
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:
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.
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;
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:
240
1678859 E-book gerado especialmente para DANIEL CRISTIAN
public class Teste {
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.
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];
}
}
}
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)
};
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.
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.
- 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:
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:
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:
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.
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:
- 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:
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:
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:
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();
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
Um cliente deste bean com estado de sessão pode acessar este bean da mesma forma que aparece
na imagem abaixo
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 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.
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.
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.
Por fim, o cliente injeta o bean através da interface, conforme exemplificado na imagem abaixo.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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).
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 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.
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.
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.
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”.
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.
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.
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.
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;
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:
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:
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:
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:
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)
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
(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.
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
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 ()
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.
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.
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 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)
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])
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')
Output:
[[22649312 1701344351]
[1818321759 1885959276]
[16779776 156368896]]
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]
Output:
[0.48691037 0.70332792 0.8123029 0.18101876 0.82146451]
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,)
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.
293
1678859 E-book gerado especialmente para DANIEL CRISTIAN
Output:
[[0. 0. 0.]
[0. 0. 0.]]
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)
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.
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)
Output:
Matrix Product =
[[19. 22.]
[43. 50.]]
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
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
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
Matriz Inversa
scipy.linalg.inv ()
Resultado:
matriz ([[-0,28571429, 0,71428571],
[0,42857143, -0,57142857]])
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"
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)
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
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()
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)
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.
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
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()
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