Ra mắt hai series mới cực hot Trí tuệ nhân tạo A đến ZPython công cụ không thể thiếu khi nghiên cứu Data science, Machine learning.

Strategy Pattern xây dựng chiến lược trong lúc chạy

1. Strategy Pattern là gì?

Tần suất sử dụng: 4/5, Strategy pattern được sử dụng khá nhiều trong lập trình.

Strategy Pattern UML Mỗi dạng Design Pattern sẽ giới thiệu một Pattern tiêu biểu và Behavioral Design Pattern sẽ được bàn luận với pattern cuối cùng là Strategy pattern, mẫu này áp dụng khi làm việc với các hành vi của đối tượng, nó “đánh dấu” cách ứng dụng chạy. Factory pattern có thể thay đổi dạng đối tượng thì Strategy có thể thay đổi thuật toán (hành vi) của đối tượng. Strategy rất hữu ích trong một số trường hợp nơi mà các class là tương tự nhau nhưng không liên quan và khác nhau về hành vi. Ví dụ, chúng ta cần lọc các chuỗi, các bộ lọc khác nhau có thể sử dụng:

  • Loại bỏ các thẻ HTML
  • Loại bỏ các từ ngữ không phù hợp.
  • Loại bỏ các ký tự sử dụng để gửi thư rác thông qua hình thức liên lạc

    Thông thường chúng ta sẽ làm 3 giải pháp và áp dụng chúng vào các chuỗi cần lọc. Các bộ lọc có thể được áp dụng một cách khác nhau. Đầu tiên, định nghĩa interface với các tính năng cần thiết

interface Filter {
  function filter($str);
}

Xác định dạng bộ lọc sau đó implement các phiên bản thích hợp của phương thức trong interface:

class HtmlFilter implements Filter {
  function filter($str) {
    // Loại bỏ mã HTML
    return $str;
  }
}
class SwearFilter implements Filter {
  function filter($str) {
    // Loại bỏ các từ ngữ không phù hợp.
    return $str;
  }
}

Cuối cùng, sử dụng bộ lọc trong một class khác.

<pre class="prettyprint prettyprinted"><span class="kwd">class</span> <span class="typ">FormData</span> <span class FormData {
  private $_data = NULL;
  function __construct($input) {
    $this->_data = $input;
  }
  function process(Filter $type) {    
    $this->_data = $type->filter($this->_data);
  }
}

Phương thức process() nhận một đối tượng dạng Filter và thông qua đó dữ liệu được lọc.

$form = new FormData($someUserInput);
if (!$allowHTML) {
  $form->process(new HtmlFilter());
}
if (!allowSwear) {
  $form->process(new SwearFilter());
}

2. Ví dụ áp dụng Strategy Pattern trong PHP

OK, lý thuyết là như vậy, chúng ta cùng áp dụng vào thực tế, ví dụ về một ứng dụng quản lý sinh viên. Tạo ra file strategy.php trong oop\pattern với nội dung:

<!doctype html>
<html lang="vi">
<head>
    <meta charset="utf-8">
    <title>Ví dụ về Strategy pattern</title>
