Skip to content

Latest commit

 

History

History
322 lines (238 loc) · 11.7 KB

File metadata and controls

322 lines (238 loc) · 11.7 KB

logo reprograma

Resumo

Encapsulamento

Recapitulando... "Quando definimos os atributos de um objeto, devemos garantir que alterar os valores desses atributos sejam responsabilidade exclusiva do próprio objeto. O encapsulamento, portanto, é o conceito de proteger os atributos de um objeto."

Conceito

A definição de encapsulamento é "a ação de colocar algo dentro ou como se estivesse em uma cápsula". Remover o acesso a partes do seu código e tornar as coisas privadas é exatamente o que o encapsulamento faz.

O encapsulamento se trata de um dos elementos que adicionam segurança à aplicação em uma programação orientada a objetos pelo fato de esconder as propriedades, criando uma espécie de caixa preta.

A maior parte das linguagens orientadas a objetos implementam o encapsulamento baseado em propriedades privadas, ligadas a métodos especiais chamados getters e setters, que irão retornar e setar o valor da propriedade, respectivamente. Essa atitude evita o acesso direto a propriedade do objeto, adicionando uma outra camada de segurança à aplicação.

Exemplos do mundo real

Para fazermos um paralelo com o que vemos no mundo real, temos o encapsulamento em outros elementos. Por exemplo, quando clicamos no botão ligar da televisão, não sabemos o que está acontecendo internamente. Podemos então dizer que os métodos que ligam a televisão estão encapsulados.

Agora, voltando na nossa analogia do carro, sabemos que ele possui atributos e métodos, ou seja, características e comportamentos. Os métodos do carro, como acelerar, podem usar atributos e outros métodos do carro como o tanque de gasolina e o mecanismo de injeção de combustível, uma vez que acelerar gasta combustível.

No entanto, se alguns desses atributos ou métodos forem facilmente visíveis e modificáveis, como o mecanismo de aceleração do carro, isso pode dar liberdade para que alterações sejam feitas, resultando em efeitos colaterais imprevisíveis. Nessa analogia, uma pessoa pode não estar satisfeita com a aceleração do carro e modifica a forma como ela ocorre, criando efeitos colaterais que podem fazer o carro nem andar, por exemplo.

Dizemos, nesse caso, que o método de aceleração do seu carro não é visível por fora do próprio carro. Na POO, um atributo ou método que não é visível de fora do próprio objeto é chamado de "privado" e quando é visível, é chamado de "público".

Mas então, como sabemos como o nosso carro acelera? É simples: não sabemos. Nós só sabemos que para acelerar, devemos pisar no acelerador e de resto o objeto sabe como executar essa ação sem expor como o faz. Dizemos que a aceleração do carro está encapsulada, pois sabemos o que ele vai fazer ao executarmos esse método, mas não sabemos como - e na verdade, não importa para o programa como o objeto o faz, só que ele o faça.

O mesmo vale para atributos. Por exemplo: não sabemos como o carro sabe qual velocidade mostrar no velocímetro ou como ele calcula sua velocidade, mas não precisamos saber como isso é feito. Só precisamos saber que ele vai nos dar a velocidade certa. Ler ou alterar um atributo encapsulado pode ser feito a partir de getters e setters (colocar referência).

Esse encapsulamento de atributos e métodos impede o chamado vazamento de escopo, onde um atributo ou método é visível por alguém que não deveria vê-lo, como outro objeto ou classe. Isso evita a confusão do uso de variáveis globais no programa, deixando mais fácil de identificar em qual estado cada variável vai estar a cada momento do programa, já que a restrição de acesso nos permite identificar quem consegue modificá-la.

Recapitulando

Vocês já aprenderam os conceitos a seguir em aulas anteriores, mas vamos recapitulá-las para ter certeza de que entendemos bem para poder utilizá-las daqui em diante.

Modificadores de acesso em Javascript

Modificadores de acesso são uma feature muito nova no JavaScript (lançada no ES2022), mas que já está disponível para cerca de 93% dos usuários totais, segundo o Can I Use.

Uma propriedade ou método pode ser:

  • Público: o padrão do JS, acessível de qualquer lugar.
  • Privado: acessível apenas de dentro da classe.

Para tornar uma propriedade privada, usamos # antes do nome. Antes dessa feature ser implementada, era comum usar _ na frente do nome de uma propriedade para indicar que ela era privada.

Exemplo:

class Gato {
	nome;
	idade;
	cor;
	castrado;
	#segredo;

	constructor(nome, idade, cor, castrado, segredo) {
		this.nome = nome;
		this.idade = idade;
		this.cor = cor;
		this.castrado = castrado;
		this.#segredo = segredo;
		this.cativado = false;
	}

	miar() {
		console.log('Miau :3');
	}

	brincar() {
		this.cativado = true;
		console.log(`${this.nome} agora confia em você!`);
	}

	fofocar() {
		if (this.cativado) {
			let fofoca = `...fui eu quem ${this.#segredo}`;

			console.log('Preciso confessar uma coisa...');
			console.log(fofoca);
		} else {
			console.log('Hmmm... suspeito');
		}
	}
}

Get e Set

As classes JavaScript contam com dois métodos especiais:

  • um com o prefixo get, que tem a função de retornar um valor de um parâmetro.
  • outro com prefixo set que serve para atribuir um valor a um parâmetro.

