Hãy nói về mẫu thiết kế Strategy (Chiến lược)!
Mẫu thiết kế này giúp bạn thay đổi thuật toán chính bên trong một phương thức.
Bạn làm điều này bằng cách truyền vào một class triển khai thuật toán này, thay vì hardcode nó vào class.
Và khi tôi nói thuật toán tôi không có nghĩa là thuật toán khoa học máy tính phức tạp, mà là bất kỳ mã nào tuân theo một chuỗi các bước để có kết quả.
Khi nào sử dụng mẫu Strategy
Mẫu này giải quyết chính xác vấn đề gì?
Vấn đề của nguyên tắc open/closed (mở/đóng).
Nguyên tắc này nói rằng:
“Các thực thể phần mềm (class, module, phương thức) nên mở cho việc mở rộng & đóng cho việc sửa đổi.”
Điều này có nghĩa là để làm cho class làm điều mới, bạn không nên cần thay đổi class đó. Điều này làm cho mã của bạn linh hoạt & mạnh mẽ hơn cùng lúc.
Nhưng làm thế nào để đạt được điều đó?
Sử dụng các mẫu thiết kế như mẫu Strategy 🙂
Giả sử bạn có class ReportGenerator, và bạn muốn tạo nhiều loại định dạng báo cáo sử dụng cùng dữ liệu.
Bạn có thể viết một phương thức cho mỗi loại báo cáo, nhưng điều đó có nghĩa là bạn phải thay đổi class (bằng cách thêm phương thức mới) mỗi khi muốn thêm loại mới, vi phạm nguyên tắc open/close.
Hoặc thậm chí tệ hơn!
Bạn có thể có một phương thức KHỦNG KHỐNG với mọi loại câu lệnh if… Những thứ này thường không kết thúc tốt.
Nhưng nếu class không cần biết cách định dạng báo cáo thì sao?
Nếu thuật toán đến từ bên ngoài class?
Khi đó chúng ta có thể thay đổi thuật toán định dạng bất cứ khi nào muốn mà không thay đổi class bản thân.
Điều này giải quyết vấn đề.
Và đó chính xác là những gì mẫu Strategy làm.
Ví dụ mẫu Strategy
Một strategy được triển khai dưới dạng một class với một phương thức đơn lẻ.
Dưới đây là một số chiến lược định dạng cho class ReportGenerator:
require 'json'
module ReportFormatters
class JSON
def self.format(data)
data.to_json
end
end
class PlainText
def self.format(data)
data.to_s
end
end
class HTML
def self.format(data)
html = ""
html << "<ul>"
data.each { |product, amount| html << "<li>#{product}: #{amount}</li>" }
html << "</ul>"
html
end
end
end
Lưu ý cách tất cả các strategy triển khai phương thức format. Phương thức này sẽ được gọi bởi class ReportGenerator.
Đây là mã:
class ReportGenerator
def self.generate(data, formatter)
formatter.format(data)
end
end
data = { onions: 31, potatoes: 24, eggs: 10 }
p ReportGenerator.generate(data, ReportFormatters::HTML)
p ReportGenerator.generate(data, ReportFormatters::JSON)
p ReportGenerator.generate(data, ReportFormatters::PlainText)
Bây giờ:
Nếu bạn muốn thay đổi định dạng đầu ra (từ HTML sang PlainText) bạn chỉ cần truyền một strategy khác & nó sẽ hoạt động miễn là strategy triển khai phương thức format.
Một cách khác để nghĩ về mẫu này là bạn đang thuê một cố vấn chuyên về một chiến lược kinh doanh cụ thể, cố vấn sẽ đến & triển khai chiến lược cho bạn, sau đó chiến lược này sẽ tạo ra kết quả cụ thể.
Ngược lại, mẫu như Decorator giống như thêm trang trí mới cho cây thông Noel để làm cho nó lấp lánh hơn.
Bạn chỉ có thể làm việc với 1 strategy tại một thời điểm, nhưng bạn có thể có bao nhiêu trang trí tùy thích.
Video
Tóm tắt
Bạn đã học về mẫu thiết kế Strategy, hãy dùng nó khi bạn cần thay đổi một phần việc class của bạn làm mà không phải thay đổi class bản thân.
Cảm ơn bạn đã đọc!
Đừng quên chia sẻ bài viết này trên Twitter nếu bạn thấy nó thú vị, nó sẽ giúp nhiều người hơn tận hưởng nó 🙂