Tìm hiểu Template Pattern trong Laravel

Tìm hiểu về Template pattern sửa dụng laravel

Bài viết được tham khảo từ cuốn Design pattern for dummies áp dụng kỹ thuật thiết kế mẩu để việc lập trình và thiết kế website được chuyên nghiệp và hiệu quả hơn.

Mẫu Template method định nghĩa một bộ khung của một thuật toán trong một chức năng, chuyển giao việc thực hiện nó cho các lớp con. Mẫu Template Method cho phép lớp con định nghĩa lại cách thực hiện của một thuật toán, mà không phải thay đổi cấu trúc thuật toán, rất thích hợp cho việc tạo ra các chủng loại robot khác nhau.

Bài toán thực tế

Bạn nhận được một hợp đồng về những con robot tự động lắp ráp xe hơi. Không vấn đề gì, các lập trình viên nói và bắt đầu bắt tay ngay vào công việc.

Tạo con robot đầu tiên

Sau vài ngày, các lập trình viên đã tạo ra một phần mềm, nó vừa đủ đơn giản để đáp ứng yêu cầu. Lớp robot bắt đầu với 1 hàm khởi tạo như sau:

class Robot
    {
        public function Robot()
        {

        }
    }

Và có một số hàm mà robot có thể thực hiện, ví dụ như để khởi động robot, bạn sử dụng hàm start, để robot làm việc, bạn sử dụng hàm lắp ráp asemble, ...

class Robot
    {
        public function Robot()
        {
        }

        public function start()
        {
            return "Starting ...";
        }

        public function getParts()
        {
            return "Getting a carburetor...";
        }

        public function assemble()
        {
            return "Installing the carburetor...";
        }

        public function stop()
        {
            return "Stopping...";
        }
    }

Và tất cả những gì bạn cần là một phương thức, tên là go(), nó sẽ làm cho robot làm việc bằng cách gọi các hàm khác có trong lớp robot.

public function go()
{
    $this->start();
    $this->getParts();
    $this->assemble();
    $this->stop();
}

Và việc còn lại chỉ là chạy chương trình bằng cách gọi hàm go() như sau:

$robot = new Robot();
$robot->go();

Và con robot của bạn sẽ vận hành như sau:

Starting...
Getting a carburetor...
Installing the carburetor...
Stopping...

Tuyệt vời, dự án vẫn hoạt động tốt, và các lập trình viên thỏa mãn với điều đó.

Tạo Robot với mẫu thiết kế Template Method

Tiếp theo, bạn nhận được 1 hợp đồng mới, 1 phần mềm làm 1 con robot làm bánh nướng. Và các lập trình viên bắt đầu đắn đo, sẽ phải viết lại tất cả phần mềm từ đầu. Đây là thời điểm thích hợp để nói về mấu thiết kế Template Method.

Có một rắc rối mà lập trình viên phải đối mặt, họ có 1 con robot lắp ráp xe hơi, nhưng bây giờ ho cần 1 con robot nướng bánh. Và họ phải viết lại toàn bộ mã nguồn.

Con Robot nướng bánh có một số chức năng giống với con robot lắp ráp xe hơi như hàm start(), stop(), tuy nhiên nó cũng có 1 số khác biệt như hàm assemble() sẽ không hiển thị là Getting a carburetor... mà thay vào đó là Getting flour and sugar....

Mẫu thiết kế Template Method có thể giải quyết được vấn đề này. Mẫu này nói rằng, bạn có thể viết một phương thức, dùng để xác định một loạt các thuật toán, giống như hàm go mà bạn thấy trước đây, để chạy một loạt các chức năng cho robot.

public function go()
{
    $this->start();
    $this->getParts();
    $this->assemble();
    $this->stop();
}

Sau đó bạn đưa hàm này vòa 1 bộ khuân template bwangf cách cho phép các lớp con định nghĩa lại các bước thuật toán theo cách cần thiết. Trong trường hợp này, để làm một con robot nướng bánh, bạn sẽ viết lại các hàm geParts()asemble(). Bạn nên sử dụng mẫu Template Method khi bạn có một thuật toán được tạo bởi nhiều bước, và bạn muốn tùy chỉnh một số bước trong đó. Chú ý rằng nếu bạn muốn viết lại mọi thứ từ đầu thì bạn không cần dùng template.