Nós chamamos eles de Getters e Setters, pois eles tem a função de fazer um get (pegar) ou um set (atribuir). Ambos funcionam como se fossem uma propriedade da classe.

Esses métodos são ideais para serem utilizados, quando temos parâmetros privados.

Getter

O getter, com a sintaxe get é associado a uma função que será chamada quando a propriedade em questão for acessada e solicitada de forma dinâmica. É possível utilizá-la para retornar o status de uma variável interna, sem utilizar métodos de forma explícita. Da seguinte forma:

class Curso {
	materia;
	professor;
	duracao;

	constructor(materia, professor, duracao) {
		this.materia = materia;
		this.professor = professor;
		this.duracao = duracao;
	}
	get prof() {
		return this.professor;
	}
}

let poo = new Curso(
	'Programação orientada a objetos',
	'Rafaella',
	'1 semestre'
);
console.log(poo.prof); //Rafaella

Nesse exemplo apenas utilizamos o getter para retornar um valor que já havia sido declarado de forma fixa. Perceba que podemos utilizar o método get tanto com atributos públicos quanto provados. Além disso, o nome da função get não precisa ser o mesmo nome do atributo que ela vai retornar.

Observe agora o seguinte exemplo:

class Boletim {
  constructor(participacao, prova, trabalho) {
    this.participacao = participacao;
    this.prova = prova;
    this.trabalho = trabalho;
  }
  get media() {
    return parseInt((this.participacao + this.prova + this.trabalho) / 3);
  }
}

let boletimSemestral = new Boletim(8, 6, 7.5);
console.log(boletimSemestral.media); //7

Perceba agora que o método get está sendo utilizado para buscar um valor que sequer é um parâmetro.

Por fim, observe o seguinte exemplo:

class Conta {
  ehAdm;
  #senha = 123;

  constructor(ehAdm) {
    this.ehAdm = ehAdm;
  }

  get senha() {
    if(this.ehAdm) {
      return this.#senha;
    }
  }
}

const conta1 = new Conta(true)
console.log(conta1.senha) //123

const conta2 = new Conta(false)
console.log(conta2.senha) //undefined

Alguns pontos importantes a serem destacados para o uso de getters são:

  • Pode ter um identificador do tipo numérico ou string (aconselho a usar string).
  • Não deve ter nenhum parâmetro.
  • Não pode ser utilizado mais de um getter para uma mesma propriedade, assim como não pode haver uma propriedade comum com o mesmo nome do getter.

Setter

Geralmente usados junto com os getters, os setters são utilizados para definirem valores para uma propriedade específica.

Observe o exemplo:

class Aluno {
  constructor(nome, curso, semestre){
    this.nome = nome,
    this.curso = curso,
    this.semestre = semestre
  }
  set nomeAluno(nomeAluno) {
    this.nome = nomeAluno
  }
}

let lucas = new Aluno('', 'Engenharia', 5)
lucas.nomeAluno = 'Lucas'
console.log(lucas.nome) //Lucas

Perceba que a propriedade nome foi modificada através de uma função, e não diretamente.

Agora, utilizando esse conceito com propriedades privadas:

class Conta {
  ehAdm;
  #saldo = 0;

  constructor(ehAdm) {
    this.ehAdm = ehAdm;
  }

  set saldo(novoSaldo) {
    if(this.ehAdm) {
      this.#saldo = novoSaldo;
    }
  }

  get saldo() {
    return this.#saldo;
  }
}

const conta1 = new Conta(true)
console.log(conta1.saldo) // 0
conta1.saldo = 100;
console.log(conta1.saldo) // 100

const conta2 = new Conta(false)
console.log(conta2.saldo) // 0
conta2.saldo = 100;
console.log(conta2.saldo) // 0

Alguns pontos importantes a serem destacados para o uso de setters são:

  • Pode ter um identificador do tipo numérico ou string.
  • Deve ter exatamente um parâmetro.
  • Não pode ter a mesma nomenclatura para propriedade e função (então por que no exemplo conseguimos utilizar a palavra saldo?).

Agora que reforçamos os conceitos de get e set, podemos ver outros exemplos de como utilizá-los juntos:

class User extends Person {
  name;
  age;
	#password;
	constructor(name, age, email, password) {
		this.name = name;
		this.age = age;
		this.email = email;
		this.#password = password;
	}

	speak() {
		console.log(`A pessoa de nome ${this.name} está falando`);
	}

	#encryptPassword() {
		return (this.#password = `*** ${this.#password} + ***`);
	}

	get password() {
		//Aqui dentro, podemos ter verificações antes de retornar a senha para quem está pedindo, para tornar a nossa aplicação mais segura
		return this.#password
	}

	set password(newPassword) {
		//Aqui dentro, podemos ter verificações antes de trocar a senha para quem está solicitando a troca, para tornar a nossa aplicação mais segura
		this.#password = newPassword
	}
}

→ Vamos aplicar?

→ Exercício para casa

Exercício para casa

Lembrete para a prô: perguntar se as alunas já aprenderam sobre exportar e importar com o module.exports e require. Caso não tenham aprendido, explicar e ensinar como se faz.

Links Úteis


Voltar para o início

Desenvolvido com 💜