Você está na página 1de 14

02

Herança

Transcrição

[00:00] Como nós vimos, ter código compartilhado entre as duas classes,
colocando tudo dentro de uma só classe, ajuda a ter código que seria repetido, a
não estar duplicado.

[00:11] Ao mesmo tempo em que quando temos alguma especialização de


comportamento, porque agora que o ByteBank pediu que na conta-corrente, na
hora de sacar tenha uma taxa de 10% em cima do valor que for sacado, vamos ter
esse problema, compartilhar 100% do código traz esse tipo de problema, porque
queremos especializar o comportamento e não conseguimos necessariamente.

[00:30] Ao mesmo tempo em que vimos que ter código 100% duplicado, como
tínhamos na ContaCorrente e na ContaPoupanca , também não é muito legal,
porque vamos ter na ContaCorrente e na ContaPoupanca duplicação de trabalho
quando a regra que for comum para os dois mudar, porque o ByteBank pode
mudar, ele pode ter vários tipos de contas, conta salário, conta empresarial e conta
conjunta, etc.

[00:52] Como vamos lidar com isso? Primeiro que tínhamos aqui na
ContaCorrente , que foi a primeira conta que zemos, aqueles assessores para

proteger nossas propriedades. Vamos trazê-las para a classe Conta , porque eu


quero continuar com as nossas propriedades, o saldo, o cliente e a agência, bem
protegidos.

set cliente(novoValor) {
if (novoValor instanceof Cliente) {
this._cliente = novoValor;
}
}

get cliente() {
return this._cliente;
}

get saldo() {
return this._saldo;
}
COPIAR CÓDIGO

[01:11] Lembrando, como vimos na documentação, precisamos sempre declarar as


propriedades da classe dentro de um método da classe e o mais comum é fazer
isso dentro do construtor. E outra boa prática é sempre inicializar essas variáveis
com algum valor.

[01:27] Na classe Conta estamos usando como parâmetro o que veio do


construtor, mas poderíamos inicializar com o valor colocado aqui, Hardcoded ou
uma string, um número, alguma coisa direto na nossa propriedade.

[01:40] Agora, na classe Conta temos nossos assessores, temos o sacar() , o


depositar() e o transferir() , e eu quero compartilhar código entre a classe

ContaCorrente e a classe ContaPoupanca , mas não quero fazer isso de qualquer

forma, porque eu quero que minha classe ContaCorrente possa sacar de uma
maneira diferente.

[01:58] Como faremos isso? Como podemos usar? Uma das maneiras é pedir para a
nossa classe Conta de nir qual é o tipo dela. Vou inserir no construtor uma
variável chamada tipo , a minha conta vai ter um tipo, agora eu vou ter uma nova
propriedade, this._tipo e aqui eu estou colocando-a como privada de propósito,
lembrando que sempre começo as propriedades como privadas.
[02:20] Se eu precisar abri-la para alguém de fora da minha classe usar, eu faço
isso depois, através de um assessor, ou se eu realmente precisar, eu deixo-a
pública, mas vou deixá-la privada por enquanto.

[02:30] E baseado nesse tipo , eu vou vir aqui e vou sacar. Vamos colocar no
sacar() que essa taxa inicial vai ser sempre de 1 , o meu valor sacado vai ser

igual a taxa multiplicada pelo valor. E se o this._tipo for igual a conta-corrente,


a minha taxa vai ser igual a 1.1 , eu só quero alterar minha taxa se estiver
dentro desse tipo de conta.

[03:12] E o valorSacado , eu vou usá-lo aqui, já que eu multipliquei a minha taxa,


vai ser o valor que eu vou ter que fazer as veri cações de saldo e retornar, se eu
tiver saldo su ciente para fazer esse saque.

