Strategy Pattern trong Ruby
Bài đăng này đã không được cập nhật trong 3 năm
Như đã nhắc tới trong bài viết về Template Method Pattern trong Ruby, pattern này giúp chúng ta thay đổi 1 phần của thuật toán, tách đoạn xử lý phức tạp trong thuật toán ra cho các subclass xử lý, nó giúp chúng ta tối giản hoá thuật toán. Có thể nói pattern này xử lý khá hiệu quả, và đã đáp ứng được nguyên tắc đầu tiên trong thiết kế pattern "Separate out the things that change from those that stay the same.". Tuy nhiên, nó lại đang vi phạm nguyên tắc số 3 "Prefer composition over inheritance" vì cách này sử dụng nhiều tới kế thừa dẫn tới một số hạn chế và nhược điểm:
- Các subclass sẽ bị rối theo superclass nếu bạn thiết kế không cẩn thận.
- Hạn chế sự linh hoạt về thời gian thực thi
- Một khi đã chọn được phần biến đổi trong thuật toán, tương ứng với việc chọn format của report, thì việc thay đổi lại sẽ gặp khó khăn. Vì nếu muốn tạo 1 report theo format khác, chúng ta sẽ phải tạo một report hoàn toàn mới.
reportHTML = HTMLReport.new
reportHTML.output_report
#Để tạo 1 report với format khác chúng ta phải tạo 1 đối tượng mới
reportPlain = PlainTextReport.new
reportPlain.output_report
Để giải quyết vấn đề này, chúng ta có thể áp dụng theo nguyên tắc 4 của GoF “Delegate, delegate, delegate.”
class Formatter
def output_report( title, text )
raise 'Abstract method called'
end
end
#HTML
class HTMLFormatter < Formatter
def output_report( title, text )
puts('<html>')
puts(' <head>')
puts(" <title>#{title}</title>")
puts(' </head>')
puts(' <body>')
text.each do |line|
puts(" <p>#{line}</p>" )
end
puts(' </body>')
puts('</html>')
end
end
#PlainText
class PlainTextFormatter < Formatter
def output_report(title, text)
puts("***** #{title} *****")
text.each do |line|
puts(line)
end
end
end
Với class Report, ta có thể loại bỏ các chi tiết format cho báo cáo vì nó đã được cài đặt trong class Format
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = 'Monthly Report'
@text = ['Things are going', 'really, really well.']
@formatter = formatter
end
def output_report
@formatter.output_report( @title, @text )
end
end
Ý tưởng của pattern strategies là xác định tập các đối tượng làm những việc giống nhau (trong ví dụ việc này là Formatter).
Bây giờ, chúng ta có thể dễ dàng chuyển đổi giữa các format report khác nhau mà không cần khởi tạo đối tượng mới
report = Report.new(HTMLFormatter.new)
report.output_report
####
report.formatter = PlainTextFormatter.new
report.output_report
Tham khảo
Github (updating):https://github.com/ducnhat1989/design-patterns-in-ruby
Sách: “DESIGN PATTERNS IN RUBY” của tác giả Russ Olsen
Bài viết liên quan:
All rights reserved