Mẫu Builder
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 48 trên 57.
Mẫu builder tạo một đối tượng phức tạp theo từng bước. Mỗi bước là một phương thức lưu một lựa chọn rồi trả về builder, nhờ vậy các bước có thể nối tiếp nhau; build kiểm tra các lựa chọn và trả về đối tượng hoàn chỉnh:
class Pizza
def initialize(size, toppings)
@size = size
@toppings = toppings
end
def to_s
"#{@size} pizza with #{@toppings.join(", ")}"
end
end
class PizzaBuilder
def initialize
@toppings = []
end
def size(value)
@size = value
self
end
def topping(name)
@toppings << name
self
end
def build
raise ArgumentError, "size is required" if @size.nil?
Pizza.new(@size, @toppings)
end
end
puts PizzaBuilder.new.size("large").topping("cheese").topping("ham").buildĐầu ra:
large pizza with cheese, hamVì builder chỉ tạo đối tượng trong build, nó có thể kiểm tra toàn bộ tổ hợp cùng lúc, chẳng hạn như một phần bắt buộc hoặc hai tùy chọn không thể kết hợp với nhau:
class TicketBuilder
def seat(value)
@seat = value
self
end
def standing
@standing = true
self
end
def build
raise ArgumentError, "seat and standing exclude each other" if @seat && @standing
@standing ? "standing ticket" : "ticket for seat #{@seat || "any"}"
end
end
puts TicketBuilder.new.seat("B7").build
puts TicketBuilder.new.standing.build
begin
TicketBuilder.new.seat("A1").standing.build
rescue ArgumentError => e
puts e.message
endĐầu ra:
ticket for seat B7
standing ticket
seat and standing exclude each otherMột cách viết phổ biến trong Ruby là truyền đối tượng dựng vào một khối lệnh. Phương thức lớp tạo đối tượng dựng, gọi khối với đối tượng đó rồi gọi build ở cuối:
class Email
attr_accessor :to, :subject, :lines
def self.compose
email = new
email.lines = []
yield email
email
end
def to_s
"To: #{to}\nSubject: #{subject}\n\n#{lines.join("\n")}"
end
end
mail = Email.compose do |e|
e.to = "ada@x.io"
e.subject = "Hi"
e.lines << "Hello Ada,"
e.lines << "see you soon."
end
puts mailĐầu ra:
To: ada@x.io
Subject: Hi
Hello Ada,
see you soon.Với một vài giá trị tùy chọn, đối số từ khóa có giá trị mặc định trong Ruby thường đã đủ. Builder hữu ích khi các thành phần có thể lặp lại, thứ tự quan trọng hoặc cần kiểm tra tổ hợp trước khi đối tượng được tạo:
Pizza.new(size: "large", crust: "thin") # Một vài tùy chọn: từ khóa (keyword)
Query.new.from("users").where("age > 18").where("active = 1").limit(10).to_sThử thách
DễHoàn thiện PizzaBuilder và Pizza. Các bước của builder là size(value), crust(value) và topping(name) (có thể lặp lại), trả về builder. build gây ra ArgumentError với size is required khi chưa có kích cỡ, dùng lớp vỏ classic khi chưa chọn lớp vỏ nào và trả về một Pizza. Pizza#to_s trả về large thin pizza with cheese, ham hoặc small classic pizza with no toppings.
Mỗi dòng đầu vào là một đơn hàng, chẳng hạn như size=large crust=thin topping=cheese topping=ham. Code được cung cấp gửi từng phần đến builder theo thứ tự, bằng cách nối chuỗi lời gọi, rồi in ra bánh pizza hoặc thông báo lỗi.
Code của bạn nằm trong pizza.rb và pizza_builder.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 'pizza'
require_relative 'pizza_builder'
# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
builder = line.split.reduce(PizzaBuilder.new) do |b, part|
step, value = part.split("=")
b.public_send(step, value)
end
begin
puts builder.build
rescue ArgumentError => e
puts 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