Một ví dụ đơn giản về Strategy Pattern

Trong phát triển phần mềm nếu bạn biết cách áp dụng các mẫu thiết kế (design pattern) bạn sẽ nhanh chóng có được ứng dụng với thiết kế đơn giản nhưng hiệu quả khi bảo trì, nâng cấp hoặc mở rộng chúng. Một trong những mẫu thiết kế đơn giản và rất dễ triển khai mà tôi trình bài trong bài viết này đó là Strategy (còn có tên gọi khác là Policy). Sau khi mô tả cơ bản về nó tôi sẽ sử dụng ngôn ngữ Java để minh họa cho mẫu thiết kế này, bạn sẽ không mất quá nhiều thời gian để nắm được Strategy và áp dụng nó vào thực tế đâu, mà biết đâu bạn đã sử dụng nó mà không biết đó là một pattern nổi tiếng?!

Strategy là một trong số rất nhiều các mẫu thiết kế dành cho phát triển ứng dụng với OOP, bạn có thể tham khảo thêm các mẫu thiết kế khác ở đây: http://www.oodesign.com. Đây là pattern cho phép các giải thuật khác nhau có thể được lựa chọn trong thời-gian-chạy (run-time). Hay nói cách khác, Strategy định nghĩa một họ các giải thuật khác nhau, mỗi giải thuật được triển khai bởi một lớp (class) cụ thể và chúng có thể hoán đổi cho nhau tùy vào ngữ cảnh. Strategy giúp các giải thuật khác nhau độc lập với client sử dụng nó. Ví dụ, một lớp thực hiện nhiệm vụ so sánh dữ liệu đầu vào có thể sử dụng mẫu thiết kế Strategy để tự động lựa chọn giải thuật cho việc này dựa trên loại dữ liệu, nguồn gốc của chúng, lựa chọn của người dùng hay các yếu tố khác. Những yếu tố này không được biết cho tới thời-gian-chạy (runtime) và khi đó tùy vào loại dữ liệu mà hệ thống lựa chọn cách thức so sánh khác nhau. Các giải pháp so sánh được đóng gói trong các đối tượng riêng biệt sẽ được sử dụng bởi những đối tượng thực hiện việc này tại các phân vùng khác nhau của hệ thống (hoặc thậm chí ở những hệ thống khác nhau) mà không gây ra sự trùng lặp về mã lệnh.

Strategy thường được biểu diễn bằng UML như sau:

Sau đây tôi sẽ sử dụng Java để minh họa một ví dụ cụ thể về Strategy.

1. Sử dụng Interface có sẵn trong Java đó là java.lang.Comparable,  interface này có phương thức compareTo() cho phép các lớp triển khai nó thực hiện việc so sánh hai đối tượng với nhau.

Bạn có thể tìm hiểu thêm Interface này ở đây: http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html

2. Triển khai 2 lớp kế thừa Comparable, lớp Student thể hiện các đối tượng sinh viên, Product thể hiện các đối tượng sản phẩm.

Hai lớp này sẽ cài đặt phương thức compareTo() để so sánh các đối tượng Sinh viên với nhau hoặc các đối tượng Sản phẩm với nhau. Khi đó ta có sơ đồ quan hệ giữa các lớp và interface như sau:

Sau đây chúng ta cùng quan sát mã nguồn của 2 lớp này:

Lớp Student:

public class Student implements Comparable<Student> {

   private String rollNo;
   private String fullName;
   private double marks;

   public Student(String rollNo, String fullName, double marks) {
      this.rollNo = rollNo;
      this.fullName = fullName;
      this.marks = marks;
   }

   /* Hai sinh viên so sánh với nhau theo tên (fullName) */
   @Override
   public int compareTo(Student o) {
      if (o == null || o.fullName == null) {
         return 1;
      }
      if (this.fullName==null) {
         return -1;
      }
      return this.fullName.compareTo(o.fullName);
   }

   @Override
   public String toString() {
      return rollNo + " - " + fullName;
   }
}

Các giá trị trả về tùy thuộc vào đối tượng sinh viên kia hoặc tuy thuộc vào việc so sánh hai thuộc tính fullName của 2 đối tượng sinh viên.

Lớp Product:

public class Product implements Comparable<Product> {
   private String serial;
   private String productName;
   private double price;

   public Product(String serial, String productName, double price) {
      this.serial = serial;
      this.productName = productName;
      this.price = price;
   }

   /* Hai sản phẩm so sánh với nhau theo giá bán */
   @Override
   public int compareTo(Product o) {
      if (o == null || this.price > o.price) {
         return 1;
      }
      if (this.price < o.price) {
         return -1;
      }

      return 0;
   }

   @Override
   public String toString() {
      return serial + " - " + productName;
   }
}

Các giá trị trả về tùy thuộc vào đối tượng sản phẩm kia hoặc tùy thuộc vào việc so sánh hai thuộc tính price của 2 sản phẩm với nhau.

3. Chương trình minh họa

Chương trình dưới đây thể hiện việc so sánh giữa các đội tượng sinh viên với nhau và giữa các sản phẩm với nhau. Trong phương thức compare(), Collections (đóng vai trò của lớp Context trong hình vẽ UML mô tả Strategy ở trên) cài đặt phương thức sort() tự xác định loại đối tượng và sử dụng phương thức compareTo() cho phù hợp.

public class Client {
   public static void compare(ArrayList list) {
      if (list != null && list.size() > 0) {
         Collections.sort(list);
         for (Object obj : list) {
            System.out.println(" + " + obj);
         }
      }
   }

   public static void main(String[] args) {
      ArrayList<Student> students = new ArrayList<Student>();
      students.add(new Student("A01234", "Minh Le Hoang", 12.5));
      students.add(new Student("A01235", "An Nguyen Van", 15.5));
      students.add(new Student("A01235", "Tuan Nguyen Anh", 13.5));
      students.add(new Student("A01235", "Ha Le Hoang", 17.5));

      System.out.println("Sap xep sinh vien: ");
      //Hệ thống sẽ dùng phương thức compareTo() của lớp Student để so sánh các đối tượng Sinh viên:
      Client.compare(students);

      ArrayList<Product> products = new ArrayList<Product>();
      products.add(new Product("P0023", "Dell Vostro 3400", 1200));
      products.add(new Product("P0012", "IBM Thinpad T60", 1100));
      products.add(new Product("P0003", "Vaio Z", 3000));
      products.add(new Product("P0303", "HP Pavilon", 1230));

      System.out.println("Sap xep san pham: ");
      //Hệ thống sẽ dùng phương thức compareTo() của lớp Product để so sánh các đối tượng sản phẩm với nhau:
      Client.compare(products);
   }
}

Qua VD đơn giản này chắc bạn đã phần nào hiểu được về Strategy Pattern và sự hiện diện của nó trong các thiết kế sẵn có của Java như bạn đã thấy trong Comparable, mong rằng bạn sẽ áp dụng được mẫu thiết kế này cho các bài toán cụ thể của mình :o)