Tạo robot bằng bộ khuôn Template

Bằng cách gọi hàm go(), tập hợp các thuật toán sẽ được thực hiện. Để tùy chỉnh trong lớp kế thừa, bạn chỉ cần viết lại một số bước mà bạn muốn.

Đó là ý tưởng đằng sau mẫu thiết kế Template Method - Một chức năng bào gồm nhiều bước sẽ được tùy chỉnh bởi lớp con. Trong trường hợp bạn cần 2 robot, 1 robot lắp ráp xe hơi và 1 robot nướng bánh, mọi việc sẽ như thế nào?

Bạn bắt đầu bằng cách tạo 1 bộ khuôn Template trong một lớp trừu tượng abstract, gọi là RobotTemplate

abstract class RobotTemplate
{
    public function go()
    {
        $this->start();
        $this->getParts();
        $this->assemble()
        $this->stop();
    }

    public function start()
    {
        return "Starting ...";
    }

    public function getParts()
    {
        return "Getting parts...";
    }

    public function assemble()
    {
        return "Assembling...";
    }

    public function stop()
    {
        return "Stopping...";
    }
}

Nếu 1 con robot sử dụng đúng các phương thức này, ví dụ như hàm start()stop(), chúng ta không cần phải viết lại chúng và chỉ cần thay đổi những phương thức mà con robot có sự thay đổi.

Ví dụ, bạn có thể sử dụng abstract RobotTemplate để tạo 1 con robot lắp ráp xe hơi. Bạn có kế thừa từ abstract RobotTemplate trong 1 lớp mới, lớp này ta đặt là AutoMotiveRobot.

class AutomotiveRobot extends RobotTemplate
{
     ....
}

Robot lắp ráp xe hơi này cần phải định nghĩa lại 1 số hàm như getParts()assemble() như sau:

class AutomotiveRobot extends RobotTemplate
{
    public function getParts()
    {
        return "Getting a carburetor...";
    }

    public function assemble()
    {
        return "Installing the carburetor...";
    }
}

Bạn có thể tùy chỉnh mã nguồn dựa trên template bằng cách thêm vào 1 số hàm, ví dụ như hàm khởi tạo sẽ nhận tên của con robot, và hàm getName sẽ trả về tên này.

class AutomotiveRobot extends RobotTemplate
{
    private $name;

    public function AutomotiveRobot($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

Bạn cũng có thể trùy chỉnh hàm go kế thừa từ template cho con robot nướng bánh. Bạn tạo lớp mới có tên là CookieRobot kế thừa từ abstract RobotTemplate. Bạn có thể viết lớp CookieRobot bằng cách viết lại các hàm getParts()assemble() như sau:

class CookieRobot extends RobotTemplate
{
    private $name;

    public function CookieRobot($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getParts()
    {
        return "Getting flour and sugar...";
    }

    public function assemble()
    {
        return "Installing a cookie...";
    }
}

Bạn đã sử dụng hàm go() từ bộ khuôn template để tạo ra 2 lớp mới AutomotiveRobotCookieRobot, và bạn đã viết lại một số bước trong thuật toán để phù hợp với từng con robot mà không cần phải viết lại 2 lớp mới này từ đầu.

Kiểm tra việc tạo Robot

Bạn hãy tạo 2 đối tượng của lớp AutomotiveRobotCookieRobot và gọi hàm go() như sau:

$automotiveRobot = new AutomotiveRobot("Automotive Robot");
$cookieRobot = new CookieRobot("Cookie Robot");
$automotiveRobot->go();
$cookieRobot->go();

Và bạn sẽ thấy chương trình đang chạy như bạn mong muốn.

Bạn sử dụng mẫu thiết kế Template Method khi bạn có một thuật toán với nhiều bước và bạn muốn cho phép tùy chỉnh chúng trong lớp con. Thật dễ dàng. Bằng cách viết lại các hàm đã được khai báo trong lớp trừu tượng, bạn sẽ thay đổi được theo cách mà bạn mong muốn.

Mẫu thiết kế Template Method là ý tưởng tuyệt vời khi bạn có một thuật toán nhiều bước mà chính bạn có thể tùy chỉnh nó.

Vậy là chúng ta vừa tìm hiểu xong về mẫu thiết kế Template Method (Template Pattern). Hy vọng bài viết sẽ giúp ích cho các bạn trong việc lập trình.