Você está na página 1de 51

Kit de Sobrevivência da

AV2 de Prog 2
Passo 1: Aprenda os
Pilares da POO
Como sabemos a Programação Orientada à Objetos (aka POO, não
pesquisem no google) é um paradigma de linguagem de
programação, ou seja, uma forma de construir um código para se
chegar em um resultado.

POO Solução
Eu e você
E para que esse paradigma seja bem utilizado, ele deve é construído
em cima de 4 pilares principais que são:
● Abstração;
● Encapsulamento;
● Herança;
● Polimorfismo.
Os pilares da POO Eu entendendo nada
Fiquem flinstons que eu vou explicar os pilares, começando pela…
Passo 1.1: Abstração
A Abstração nada mais é do que a forma como pegamos algo do
mundo real, que é complexo, simplificamos ao máximo e o
transformamos em algo que pode ser manipulável através do
código. Por exemplo:
Veja este carro, suponhamos que temos que fazer
um programinha onde possamos criar um carro
com: nome, marca e ano de fabricação.

Sabendo o que precisamos ter de informação dele,


fica óbvio que não precisamos informar outras
informações desnecessárias como a cor, formato,
tamanho da roda.
Ou seja, abstraiu-se (simplificou-se) ao máximo esse carro para que
usássemos somente o que era importante pro nosso programa.

Veja que ao saber de quais informações precisamos para criar a classe, todo o resto
deve ser ignorado, a abstração é um conceito bem teórico e você precisa fazer esse
planejamento antes de começar a criar seu sistema com POO.
Passo 1.2: Encapsulamento
Seguimos então para o Encapsulamento, que serve para proteger os
dados de dentro da classe, para evitar modificações acidentais ou
algo assim, vejamos:
Ainda no exemplo do carro, podemos notar que as informações dele podem ser
acessadas sem restrição alguma através do ponto:
Além também de poderem ter seus valores modificados:

E como dito antes, essa forma de manipular os dados


pode ser perigosa…
Por esse motivo, essa abordagem pode ser ruim, pois podemos
acidentalmente mudar ou ler uma informação. É por isso que com
base no Encapsulamento, o correto é criarmos:
● Getters: eles retornam uma informação de dentro da classe para
o usuário, a nomenclatura padrão para eles é começar com get.
● Setters: eles recebem uma informação de fora da classe para
alterar algo de dentro, a nomenclatura padrão para eles é
começar com set.
Veja um exemplo onde criamos getters e
setters para a nossa classe:
Jesus, que classe enorme! De fato, mas isso garante a
segurança do nosso código, nos permite criar regras
para a modificação de dados e até criar getters e
setters mais “amplos”, como por exemplo:

Aqui temos um getter e um setter que faz várias


operações de uma vez, o que pode economizar um
tempo dependendo do que precisamos.
Tá, mas como isso funciona? Vejamos:

Veja como o código ficou até mais claro, quando lemos set já percebemos que
estamos modificando um valor, e ao ler o get, percebemos que uma informação virá
de lá.
Agora usando o getter e setter das infos:

Uou! Esses dois métodos economizaram várias linhas de código e ainda deixaram o
código legível. Essa é a vantagem de usar o Encapsulamento, mas ainda tem uma
coisinha que podemos fazer que o JavaScript nos propõe.
Podemos usar as palavras reservadas “get” e “set” antes dos
métodos, e eles atuarão de acordo com seus papéis, vejamos o
antes e depois:
Perceba também que a forma como acessamos as propriedades
ficou um pouco diferente, veja:

Por que eu tô colocando um “_” antes do nome


das propriedades agora? Bom, isso acontece por
causa das palavras reservadas que colocamos
antes do nome do métodos, por exemplo:

Veja que o nome do método em si é “name”, o get


não faz mais parte do seu nome, mas ele indica
para o js que aquela é uma função que retorna um
dado.
E quando vamos acessá-la para ler seus dados, a chama é um pouco
diferente:

Note que estamos conseguindo ler a propriedade name, mas em tese, nós criamos
um “_name” lá no constructor, então de onde vem isso? Isso mesmo, do método
name com o “get” antes, a partir do momento em que se adiciona o “get” ou o “set”
como palavra reservada, ele começa a agir como uma propriedade do objeto,
doideira, não? Vejamos o caso do set.
Veja como o setter está atuando, não sendo chamado como um método, mas tendo
um valor atribuído à propriedade que ele quer modificar, em resumo, o que estiver
depois do “=” será usado como o parâmetro da função com a palavra reservada “set”:
E é por isso que não podemos nomear as propriedades com o mesmo
nome dos métodos com “get” e “set” antes, veja:

Veja que toda vez que atribuímos um valor à


