Menu
CoddyTech

Phương thức được bảo vệ

Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 18 trên 57.

Phương thức protected nằm giữa public và private. Code bên ngoài không thể gọi phương thức này, nhưng mọi đối tượng thuộc cùng một lớp đều có thể gọi phương thức đó trên một đối tượng khác. Nhờ vậy, hai đối tượng có thể so sánh dữ liệu mà chúng không hiển thị cho phần còn lại của chương trình:

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def richer_than?(other)
    balance > other.balance
  end

  protected

  def balance
    @balance
  end
end

ada = Account.new("Ada", 500)
bo = Account.new("Bo", 300)
puts ada.richer_than?(bo)
begin
  ada.balance
rescue NoMethodError => e
  puts e.message
end

Đầu ra:

true
protected method `balance' called for an instance of Account

Sự khác biệt so với private thể hiện khi đối tượng nhận là một đối tượng khác. other.balance hoạt động với phương thức được bảo vệ nhưng không hoạt động với phương thức riêng tư:

class Sample
  def initialize(n)
    @n = n
  end

  def compare_protected(other)
    other.guarded + guarded
  end

  def compare_private(other)
    other.hidden + hidden
  rescue NoMethodError
    "private refused other.hidden"
  end

  protected

  def guarded
    @n
  end

  private

  def hidden
    @n
  end
end

a = Sample.new(1)
b = Sample.new(2)
puts a.compare_protected(b)
puts a.compare_private(b)

Đầu ra:

3
private refused other.hidden

protected cũng hoạt động với attr_reader, cách thường dùng để chia sẻ một giá trị chỉ giữa các đối tượng thuộc cùng một lớp, chẳng hạn để quyết định đối tượng nào trong hai đối tượng thắng:

class Player
  attr_reader :name

  def initialize(name, rating)
    @name = name
    @rating = rating
  end

  def stronger?(other)
    rating > other.rating
  end

  protected

  attr_reader :rating
end

a = Player.new("Ada", 1200)
b = Player.new("Bo", 1100)
puts a.stronger?(b)
puts [a, b].select { |pl| pl.stronger?(b) }.map(&:name).inspect

Đầu ra:

true
["Ada"]

Chọn mức độ hạn chế nhất vẫn đáp ứng nhu cầu. public dành cho giao diện của lớp, protected dành cho dữ liệu mà các đối tượng của lớp so sánh với nhau, còn private dành cho mọi thứ khác:

public      # Bất kỳ ai: order.total
protected   # Các đối tượng của lớp: other.balance bên trong Account
private     # Chỉ chính đối tượng đó: subtotal, self.subtotal
challenge icon

Thử thách

Dễ

Hoàn thiện lớp Account. Chỉ các tài khoản khác mới có thể đọc số dư, hãy đặt phương thức đọc của nó ở phạm vi protected. richer_than?(other) so sánh số dư, transfer_to(other, amount) chuyển tiền sang tài khoản khác thông qua phương thức protected receive(amount) của tài khoản đó và trả về true, hoặc trả về false khi số dư quá thấp. to_s chỉ trả về tên chủ sở hữu.

Code được cung cấp đọc tên chủ sở hữu và số dư của hai tài khoản, sau đó xử lý các lệnh transfer a b 50, rồi in ra ok hoặc refused. Tiếp theo, code in ra Ada is richer (hoặc Bo is richer, hoặc equal) rồi thử đọc balance từ bên ngoài, in ra balance is protected khi Ruby từ chối.

Code của bạn nằm trong account.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

require_relative 'account'

# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input = []
while (line = gets)
  input << line.chomp
end
accounts = input[0..1].map do |line|
  owner, balance = line.split
  Account.new(owner, balance.to_i)
end
a, b = accounts
input.drop(2).each do |line|
  _, from, to, amount = line.split
  source = from == "a" ? a : b
  target = to == "a" ? a : b
  puts source.transfer_to(target, amount.to_i) ? "ok" : "refused"
end
if a.richer_than?(b) then puts "#{a} is richer"
elsif b.richer_than?(a) then puts "#{b} is richer"
else puts "equal"
end
begin
  a.balance
  puts "balance is public"
rescue NoMethodError
  puts "balance is protected"
end
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Ruby online