Por que encapsulamento esconde implementação
O BankAccount que ficou aberto esse tempo todo
O curso Variáveis e atribuição construiu esta classe. Leia de novo com outros olhos:
class BankAccount
def initialize(starting_balance)
@balance = starting_balance
end
def deposit(amount)
@balance += amount
end
def withdraw(amount)
return "insufficient funds" if amount > @balance
@balance -= amount
end
def balance
@balance
end
end
Todo método aqui (initialize, deposit, withdraw, balance) é público, o padrão do Ruby para qualquer método não marcado de outro jeito. Isso estava certo, porque nada no curso anterior precisava de outra coisa. Este curso faz outra pergunta: agora que a classe faz trabalho de verdade, quais métodos dela o código de fora deveria poder chamar?
Uma peça de maquinário a mais
Suponha que um saque agora cobre uma pequena taxa, calculada por um método próprio:
class BankAccount
FEE_RATE = 0.02
def withdraw(amount)
return "insufficient funds" if amount > @balance
fee = service_fee(amount)
@balance -= (amount + fee)
fee
end
def service_fee(amount)
(amount * FEE_RATE).round(2)
end
end
account = BankAccount.new(1000)
account.withdraw(100)
# 2.0, a taxa cobrada, e @balance agora é 898.0
service_fee faz um trabalho correto, e withdraw depende dele. Mas, como método público comum, nada impede que um código qualquer da app chame account.service_fee(100) direto. Esse código passa por cima de withdraw e trata um detalhe de implementação como se fosse uma funcionalidade da classe.
Por que isso é um problema
service_fee é um cálculo de que withdraw precisa, e não uma promessa que BankAccount faz ao resto da app. A fórmula da taxa pode mudar no mês que vem, ganhar outro nome ou dar lugar a uma taxa fixa. Nada disso muda a interface que BankAccount quer oferecer: deposit, withdraw e balance funcionam igual vistos de fora. Se algum código de fora tivesse começado a chamar service_fee, porém, a mudança quebraria esse código, e ele nunca teve motivo para ir tão fundo.
Encapsulamento
Encapsulamento é decidir quais métodos de uma classe formam a interface pública dela e quais são maquinário interno que por acaso foi escrito como método. O Ruby oferece duas palavras-chave para estreitar essa interface de propósito. private vem na próxima aula, e protected, uma ferramenta parecida mas diferente, algumas aulas depois. Este curso dispara as duas, com NoMethodError e tudo, em vez de só descrevê-las.
Para onde o curso vai
service_fee vira o exemplo de private ao longo do curso. balance ganha protected quando aparecer um motivo para o código de fora de BankAccount não lê-lo direto. No fim, BankAccount parece quase igual por fora, com deposit e withdraw funcionando como antes, mas o que tem dentro só diz respeito a ela.
Pontos-chave
- Todo método Ruby é público por padrão; encapsular é escolher estreitar isso.
service_feeé maquinário interno de quewithdrawdepende, e não parte da interface pública pretendida deBankAccount.- Deixar maquinário interno público permite que código de fora dependa dele sem querer, e aí mudar um detalhe quebra quem chama.
privateeprotectedsão as duas ferramentas do Ruby para estreitar a interface de uma classe de propósito, e as próximas aulas rodam as duas.
Continue com a aula 2
private: sem receiver explícito
Marca service_fee como private: a chamada sem receiver funciona e account.service_fee levanta NoMethodError.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.