sacar(valor) {

let = taxa = 1
if(this._tipo == "corrente"){
taxa = 1.1;
}
const valorSacado = taxa * valor;
if (this._saldo >= valorSacado) {
(this._saldo -= valorSacado;
return valorSacado;
}
}
COPIAR CÓDIGO

[03:22] Como eu vou fazer isso agora? Se eu tenho a minha conta-corrente, meu
ByteBank pode vir e falar que a conta-corrente vai ter uma taxa de 1.1, mas a conta
salário vai ter uma taxa de 0.5, ter uma taxa de 5% quando a conta for salário. E
uma taxa também para a conta de empresa, então a conta empresarial vai ter uma
taxa de 15%, já que é uma conta de CNPJ, eles têm mais dinheiro, então deve ter
uma taxa maior para eles.
[03:55] Isso é para sacar, mas e se o ByteBank disser que também vai ter algumas
regras diferentes para depositar ou para transferir. Falar que conta salário não
pode transferir. Então se ela for conta salário, eu vou vir no transferir() e
inserir um return , não faz mais nada, você não pode transferir. Mas eu também
posso ter aqui no depositar.

sacar(valor) {

let = taxa = 1
if(this._tipo == "corrente"){
taxa = 1.1;
}
if(this._tipo == "salario"){
taxa = 1.05;
}
if(this._tipo == "empresarial"){
taxa = 1.15;
}
const valorSacado = taxa * valor;
if (this._saldo >= valorSacado) {
this._saldo -= valorSacado;
return valorSacado;
}
}
COPIAR CÓDIGO

[04:16] Espera, olha quanta regra que estamos colocando dentro desse código,
quantos if's vamos ter que criar só porque o ByteBank quer mudar algum
comportamento especí co dessas contas. Não faz muito sentido.

[04:27] Ter essa variável do tipo na nossa conta não é tão legal, primeiro porque
eu vou ter que criar um monte de if dentro do meu código, para cada uma das
variações, para cada um dos tipos de conta que forem criadas. Eu tive que fazer
tudo isso para mudar uma única regra.
[04:43] Agora eu vou ter que vir no index.js e colocar que a conta-corrente é
"corrente", então essa conta-corrente vai cair naquela regra do sacar, a minha
conta poupança também preciso colocar que é "poupança".

const contaCorrenteRicardo = new Conta("corrente", 0, client


contaCorrenteRicardo.depositar(500);
contaCorrenteRicardo.sacar(100);

const contaPoupanca = new Conta("poupanca", 50, cliente1, 10

COPIAR CÓDIGO

[04:56] Mas se eu colocar o “corrente” errado, colocar dois “o”, ele não vai entrar
no if , então aquela regra que eu coloquei na conta não vai entrar, olha como ca
frágil essa código.

[05:07] E uma das coisas que nós continuamos não tendo ainda, se abrirmos nosso
terminal, antes vamos solucionar esse erro que o terminal indicou ao chamarmos
o node ./index.js , na linha 25 do arquivo Conta , let = taxa não faz muito
sentido mesmo, então vamos fechar nosso terminal. Não deve ter o sinal de igual
entre let e taxa .

[05:28] Vamos abrir o terminal, tentar executar de novo, limpar o terminal, agora
sim. Trouxemos aqui minha Conta , então eu tenho dois objetos do tipo Conta,
mas eu tenho aqui o _tipo: 'poupanca' e eu tenho o _tipo: 'corrente' .
Então parte do que perdemos de vocabulário na hora de conversar nós ganhamos
de volta, porque eu tenho aqui qual é o tipo da conta.

Conta {
_saldo: 50,
_cliente: Cliente { nome: 'Ricardo', _cpf:11122233309 },
_agencia: 1001
_tipo: 'poupanca'
}
Conta {
_saldo: 400,
_cliente: Cliente { nome: 'Ricardo', _cpf:11122233309 },
agencia: 1001
_tipo: 'corrente'
}
COPIAR CÓDIGO

[05:46] Mas o meu código ainda não ca maravilhoso, porque eu tenho muitas
condicionais dentro do mesmo código, muitos caminhos possíveis dentro desse
método sacar() , muitos caminhos possíveis dento do depositar() , e eu vou ter
esse if todas as vezes que eu tiver alguma regra especial para algum
comportamento especial de uma conta que o ByteBank quiser. Então não é a
melhor maneira de fazermos isso.

[06:07] Como podemos compartilhar código entre as contas e ganhar de volta


aquele vocabulário de chamar para os outros desenvolvedores, conversar com o
meu cliente na linguagem do negócio, falar: "eu tenho aqui um objeto do tipo
ContaCorrente , ele fez essas operações, ele tem essas propriedades especí cas

dele".

[06:27] Eu vou voltar aqui no sacar() da maneira que ele estava antes dos if's
para "corrente", "salario" e "empresarial":

sacar(valor) {

let = taxa = 1
const valorSacado = taxa * valor;
if (this._saldo >= valorSacado) {
this._saldo -= valorSacado;
return valorSacado;
}
}
COPIAR CÓDIGO
O depositar() e o transferir() , vamos só arrumá-los um pouco, vamos
identar nosso código corretamente, vou deixar tudo aqui, já trouxemos os nossos
assessores para Conta e já vimos que essa variável que chama tipo , que de nia
qual é o tipo de conta não é muito legal de ter, então eu já vou tirá-la, porque eu
não quero ter aquele monte de if no meu código.

[06:52] Mas o que eu quero fazer é: eu quero falar, minha conta-corrente é igual a
nova ContaCorrente , e ela tem que seguir esse construtor, ela tem que seguir que
ela recebe um cliente e ela recebe uma agência.

[07:06] E minha contaPoupanca é uma ContaPoupanca , eu não preciso declarar


isso como uma string, eu tenho que declarar isso como um tipo, porque o tipo do
JavaScript, essa classe que eu criei, é muito melhor para de nir isso do que uma
string que eu posso errar na hora de escrevê-la, então eu quero declarar meus
objetos dessa maneira:

const cliente1 = new Cliente("Ricardo", 11122233309);

const contaCorrenteRicardo = new ContaCorrente(cliente1, 100


contaCorrenteRicardo.depositar(500);
contaCorrenteRicardo.sacar(100);

const contaPoupanca = new ContaPoupanca(50, cliente1, 1001);

COPIAR CÓDIGO

[07:25] Só que eu quero compartilhar código entre eles também, e para isso o que
podemos falar é: tudo o que você tiver da ContaCorrente você vai estender da
classe Conta , porque a classe Conta já tem uma série de coisas, então você pega
tudo o que estiver na classe Conta , traz para você e usa como se fosse seu.

[07:43] Como nós fazemos isso? Na ContaCorrente , vamos inserir o extends


para Conta de nindo que deve estender tudo o que estiver na classe Conta. Eu
estou fazendo uma mudança na declaração da minha classe, que eu estou
declarando uma classe ContaCorrente e falando que ela estende a classe Conta .

export class ContaCorrente extends Conta{


static numeroDeContas = 0;

//código omitido
}
COPIAR CÓDIGO

[07:58] Por "trás dos panos" o JavaScript vai fazer o quê? Vai pegar tudo o que
estiver na classe Conta e vai trabalhar na classe ContaCorrente como se o
código estivesse escrito aqui. Dessa maneira, todo esse código que está no
ContaCorrente.js para sacar, transferir e depositar, não precisa estar aqui, eu

posso ter só o meu construtor para declarar as minhas propriedades.

[08:15] Mas espera, as propriedades que eu tenho no meu construtor da


ContaCorrente são agência, cliente e saldo, e as propriedades que eu tenho na

minha Conta são agência, cliente e saldo, então eu não preciso dessas
propriedades também na ContaCorrente , eu posso tirar.

export class ContaCorrente extends Conta{


static numeroDeContas = 0;

// #saldo =0 https://github.com/tc39/proposal-cl

constructor(agencia, cliente) {

ContaCorrente.numeroDeContas += 1;
}
}
COPIAR CÓDIGO
[08:27] E dessa maneira eu vou ter no JavaScript todo o comportamento que eu
tinha antes na ContaCorrente , mas eu estou herdando de Conta , então eu estou
pegando da minha classe Conta e estou trazendo para a minha ContaCorrente ,
isso que nós chamamos de herança.

[08:42] Eu estou fazendo uma herança da classe Conta , então essa classe
ContaCorrente vai pegar tudo o que tem na classe Conta , vai trazer para ela e

usar como se fosse dela.

[08:51] E umas das coisas que temos de vantagem disso, inclusive, que não
tínhamos nem percebido, é que a classe ContaCorrente tinha um
comportamento muito especí co dela, que é, eu conto o número de contas-
correntes que foram criadas.

[09:02] Ele tem uma propriedade estática que conta quantas vezes foi criado, da
maneira que eu tinha antes, código compartilhado com Conta , eu não tinha mais
esse comportamento, eu teria que fazer um monte de if também para ter uma
propriedade estática que zesse essa contagem para mim.

[09:17] Fazendo uma herança da Conta eu consigo ter esse comportamento


especializado de volta, ao mesmo tempo em que eu estou reutilizando o código já
existente em outra classe.

[09:25] E posso fazer a mesma coisa para a minha poupança. Na minha


ContaPoupanca , class ContaPoupanca estende de Conta , então extends

Conta . E no import é o do "./Conta.js" , não podemos esquecer, a importação

sempre tem que ter a extensão do arquivo no nal.

import { Conta } from "./Conta.js";

export class ContaPoupanca extends Conta{

//código omitido
}
COPIAR CÓDIGO

[09:42] E a minha ContaPoupanca estende de Conta , então eu não preciso do


saldo, do cliente e da agência, eu não preciso do sacar() , depositar() e
transferir() declarados na minha ContaPoupanca , porque a minha

ContaPoupanca é uma Conta , então ela já vai trazer também tudo o que tiver em

Conta para ela.

import { Conta } from "./Conta.js";

export class ContaPoupanca extends Conta{


constructor(saldoInicial, cliente, agencia) {
}

}
COPIAR CÓDIGO

[10:01] Vamos ver se isso está funcionando? Vamos ver se eu consegui trazer
realmente esses comportamentos? Vamos abrir nosso terminal do VS Code, vamos
limpá-lo e executar o node ./index.js .

[10:12] Estamos executando e está dando um erro em ContaCorrente , no trecho


ContaCorrente.numeroDeContas += 1 , na hora que ele veio passar nessa linha

ele deu um erro, e o nosso erro foi um erro de referência. A mensagem dessa vez é
um pouco diferente, você precisa chamar o super construtor na hora de derivar
uma classe, antes de acessar o this .

ReferenceError: Must call super constructor in derived class before accessing


'this' or returning from derived constructor
[10:32] Mas espera, eu não estou acessando o this , porque ele está falando que
eu tenho que chamar o super construtor? Aliás, o que é esse super construtor?
Antes de eu chamar o this , se eu nem estou chamando o this ?

[10:42] Então o teste é, sempre que fazemos essa herança, como zemos na
ContaCorrente , para o JavaScript ele precisa criar o objeto Conta para falar:

"você herda todas essas propriedades na hora em que você for construido aqui no
seu construtor". E para isso precisamos chamar o construtor da classe Conta .

[11:00] Só que normalmente nós chamamos o construtor com o new, então eu vou
ter que chamar o new Conta() aqui no construtor da ContaCorrente . Só que
chamar o new Conta não faz muito sentido quando eu estou dentro de uma
classe, então o que usaremos é uma palavra especial que se chama super .

[11:13] O super é uma palavra especial que vai chamar o construtor da classe pai.
Não é sempre que ele vai chamar o construtor, na verdade, ele depende do método
em que ele está dentro.

[11:24] Se ele estiver em outro método, por exemplo, se eu tivesse o sacar() aqui
na ContaCorrente e eu chamasse o super dentro do método sacar() , esse
super estaria chamando o método sacar() da classe Conta e não da classe

ContaCorrente . Então ele é uma palavra especial aqui e, no caso, ele está

chamando o construtor, porque eu estou chamando o super dentro do


construtor.

import { Conta } from "./Conta.js";

export class ContaCorrente extends Conta{


static numeroDeContas = 0;
constructor(agencia, cliente) {
super();
ContaCorrente.numeroDeContas += 1;
}
}
COPIAR CÓDIGO
[11:40] Esse super() , inclusive, se eu passar o mouse em cima dele, ele está
falando que esse super está referenciando o construtor da classe Conta , e eu
preciso de um saldo inicial de um cliente e de uma agência.

[11:49] Como sabemos, o nosso saldo inicial é sempre 0, o nosso cliente é o cliente
que estamos recebendo pelo nosso construtor e a nossa agência também está
sendo chamada aqui pelo construtor.

[12:01] Inclusive, como estamos derivando da classe Conta , eu vou alterar a


ordem do meu construtor para deixar sempre na mesma ordem. A minha conta
recebe cliente e agência nessa ordem, então a minha ContaCorrente vai receber
cliente e agência nessa ordem.

export class ContaCorrente extends Conta{


static numeroDeContas = 0;
constructor(cliente, agencia) {
super(0, cliente, agencia);
ContaCorrente.numeroDeContas += 1;
}
COPIAR CÓDIGO

[12:15] Fica um pouco mais fácil de trabalharmos mais para frente, quando
tivermos essa uniformidade entre todos os tipos de conta, todos recebem os
parâmetros mais ou menos na mesma ordem.

[12:27] O ContaCorrente recebe (cliente, agencia) , e minha ContaPoupanca


também tem que chamar o super , então eu também tenho que chamar o
construtor da classe Conta , passando saldoInicial, , que é o saldo inicial que a
própria ContaPoupanca vai receber, passando cliente, agencia . Dessa forma
já temos o super das duas classes, ContaPoupanca e ContaCorrente .
import { Conta } from "./Conta.js";

export class ContaPoupanca extends Conta{


constructor(saldoInicial, cliente, agencia) {
super(saldoInicial, cliente, agencia);
}

}
COPIAR CÓDIGO

[12:49] Vamos limpar nosso terminal, que tinha dado aquele erro, e vamos
executar o nosso código. E dessa vez agora ele funcionou. Temos de volta a nossa
ContaPoupanca , temos a nossa ContaCorrente e conseguimos fazer a herança

das nossas propriedades da classe Conta .

ContaPoupanca {
_saldo: 50,
_cliente: Cliente { nome: 'Ricardo', _cpf:11122233309 },
_agencia: 1001
}
ContaCorrente {
_saldo: 400,
_cliente: Cliente { nome: 'Ricardo', _cpf:11122233309 },
agencia: 1001
}
COPIAR CÓDIGO

[13:05] Você pode ver que minha ContaPoupanca tem um saldo, tem um cliente e
tem uma agência, e a nossa ContaCorrente também tem um saldo, um cliente e
uma agência.

[13:14] Inclusive, ela conseguiu manipular daqui com os comportamentos da nossa


conta, com o nosso sacar e com o nosso depositar, já que na index.js estávamos
chamando aqui da ContaCorrente o nosso sacar e depositar, e como nossa
ContaCorrente não declarou nada disso, ela está usando os comportamentos da

classe mãe dela, da nossa classe Conta .

[13:36] Conseguimos fazer uma herança, compartilhar código de uma maneira um


pouco mais legal, em que não perdemos o vocabulário, continuamos tendo uma
linguagem de negócio mais próxima da linguagem de negócio, conta-corrente e
conta poupança, que são os termos que o ByteBank vai usar, ao mesmo tempo em
que estamos compartilhando código entre essas duas classes, fazendo a herança
delas.

Você também pode gostar