Por que encapsulamento esconde implementação

Prévia grátis 3 min de leitura

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 que withdraw depende, e não parte da interface pública pretendida de BankAccount.
  • Deixar maquinário interno público permite que código de fora dependa dele sem querer, e aí mudar um detalhe quebra quem chama.
  • private e protected são as duas ferramentas do Ruby para estreitar a interface de uma classe de propósito, e as próximas aulas rodam as duas.
Aula 1 de 7 · grátis

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.

Digite para buscar...