Strategy Pattern trong Ruby
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 ...
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
:
- CÁC NGUYÊN TẮC TRONG DESIGN PATTERN
- Template Method Pattern trong Ruby