Mẫu Strategy
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 45 trên 57.
Mẫu strategy đặt một phần của thuật toán vào đối tượng riêng và cho phép đối tượng chính ủy quyền việc đó. Thay đổi strategy sẽ thay đổi hành vi mà không cần thay đổi lớp chính:
class Standard
def cost(kg)
5 + kg * 1.5
end
end
class Express
def cost(kg)
15 + kg * 3
end
end
class Shipment
attr_writer :strategy
def initialize(kg, strategy)
@kg = kg
@strategy = strategy
end
def cost
@strategy.cost(@kg)
end
end
s = Shipment.new(2, Standard.new)
puts s.cost
s.strategy = Express.new
puts s.costĐầu ra:
8.0
21Các chiến lược chỉ dùng chung tên phương thức, vì vậy mọi đối tượng có cost đều hoạt động: lại là duck typing. Thêm một cách giao hàng mới nghĩa là thêm một lớp, chứ không phải thêm một nhánh khác bên trong Shipment:
class Pickup
def cost(_kg)
0
end
end
shipment.strategy = Pickup.new # Shipment không thay đổiTrong Ruby, strategy thường là một lambda. Bất cứ thứ gì phản hồi với call đều có thể được lưu trữ và gọi, dù đó là lambda, một đối tượng phương thức hay một đối tượng thuộc lớp do bạn tự tạo:
class TextFormatter
def initialize(strategy)
@strategy = strategy
end
def format_all(words)
words.map { |w| @strategy.call(w) }.join(" ")
end
end
class Censor
def call(word)
word.size > 3 ? "*" * word.size : word
end
end
words = %w[ruby is great]
puts TextFormatter.new(->(w) { w.upcase }).format_all(words)
puts TextFormatter.new(:capitalize.to_proc).format_all(words)
puts TextFormatter.new(Censor.new).format_all(words)Đầu ra:
RUBY IS GREAT
Ruby Is Great
**** is *****Hãy chọn strategy thay vì lớp con khi một đối tượng cần thay đổi hành vi trong lúc chương trình chạy hoặc khi sự khác biệt chỉ nằm ở một bước. Lớp con cố định hành vi trong suốt vòng đời của đối tượng:
# Tạo lớp con: cố định ngay khi tạo
ExpressShipment.new(2)
# Chiến lược: có thể thay đổi sau
shipment = Shipment.new(2, Standard.new)
shipment.strategy = Express.newThử thách
DễHoàn thiện các chiến lược vận chuyển và ShippingCalculator. Standard#cost(kg) là 5 cộng thêm 1.50 cho mỗi kg, Express#cost(kg) là 15 cộng thêm 3 cho mỗi kg, còn Pickup#cost(kg) là 0. Bộ tính phí được khởi tạo với một chiến lược, có thể đổi chiến lược bằng strategy=, và quote(kg) trả về 2.5 kg by Express: 22.50, sử dụng tên lớp của chiến lược.
Code được cung cấp bắt đầu với Standard và đọc các lệnh use express, use pickup, use standard hoặc quote 2.5, rồi in ra từng báo giá.
Code của bạn nằm trong standard.rb, express.rb, pickup.rb và shipping_calculator.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 'standard'
require_relative 'express'
require_relative 'pickup'
require_relative 'shipping_calculator'
# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input = []
while (line = gets)
input << line.chomp
end
strategies = { "standard" => Standard, "express" => Express, "pickup" => Pickup }
calc = ShippingCalculator.new(Standard.new)
input.each do |line|
cmd, arg = line.split
if cmd == "use"
calc.strategy = strategies[arg].new
else
puts calc.quote(arg.to_f)
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