08/10/2018, 07:57

Design Pattern in Ruby: Strategy

Định nghĩa về pattern: A Pattern is a solution to a problem in a context Có 3 phần của một pattern: Context : là hoàn cảnh, trường hợp mà pattern được áp dụng. Nó là tình huống thường mang tính định kì. Problem: vấn đề mà bạn đang gặp phải và phải giải quyết để đạt được mục ...

Định nghĩa về pattern:

A Pattern is a solution to a problem in a context

      Có 3 phần của một pattern:

  • Context : là hoàn cảnh, trường hợp mà pattern được áp dụng. Nó là tình huống thường mang tính định kì.
  • Problem: vấn đề mà bạn đang gặp phải và phải giải quyết để đạt được mục tiêu của mình, nó cũng mang theo các ràng buộc (constraints) xảy ra trong hoành cảnh đó (context).
  • Solution: là giải pháp sau cùng, các thiết kế chung để mọi người đều có thể áp dụng để giải quyết vấn đề để đạt được mục tiêu với các ràng buộc.

Có thể ví dụ vui như thế này: bạn có một vấn đề, bạn phải đến đi làm và đến công ty đúng giờ nhưng bạn lại để chìa khóa ở trong xe và khóa cửa lại.Trong ví dụ này:

  • Problem: làm thế nào để đi làm đúng giờ
  • Context: xe khóa và chìa khóa để trong xe
  • Solution: phá cửa, vào trong xe, bật máy và đi làm =))

Ta gần như có đầy đủ tất cả mọi thành phần của một pattern: ta có problem bao gồm là mục tiêu đi làm với ràng buộc (constraints) về mặt thời gian, khoảng cách và có thể vài yếu tố khác (tắc đường chẳng hạn..). Ta có context là chìa khóa để trong xe và xe không thể mở được và solution giúp ta lấy được chìa khóa và giải quyết cả ràng buộc về thời gian và quãng đường.

Khi học một desingn pattern có 3 vấn đề cần chú ý:

  • Vấn đề mà design pattern đó giải quyết
  • Sơ đồ UML mô tả design pattern
  • Code minh họa

Định nghĩa

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Trường hợp áp dụng:

We need to vary part of an algorithm— something we previously solved using the Template Method pattern— although we want to avoid its drawbacks, introduced by the fact that it's built around inheritance.

Sơ đồ mô tả:
Algorithm ở đây có thể hiểu là code theo một thứ tự nào đó để cho ra một kết quả.
Có 2 nguyên tắc design được thể hiện trong strategy:

Program to an interface, not an implementation.
Favor composition over inheritance

Để dễ hình dung ta có thể tham khảo ví dụ về report format: gỉa sử phải viết monthly report dưới nhiều định dạng khác nhau, bạn viết một chương trình nhỏ chỉ việc nhập vào tiệu đề và nội dung sau đó sẽ tự generate ra format yêu cầu.Trường hợp ở đây là là generate 2 format là HTML và plain text. Nếu code design sử dụng strategy sẽ được viết như sau:
Định nghĩa class Report:

class Report
  attr_accessor :format
  
  def initialize(format)
    @title = "Monthly Report"
    @content = "Things are going very well"
    @format = format
  end
  
  def output_report
    @format.output_report(self)
  end
end

Định nghĩa class HTMLFormat và PlainTextFormat:

class HTMLFormat
  def output_format(context)
      puts("<html>")
      puts(" <head>")
      puts("<title>#{context.title}</title>")
      puts(" </head>")
      puts(" <body>")
      puts("<p>#{context.content}</p>")
      puts(" </body>")
      puts("</html>")
  end
end

class PlanTextFormat
  def output_report(context)
    puts("***** #{context.title} *****")
    puts(context.content)
  end
end

Ta có thể sử dụng như sau:

report = Report.new(HTMLFormat.new)
report.output_format #HTML format
report.format = PlainTextFormat.new
report.output_format #Plain text output

Ý tưởng chính ở đây là tách các "algorithm" riêng ra thành các object,sẽ định nghĩa một họ các object (family of object) được gọi là các strategies cùng thực hiện một thứ như trong trường hợp trên là format report, các object đều cùng một interface.
Chuyên mục Q and A
Q: ở ví dụ trên có nhất định dùng strategy ko?
A: ko, bạn có thể chỉ định nghĩa mình class Report và một method output_report nhưng trong method này sẽ phải hard code dùng if else để check hoặc sử dụng template method, nhưng mỗi cách sẽ có 1 hạn chế nhất địn, trường hợp này nên áp dụng strategy là tốt nhất.
Q: template method là gì? Ở trường hợp áp dụng có nhắc đến "avoid its drawbacks" vậy cái drawbacks ở đây là gì?
A: Template method cũng là một design pattern (bạn có thể lên mạng tìm hiểu hoặc chờ bài viết sau của tui) như trong ví dụ trên nếu áp dụng template method thì sẽ xây dưng một base class Report và sau đó với mỗi loại format ta sẽ xây dưng một class tương ứng và kế thừa từ Report class và overide lại các method tướng ứng. Drawbacks của template method là nó xây dựng dựa trên tính kế thừa, class con cũng sẽ rối lên với class cha và tính kế thừ cũng làm giảm tính linh hoạt trong thời gian chạy.
Q: strategy có gì hay ho?
A: strategy được xây dựng dựa trên compostion và deligation nên có khả năng linh hoạt, bằng việc implement mỗi phiên bản của "algorithm" vào một object riêng biệt, bạn có thể thay đổi "algorithm" bằng việc cũng cấp một strategy object khác cho context, như ví dụ sử dụng trong trường hợp trên, từ report có thể sử dụng để in cả HTML và plain text format chỉ bằng việc đồi format instance bằng strategy object tương ứng.

0