</head>
<body>
    <?php 

    // Interface Sort định nghĩa phương thức sort()
    interface iSort {
        function sort(array $list);
    }

    // Lớp MultiAlphaSort sắp xếp mảng đa chiều chứa ký tự
    class MultiAlphaSort implements iSort {
        // Cách sắp xếp: tăng dần, giảm dần
        private $_order;

        // Sort index:
        private $_index;

        function __construct($index, $order = 'ascending') {
            $this->_index = $index;
            $this->_order = $order;
        }

        // Phương thức thực hiện sắp xếp
        function sort(array $list) {

            // Change the algorithm to match the sort preference:
            if ($this->_order == 'ascending') {
                uasort($list, array($this, 'ascSort'));
            } else {
                uasort($list, array($this, 'descSort'));
            }
            return $list;
        }
        // Phương thức so sánh hai giá trị
        function ascSort($x, $y) {
            return strcasecmp($x[$this->_index], $y[$this->_index]);
        }
        function descSort($x, $y) {
            return strcasecmp($y[$this->_index], $x[$this->_index]);
        }
    }

    // Class MultiNumberSort sắp xếp một mảng đa chiều
    class MultiNumberSort implements iSort {
        // Cách sắp xếp
        private $_order;

        // Sort index
        private $_index;

        function __construct($index, $order = 'ascending') {
            $this->_index = $index;
            $this->_order = $order;
        }

        // Thực hiện sắp xếp
        function sort(array $list) {
            // Thay đổi thuật toán phù hợp với thiết lập
            if ($this->_order == 'ascending') {
                uasort($list, array($this, 'ascSort'));
            } else {
                uasort($list, array($this, 'descSort'));
            }
            return $list;
        }
        // Phương thức so sánh hai giá trị
        function ascSort($x, $y) {
            return ($x[$this->_index] > $y[$this->_index]);
        }
        function descSort($x, $y) {
            return ($x[$this->_index] < $y[$this->_index]);
        }
    }

    /* Lớp StudentsList
     * Lớp có 1 thuộc tính: _students.
     * Lớp có 3 phương thức:
     * - __construct()
     * - sort()
     * - display()
     */
    class StudentsList {
        // Danh sách sinh viên được sắp xếp 
        private $_students = array();

        function __construct($list) {
            $this->_students = $list;
        }

        // Thực hiện sắp xếp sử dụng một thực thi từ iSort
        function sort(iSort $type) {
            $this->_students = $type->sort($this->_students);
        }

        // Hiển thị danh sách sinh viên dạng HTML
        function display() {
            echo '<ol>';
            foreach ($this->_students as $student) {
                echo "<li>{$student['last_name']} {$student['first_name']} : {$student['grade']}</li>";
            }
            echo '</ol>';
        }

    }

    // Tạo mảng sinh viên, mỗi sinh viên có cấu trúc studentID => array('first_name' => 'First Name', 'last_name' => 'Last Name', 'grade' => XX.X)
    $students = array(
        256 => array('first_name' => 'Tuấn', 'last_name' => 'Trần Đăng', 'grade' => 98.5),
        2   => array('first_name' => 'An', 'last_name' => 'Nguyễn Xuân', 'grade' => 85.1),
        9   => array('first_name' => 'Dương', 'last_name' => 'Nguyễn Ngọc', 'grade' => 94.0),
        364 => array('first_name' => 'Chiến', 'last_name' => 'Hoàng Văn', 'grade' => 85.1),
        68  => array('first_name' => 'Phương', 'last_name' => 'Trần Thanh', 'grade' => 74.6)
    );

    // Tạo đối tượng
    $list = new StudentsList($students);

    // Hiển thị mảng trước khi sắp xếp
    echo '<h2>Danh sách gốc</h2>';
    $list->display();

    // Sắp xếp theo tên
    $list->sort(new MultiAlphaSort('first_name'));
    echo '<h2>Danh sách sắp xếp theo tên</h2>';
    $list->display();

    // Sắp xếp theo điểm
    $list->sort(new MultiNumberSort('grade', 'descending'));
    echo '<h2>Danh sách sắp xếp theo điểm</h2>';
    $list->display();

    // Xóa đối tượng
    unset($list);
    ?>
</body>
</html>

Trong ví dụ này chúng ta thấy rằng cùng là hành vi sort() nhưng nó khác nhau ở các thời điểm khác nhau, chỗ thì sắp xếp tên, chỗ thì sắp xếp điểm. Như vậy việc áp dụng Strategy Pattern giúp chúng ta thay đổi được hành vi của một đối tượng trong thời gian chạy. Kết quả khi thực hiện trong trình duyệt như sau:

Ví dụ Strategy Pattern


CÁC BÀI VIẾT KHÁC

FirebirD

Đam mê Toán học, Lập trình. Sở thích chia sẻ kiến thức, Phim hài, Bóng đá, Cà phê sáng với bạn bè.

Composite Pattern từ khái niệm đến thực hành

Chuyển website sang chế độ bảo trì trong Laravel

1 Bình luận trong "Strategy Pattern xây dựng chiến lược trong lúc chạy"

  1. Tèo

    5 years ago

    Phản hồi
    Bài viết khá hay. Hy vọng tác giả cho ra đủ tất cả các design pattern luôn

Thêm bình luận