Public and Private Interfaces in ruby

来源:互联网 发布:软件工程推荐书籍知乎 编辑:程序博客网 时间:2024/05/16 23:40

Your latest client is a bank, and they’ve tasked you with requiring customers to enter their password in order to make withdrawals.

Currently, this is what they’ve got:

class Customer  attr_reader :funds  def initialize(funds, password)    @funds = funds    @password = password  end  def remove_funds(amount)    @funds -= amount  endend

Let’s break that apart. You can paste that whole class into irb to follow along.

When a customer is initialized, it receives a specified amount of funds and a password is set.

diego = Customer.new(500, "udacious")# => #<Customer:0x007fcdb48ca5a8 @funds=500 @password="udacious">

Thanks to the attr_reader, you can see the value of his current funds.

diego.funds# => 500

And the remove_funds method allows funds to be removed from the customer’s account.

Checking on the funds again confirms this.

diego.remove_funds(50)# => 450diego.funds# => 450

These methods, funds and remove_funds, are part of the Customer class’ API, or application programming interface.

An API is, according to Wikipedia, “a set of routines, protocols, and tools for building software applications”.

Well, that’s vague.

“API” is a popular term in recent years, but many people use it without quite understanding what it means. Think of methods like remove_funds as your way of interfacing with the Customer class. These methods are the keys to accessing information about a particular customer.

There isn’t currently a way to access the @password instance variable.

It could be said that the customer’s password can’t be accessed by the customer’s public API.

In this situation, that’s a good thing! You don’t want information like a password to be publicly available to other objects.

Let’s implement a method called withdraw_securely, which takes two arguments, amount andpassword.

If the password entered matches the customer’s password, go ahead and remove the funds. Otherwise, nothing happens.

class Customer  attr_reader :funds  def initialize(funds, password)    @password = password    @funds = funds  end  def remove_funds(amount)    @funds -= amount  end  def withdraw_securely(amount, password)    if password == @password      remove_funds(amount)    end  endend

Play around with this in irb to see it in action.

diego.withdraw_securely(50, "udacious")# => 400diego.withdraw_securely(100, "wrong password")# => nildiego.funds# => 400

✨Hooray. Calling withdraw_securely using the correct password decreases the total funds by calling remove_funds,

while using the incorrect password does nothing.

There’s one issue here, can you spot it?

diego.remove_funds(75)# => 325diego.funds# => 325

Malicious users can still withdraw funds directly using the remove_funds method!

0 0
原创粉丝点击