Mẫu Decorator
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 47 trên 57.
Mẫu trang trí bọc một đối tượng bên trong một đối tượng khác có cùng giao diện, nhờ đó thêm một chức năng trước hoặc sau khi chuyển tiếp lời gọi. Bạn có thể xếp chồng các bộ trang trí theo bất kỳ thứ tự nào:
class Coffee
def cost
2.0
end
def description
"coffee"
end
end
class Milk
def initialize(drink)
@drink = drink
end
def cost
@drink.cost + 0.5
end
def description
"#{@drink.description}, milk"
end
end
order = Milk.new(Milk.new(Coffee.new))
puts format("%s: %.2f", order.description, order.cost)Đầu ra:
coffee, milk, milk: 3.00Mỗi decorator đều lưu đối tượng được bọc và chuyển tiếp lời gọi đến đối tượng đó, nên một lớp cơ sở dùng chung có thể đảm nhận phần này. Sau đó, mỗi decorator cụ thể chỉ ghi đè những gì nó thay đổi:
class Coffee
def cost
2.0
end
def description
"coffee"
end
end
class Extra
def initialize(drink)
@drink = drink
end
def cost
@drink.cost
end
def description
@drink.description
end
end
class Sugar < Extra
def cost
super + 0.2
end
def description
super + ", sugar"
end
end
order = Sugar.new(Coffee.new)
puts format("%s: %.2f", order.description, order.cost)Đầu ra:
coffee, sugar: 2.20Thư viện chuẩn có SimpleDelegator (require 'delegate'), chuyển tiếp mọi phương thức mà nó không định nghĩa đến đối tượng được bọc, giúp decorator ngắn gọn khi giao diện có nhiều phương thức:
require 'delegate'
class Loud < SimpleDelegator
def to_s
super.upcase + "!"
end
end
text = Loud.new("hello")
puts text
puts text.size
puts text.start_with?("he")Đầu ra:
HELLO!
5
trueDecorator thêm hành vi cho từng đối tượng khi chạy và có thể kết hợp tùy ý. Với các lớp con, mỗi tổ hợp như MilkSugarCoffee đều cần có lớp riêng:
Sugar.new(Milk.new(Coffee.new)) # Kết hợp tùy ý, theo thứ tự bất kỳ
Whip.new(Sugar.new(Coffee.new))
# Với các lớp con (subclasses): MilkCoffee, SugarCoffee, MilkSugarCoffee, ...Thử thách
DễHoàn thiện các lớp trang trí cà phê. Coffee có giá 2.00 và được mô tả là coffee. Milk thêm 0.50 và , milk, Sugar thêm 0.20 và , sugar, còn Whip thêm 0.70 và , whip. Cả ba lớp trang trí đều dùng chung lớp cơ sở Extra, lớp này lưu thức uống được bọc và chuyển tiếp cost cùng description; mỗi lớp con đều dùng super.
Mỗi dòng đầu vào là một đơn hàng, chẳng hạn milk sugar. Code được cung cấp sẽ bọc một Coffee mới bằng các lớp trang trí theo thứ tự đó và in ra coffee, milk, sugar: 2.70.
Code của bạn nằm trong các tệp coffee.rb, extra.rb, milk.rb, sugar.rb và whip.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 'coffee'
require_relative 'extra'
require_relative 'milk'
require_relative 'sugar'
require_relative 'whip'
# Đoạn code đầu vào/đầu ra đã có sẵn: giữ nguyên
input = []
while (line = gets)
input << line.chomp
end
extras = { "milk" => Milk, "sugar" => Sugar, "whip" => Whip }
input.each do |line|
drink = line.split.reduce(Coffee.new) { |d, name| extras[name].new(d) }
puts format("%s: %.2f", drink.description, drink.cost)
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