Bảo vệ trạng thái
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 20 trên 57.
Đối tượng nên từ chối những yêu cầu có thể làm hỏng trạng thái của nó. raise dừng phương thức trước khi có bất kỳ thay đổi nào. Tên lớp lỗi do bạn tạo giúp nêu chính xác vấn đề; < StandardError khiến lớp này trở thành một loại lỗi (kế thừa (inheritance), chương 5):
class InsufficientFunds < StandardError
end
class Account
attr_reader :balance
def initialize(balance)
@balance = balance
end
def withdraw(amount)
raise InsufficientFunds, "only #{balance} available" if amount > balance
@balance -= amount
end
end
account = Account.new(50)
begin
account.withdraw(80)
rescue InsufficientFunds => e
puts "#{e.class}: #{e.message}"
end
puts account.balanceĐầu ra:
InsufficientFunds: only 50 available
50Các lớp lỗi riêng biệt cho phép bên gọi xử lý từng vấn đề theo cách khác nhau, với một rescue cho mỗi lớp. Khai báo các lớp lỗi trước những lớp phát sinh lỗi đó:
class InvalidAmount < StandardError
end
class LimitReached < StandardError
end
class Card
def charge(amount)
raise InvalidAmount, "amount must be positive" unless amount.positive?
raise LimitReached, "limit is 100" if amount > 100
"charged #{amount}"
end
end
[40, -5, 500].each do |amount|
puts Card.new.charge(amount)
rescue InvalidAmount => e
puts "fix the input: #{e.message}"
rescue LimitReached => e
puts "try later: #{e.message}"
endĐầu ra:
charged 40
fix the input: amount must be positive
try later: limit is 100freeze chuyển một đối tượng sang chế độ chỉ đọc: sau đó, mọi thay đổi đối với các biến thực thể của đối tượng đều gây ra FrozenError, còn việc đọc vẫn hoạt động. frozen? cho biết một đối tượng có bị đóng băng hay không:
class Point
attr_accessor :x
def initialize(x)
@x = x
end
end
pt = Point.new(1).freeze
puts pt.frozen?
begin
pt.x = 2
rescue FrozenError => e
puts e.class
end
puts pt.xĐầu ra:
true
FrozenError
1Một phương thức đọc trả về mảng nội bộ sẽ đưa ra mảng thật, nên code bên ngoài có thể thay đổi mảng đó. Hãy trả về một bản sao đã đóng băng bằng @items.dup.freeze, hoặc @items.dup, để đối tượng tự kiểm soát dữ liệu của mình:
class Playlist
def initialize
@songs = []
end
def add(song)
@songs << song
end
def songs
@songs.dup.freeze
end
end
list = Playlist.new
list.add("Intro")
begin
list.songs << "Hack"
rescue FrozenError
puts "songs are read-only"
end
p list.songsĐầu ra:
songs are read-only
["Intro"]Thử thách
DễKhai báo các lớp lỗi InvalidAmount và InsufficientFunds, cả hai đều là lớp con của StandardError, rồi hoàn thiện Account. deposit(amount) và withdraw(amount) phát sinh InvalidAmount với thông báo amount must be positive khi số tiền nhỏ hơn 1. withdraw phát sinh InsufficientFunds với thông báo balance is only 20 khi số tiền lớn hơn số dư. Số dư không thay đổi khi có lỗi phát sinh.
Code được cung cấp đọc các lệnh dep n hoặc wd n và in ra balance 70 sau mỗi lệnh, invalid: amount must be positive cho InvalidAmount và refused: balance is only 20 cho InsufficientFunds.
Đặt code của bạn trong invalid_amount.rb, insufficient_funds.rb và account.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.
Tự làm thử
require_relative 'invalid_amount'
require_relative 'insufficient_funds'
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
account = Account.new
input.each do |line|
action, amount = line.split
begin
action == "dep" ? account.deposit(amount.to_i) : account.withdraw(amount.to_i)
puts "balance #{account.balance}"
rescue InvalidAmount => e
puts "invalid: #{e.message}"
rescue InsufficientFunds => e
puts "refused: #{e.message}"
end
end
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
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpBiến instancePhương thức và selfto_s và inspectÔn tập: thẻ thư viện4Tính đóng gói
Phương thức riêng tưPhương thức được bảo vệPhương thức thiết lập riêng tưBảo vệ trạng tháiÔn tập: máy bán hàng tự động7Tính đa hình và duck typing
Tính đa hìnhDuck typingPhương thức trừu tượngcase và lớpÔn tập: phương thức thanh toán2Phương thức truy cập và self
Phương thức đọc và ghiPhương thức gán tùy chỉnhself trong phương thứcĐịnh danh và tính bằng nhauÔn tập: Danh sách liên hệTự luyện tập: Trình biên dịch Ruby online