Design-Patterns

Template Method

Khái Niệm

Template Pattern là một mẫu thiết kế thuộc loại mẫu thiết kế hành vi (behavioral design pattern) trong phát triển phần mềm. Mẫu này hoạt động bằng cách xác định khung sườn của một thuật toán trong một phương thức, hoãn một số bước cho các lớp con. Template Pattern cho phép lớp con có thể thay đổi hoặc mở rộng các bước cụ thể của thuật toán mà không thay đổi cấu trúc tổng thể của thuật toán.

Tổng quan

Đặt vấn đề

Trong nhiều ứng dụng phần mềm, các lớp khác nhau thường có những phần xử lý giống nhau, nhưng cũng có một số bước thực thi cần được tuỳ chỉnh theo từng ngữ cảnh cụ thể. Điều này dẫn đến việc lặp lại mã nguồn, làm tăng độ phức tạp và khó khăn trong việc bảo trì. Ví dụ, trong một ứng dụng về xử lý bản tin, các loại bản tin khác nhau như tin tức, phân tích thị trường, và báo cáo kỹ thuật có thể cần đến một quy trình xử lý tương tự nhưng với một số bước đặc biệt tuỳ chỉnh cho từng loại.

graph TD;
    A[Bản tin tổng quát] --> B[Tin tức]
    A --> C[Phân tích thị trường]
    A --> D[Báo cáo kỹ thuật]
    B --> E{Quy trình xử lý chung}
    C --> E
    D --> E
    E --> F[Tuỳ chỉnh theo loại]

Giải pháp

Template Method Pattern giải quyết vấn đề trên bằng cách xác định khung của một thuật toán trong một phương thức, chừa lại các bước cụ thể để được ghi đè trong các lớp con. Điều này cho phép các lớp con mở rộng các bước cụ thể mà không cần thay đổi cấu trúc của thuật toán. Trong ví dụ về bản tin, có thể tạo một lớp trừu tượng với các phương thức cố định và các phương thức trừu tượng tương ứng với các bước tuỳ chỉnh.

Việc áp dụng Template Method Pattern giúp giảm bớt sự trùng lặp mã nguồn và tăng tính tái sử dụng. Nó cũng giúp tập trung quản lý quy trình xử lý, đồng thời cung cấp khuôn mẫu cho các phần tuỳ chỉnh, làm cho mã nguồn dễ hiểu và bảo trì hơn.

Mặc dù Template Method Pattern giúp giảm sự lặp code và tăng tính mô-đun, nhưng nó cũng có thể dẫn đến một cấu trúc lớp phức tạp hơn và ít linh hoạt hơn do cơ chế kế thừa. Ngoài ra, việc sử dụng quá mức có thể làm giảm sự minh bạch và khả năng hiểu mã nguồn cho những người mới làm quen.

classDiagram
    class NewsletterTemplate {
        +publishNewsletter()
        #writeNewsSection()
        #writeMarketAnalysis()
        #writeTechnicalReport()
    }
    class SpecificNewsletter {
        #writeNewsSection()
        #writeMarketAnalysis()
        #writeTechnicalReport()
    }

    NewsletterTemplate <|-- SpecificNewsletter : extends

Cấu trúc của Template Method Pattern

classDiagram
      class AbstractClass {
        +templateMethod()
        #primitiveOperation1()
        #primitiveOperation2()
      }
      class ConcreteClass {
        #primitiveOperation1() 
        #primitiveOperation2()
      }
      
      AbstractClass <|.. ConcreteClass : Extends

Mục đích của Template Method Pattern là để xác định khung của một thuật toán trong một phương thức, trì hoãn một số bước đến các lớp con. Template Method cho phép lớp con ghi đè và mở rộng cấu trúc mà không thay đổi cấu trúc tổng thể của thuật toán.

Cách triển khai Template Method Pattern

1. Abstract Class

Đây là lớp trừu tượng định nghĩa phương thức template. Phương thức này chứa một loạt các bước, một số trong đó có thể được triển khai ở lớp này hoặc bị hoãn lại để các lớp con triển khai.

public abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    // Template method
    public final void play(){
        // Initialize the game
        initialize();

        // Start game
        startPlay();

        // End game
        endPlay();
    }
}

2. Concrete Classes

Các lớp này triển khai các phần của phương thức template cần được tùy chỉnh.

public class Cricket extends Game {
    @Override
    void initialize() {
        System.out.println("Cricket Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Cricket Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Cricket Game Finished!");
    }
}

public class Football extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

3. Sử dụng Pattern

Đây là cách chúng ta có thể sử dụng Template Method Pattern trong một ứng dụng.

public class TemplatePatternDemo {
    public static void main(String[] args) {

        Game game = new Cricket();
        game.play();
        System.out.println();

        game = new Football();
        game.play();
    }
}

Trong ví dụ trên, CricketFootball là các lớp con của Game và chúng triển khai theo phương thức template được định nghĩa trong lớp trừu tượng Game. Phương thức play() là phương thức template và nó gọi đến ba phương thức khác mà các lớp con phải cung cấp cài đặt.

Ví dụ

Trong ví dụ này, Game đại diện cho AbstractClass, nó chứa một số phương thức abstract (initialize(), startPlay(), endPlay()) và một phương thức Template (play()), tự động gọi tất cả các phương thức khác theo đúng trình tự. CricketFootball là các ConcreteClass, chúng cung cấp cài đặt cụ thể cho các phương thức trừu tượng của Game. Khi play() được gọi, nó thực hiện quy trình trò chơi từ đầu đến cuối, từ khởi tạo đến bắt đầu chơi và cuối cùng là kết thúc trò chơi, cho phép các lớp con chỉ định các bước cụ thể mà không thay đổi cấu trúc tổng thể.

// AbstractClass
abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    // Template method
    public final void play() {
        // Initialize the game
        initialize();

        // Start game
        startPlay();

        // End game
        endPlay();
    }
}

// ConcreteClass
class Cricket extends Game {
    @Override
    void initialize() {
        System.out.println("Cricket Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Cricket Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Cricket Game Finished!");
    }
}

// ConcreteClass
class Football extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

// TemplatePatternDemo class
public class TemplatePatternDemo {
    public static void main(String[] args) {

        Game game = new Cricket();
        game.play();
        System.out.println();

        game = new Football();
        game.play();
    }
}

Khi nào nên sử dụng Template Method