Menu
CoddyTech

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
21

Cá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 đổi

Trong 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.new
challenge icon

Thử 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
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