propriedade name, na verdade chamamos o get de
name, e dentro dele, chamamos novamente ele
mesmo e isso acarreta em um loop infinito, por
isso devemos distinguir a propriedade do getter e
do setter.
Passo 1.3: Herança
Com essa explicação, podemos partir para a aplicação da Herança
nas classes. Ela nada mais é do que a forma como uma classe
(subclasse, classe filho) pode trazer propriedades e métodos de
outra classe (superclasse, ou classe pai). Ou seja, o “filho” herda de
seus “pais”.

Veja os emojis, o menor se assemelha ao maior,


sendo amarelo, tendo luvas, sendo feio igual. Isso
mostra que o menor é filho do maior (ou não, sei lá),
sendo um exemplo de herança.
Pense no seguinte, temos uma classe bem geral que representa um
animal, com nome e idade, e outra para o cachorro, que também
possui essas informações, com o adicional da raça:

Note que a classe torna-se redundante ao receber da mesma forma os mesmo


parâmetros de nome e idade. Sendo assim, a Herança vem para simplificar classes
que têm essências parecidas, sendo possível que eu crie outras classes que vem da
animal, como gatos, papagaios, o que quiser.
Por isso, podemos reescrever a classe assim:
Veja que para pegarmos os valores repetidos,
usamos a função super( ), que vai passar os
valores para o constructor da classe pai, ou seja:
E como isso fica na hora de ler os dados? Funciona normalmente,
veja:

Então notamos que mesmo uma instância


da classe filha da animal consegue
acessar propriedades que estão
presentes lá, como o nome e a idade.

O mesmo vale para os métodos:


Passo 1.4: Polimorfismo
Com a Herança na caixola, podemos entrar no último pilar: o
Polimorfismo. Poli o quê? Polimorfismo, que é a característica de
uma classe se adaptar, através da sobrescrita (overriding) de
métodos, que é quando uma subclasse possui um método com o
mesmo nome da sua superclasse e por isso, o seu método substitui
o de seu pai.

Sobrescrita
Embora o nome difícil, o pilar é basicamente
isso, um método substitui o outro.
Passo 2: Static
Um ponto importante ainda das classes, é a flag “static” que pode
ser colocada antes de atributos e métodos, para que eles possam
ser diretamente acessados sem precisar instanciar uma classe.
Resumidamente, atente-se ao exemplo:

Veja que esses métodos são usados bem


desacoplados do objeto, não usando nada de
dentro da classe, somente fazendo uma ação
com a palavra que passamos, o que poderia
muito bem ser transformado em uma simples
função, sem que precisemos instanciar a classe
e fazer todo esse rolê.
Mas por que não usar a programação funcional? Se notar, todas
esses métodos referem à saudações, e com essa classe, o contexto
do que elas fazem fica claro, ou seja, temos uma classe dedicada à
saudações. Mas como eliminar o problema da instanciação
desnecessária? Adicionando a flag “static” nos métodos:
Perceba que não estamos mais instanciando a
classe e ao invés disso, estamos acessando
diretamente nossa classe! Esse é o papel do
“static”, tornar métodos/atributos acessíveis
diretamente pela classe, geralmente utilizado em
classes utilitárias.
Um exemplo de classe utilitária é a Math do JavaScript, no p5.js ela
é simplificada sendo chamada como funções (ceil, floor, sqrt), mas
no JavaScript Vanilla elas são métodos estáticos da classe Math,
veja:
Perceba que os métodos da classe
Math estão sendo usados sem que
a classe tenha sido instanciada, isso
por conta deles serem estáticos
(static), claro. E então, aí está uma
das utilidades dessa flag, criar
classes que acumule essas
operações parecidas de
transformação ou leitura de dados e
torná-las diretamente acessíveis
pela classe.
Passo 3: Recursão
Agora entramos na temida Recursão (recursividade sla). Que nada
mais é a característica de uma função chamar a si mesma
continuamente até uma certa condição de parada, que é seu caso
base.
a função
dnv

caso base

função
a função a função parando
dnv
Bom, para exemplificarmos, vejamos esse exemplo:

Note que essa função imprime uma letra do nome


na tela e retorna ela mesma em um determinado
ponto, apenas incrementando o valor do index, ou
seja, ela está chamando ela mesma consecutivas
vezes até que o caso base (quando o index for do
tamanho do nome) seja atendido.

Perceba que esse caso base serve para que a


função não caia em um loop infinito, sendo o
ponto crucial para a criação de funções
recursivas.
Um exemplo muito usado é o do fatorial ( ! ), onde um número é
multiplicado pelo seu antecessor até o 1.
Ex: 5! = 5 * 4 * 3 * 2 * 1 = 120; Vejamos no código:

n=5 n=4 n=2

n = 0 (caso base)

n=3 n=1
Essas funções são muito úteis para simplificar lógicas repetitivas,
mas atenha-se ao caso base, se não seu programa vai dar pau.

ã o rec ursiva
o u ma funç
n d se
Eu faze sem caso ba
O console
Passo 4: Algoritmos de
Ordenação
Então, chegamos ao último assunto: Algoritmos de Ordenação. E
como o nome já é bem descritivo, podemos dizer que eles servem
para organizar dados, geralmente de arrays, em uma ordem, com
base em critérios pré-estabelecidos (ordem alfabética, numérica e
tals).

eu falando que meu pix é


vocês fazendo uma fila ordenada pra me pagar daviddsilva123478@gmail.com
uma coca pelo slide muito bom
E há vários algoritmos de ordenação, mas para a prova, acho que
devemos nos contentar com o…
Passo 4.1: Bubble Sort
O Bubble Sort, ou, para quem não é bilíngue, Ordenação por
Flutuação, é um algoritmo que percorre uma lista e vai comparando
os elementos em pares e vendo se precisa substituí-los ou não, ela
recebe esse nome porque os elementos vão flutuando pela lista,
saca só o exemplo:

Veja que um elemento é comparado com o que vem depois, e se o que vem depois for
menor que ele, eles trocam (pintado de laranja), se não, ficam de boa (pintado de
verde). Note também que esse algoritmo é executado várias vezes (first, second e
third pass), e isso depende do tamanho do array, é por essa causa que ele é um dos
mais custosos em questão de performance também.
O funcionamento básico desse algoritmo está descrito nesses
passos:
● Em uma matriz não classificada de 5
elementos, comece com os dois primeiros
elementos e classifique-os em ordem
crescente. (Compare o elemento para verificar
qual é o maior).
● Compare o segundo e o terceiro elemento para
verificar qual é o maior e classifique-os em
ordem crescente.
● Compare o terceiro e o quarto elemento para
verificar qual é o maior e classifique-os em
ordem crescente.
● Compare o quarto e o quinto elemento para
verificar qual é o maior e classifique-os em
ordem crescente.
● Repita as etapas 1–5 até que não sejam
necessárias mais trocas.
E como aplicamos esse algoritmo na prática? Dessa forma:
Vamos por partes:
Primeiro definimos o array bagunçado:

Então criamos dois loops: o de dentro irá percorrer os elementos de 2 em 2, e o de


fora servirá para repetir essa ação várias vezes até que esteja tudo no lugar:

O de dentro será repetido uma vez a


menos que o de fora por conta de que a
cada repetição dele, ele pega o primeiro (j)
e o segundo (j + 1), assim evitamos erros
no loop.
Vamos por partes:
Com os loops definidos, vamos chamados o par de elementos de a (primeiro
elemento) e de b (segundo), para facilitar. Veja que o segundo é sempre o próximo
elemento da lista:
a b

Então fazemos a verificação responsável por trocar os itens caso o a (primeiro) seja
maior que o b (segundo):

Aqui não tem muito segredo, o primeiro elemento recebe o


valor de b (que tem o valor do segundo) e o segundo recebe
a (que tem o valor de primeiro), eles literalmente invertem.
Vamos por partes:
Por fim, temos nosso array organizado e bonitinho:
Agora, imagine que temos um array de 5 elementos,
consequentemente, a ordenação vai rolar 5 vezes. Porém, se caso
na 3ª volta, tudo esteja perfeitamente ordenado, não é necessário
que as outras 2 voltas sejam feitas, certo? Veja o que acontece:

Veja que ao final da volta 1, o array já está


organizado as outras voltas só estão
consumindo memória desnecessária, mas
para isso, temos uma solução!
Bom, a solução consiste em verificar se alguma troca foi feita na
volta, e se não tiver havido nenhuma troca, saímos do loop, pois isso
implica que o array já está organizado. Veja como:

Perceba que temos a criação de um boolean


antes do loop de verificar os itens, esse
booleano começa falso e caso haja alguma
troca, ele fica true, se não, ele continua false. E
caso ela seja false (não houveram alterações)
ele executa um break no loop de fora, para sair
do loop imediatamente.
Basicamente, esse é um dos algoritmos mais famosos de
ordenação, além dele, foi comentado sobre o Merge Sort, que bebe o
“dividir para conquistar” e executa uma série de fragmentações no
array, mas acredito que ele não vá ser abordado.

Mãe posso dormir com você?

tô com medo do
É isso aí galerinha do mal, acho que esses foram o conteúdos
abordados desde a última prova, tentei exemplificar da maneira
mais mastigada possível.

Qualquer dúvida chamem esse imundo


de
no discord
f a zer sli
ma is
nto
ague
Não

Você também pode gostar