Template Method Pattern là một mẫu thiết kế hành vi, nó định nghĩa một khung của một thuật toán trong lớp cha và cho phép các lớp con ghi đè các bước cụ thể của thuật toán mà không làm thay đổi cấu trúc.
Template Method Pattern là một trong những mẫu thiết kế phổ biến nhất, nó là một kỹ thuật cơ bản của việc tái sử dụng mã dựa trên kế thừa. Trong sơ đồ lớp của Template Method Pattern, chỉ có mối quan hệ kế thừa.
Template Method Pattern yêu cầu sự cộng tác giữa nhà thiết kế của lớp trừu tượng và các lớp con cụ thể. Một nhà thiết kế đảm nhận việc xác định khung của thuật toán, tức là triển khai tất cả các phương thức cần thiết cho thuật toán.
Các phương thức chung được đặt trong phương thức cụ thể của lớp cha.
Các phương thức cụ thể của từng lớp con được đặt trong phương thức cụ thể của lớp con đó. Tuy nhiên, lớp cha phải có các phương thức trừu tượng tương ứng.
Phương thức kết nối có thể cho phép lớp con quyết định xem có nên kết nối các điểm khác nhau của thuật toán hay không.
Ứng dụng
Khi bạn chỉ muốn mở rộng một bước cụ thể của thuật toán, mà không phải toàn bộ thuật toán hoặc cấu trúc của nó, bạn có thể sử dụng Template Method Pattern.
Khi các thuật toán của nhiều lớp chỉ khác nhau một chút, bạn có thể sử dụng mẫu thiết kế này. Tuy nhiên, hậu quả của việc này là khi thuật toán thay đổi, bạn có thể phải sửa đổi tất cả các lớp.
Cấu trúc
Giải thích cấu trúc
Lớp trừu tượng (Abstract Class) khai báo và triển khai một Template Method. Template Method này xác định cấu trúc của thuật toán và gọi các phương thức cụ thể tương ứng. Các bước thuật toán có thể được khai báo là kiểu trừu tượng hoặc có thể cung cấp một số triển khai mặc định.
Lớp cụ thể (Concrete Class) có thể ghi đè tất cả các bước hoặc một số bước của thuật toán, nhưng không thể ghi đè Template Method chính.
Mẫu mã cấu trúc
AbstractClass: Lớp trừu tượng, định nghĩa và triển khai một Template Method. Template Method này xác định cấu trúc của thuật toán, trong đó các bước logic được hoãn lại để các lớp con triển khai. Phương thức cấp cao cũng có thể gọi các phương thức cụ thể.
ConcreteClass: Triển khai một hoặc nhiều phương thức trừu tượng của lớp cha.
Khách hàng
Pseudocode
Trong ví dụ này, Template Method Pattern được sử dụng để cung cấp một “khung” cho các nhánh AI khác nhau trong một trò chơi chiến thuật đơn giản.
Một lớp AI cho trò chơi đơn giản.
Tất cả các chủng tộc trong trò chơi đều có các đơn vị và công trình gần như giống nhau. Do đó, bạn có thể tái sử dụng cấu trúc AI giống nhau trên các chủng tộc khác nhau, đồng thời vẫn cần khả năng ghi đè một số chi tiết. Bằng cách này, bạn có thể ghi đè AI của Orcs để làm nó tấn công hơn, hoặc làm cho con người tập trung vào phòng thủ, hoặc ngăn chặn quá trình xây dựng công trình của quái vật. Để thêm một chủng tộc mới vào trò chơi, bạn cần tạo một lớp con AI mới và ghi đè các phương thức mặc định được khai báo trong lớp cơ sở AI.
Ví dụ
Template Method Pattern được áp dụng rộng rãi trong nhiều tình huống.
Trong chương về Template Method Pattern trong sách “Head First”, có một ví dụ rất đặc trưng.
Trong cuộc sống hàng ngày, trà và cà phê là những loại đồ uống phổ biến. Quá trình pha trà và pha cà phê như thế nào?
Hãy xem xét quy trình.
Pha trà: Đun nước sôi > Ngâm lá trà > Rót vào cốc ⇒ Thêm chanh
Pha cà phê: Đun nước sôi > Pha cà phê > Rót vào cốc ⇒ Thêm đường và sữa
Từ các bước xử lý trên, dễ thấy quy trình chuẩn bị đồ uống này rất tương tự nhau. Chúng ta có thể sử dụng Template Method để xác định cấu trúc của thuật toán pha đồ uống.
Các bước chung và có tính chất chung (ví dụ: đun nước sôi, rót vào cốc) được thực hiện trong phương thức cụ thể của lớp trừu tượng.
Các bước có sự khác biệt được thực hiện trong các lớp cụ thể tương ứng. Tuy nhiên, lớp trừu tượng phải có các phương thức trừu tượng tương ứng.
【Lớp trừu tượng】
【Lớp cụ thể】
【Khách hàng】
Kết quả
============= Chuẩn bị trà =============
Đun nước sôi
Ngâm lá trà
Rót vào cốc
Thêm chanh
============= Chuẩn bị cà phê =============
Đun nước sôi
Pha cà phê
Rót vào cốc
Thêm đường và sữa
Ví dụ thực tế
Ví dụ sử dụng: Mẫu Template Method thường được sử dụng trong các framework Java. Nhà phát triển thường sử dụng nó để cung cấp một cách đơn giản để mở rộng chức năng tiêu chuẩn thông qua kế thừa cho người dùng của framework.
Dưới đây là một số ví dụ về Template Method trong các thư viện Java cốt lõi:
javax.servlet.http.HttpServlet, tất cả các phương thức doXXX() mặc định gửi phản hồi lỗi HTTP 405 “Phương thức không được phép”. Bạn có thể ghi đè lên chúng bất cứ lúc nào.
Cách nhận biết: Template Method có thể được nhận biết thông qua phương thức hành vi, phương thức này đã có một hành vi “mặc định” được định nghĩa trong lớp cơ sở.
Loại bỏ if … else và mã lặp lại
Giả sử chúng ta muốn phát triển một chức năng giỏ hàng để xử lý khác nhau cho các người dùng khác nhau:
Người dùng thông thường phải trả phí vận chuyển, phí vận chuyển là 10% giá sản phẩm, không có giảm giá sản phẩm;
Người dùng VIP cũng phải trả phí vận chuyển 10% giá sản phẩm, nhưng khi mua hai mặt hàng giống nhau trở lên, từ mặt hàng thứ ba trở đi sẽ được giảm giá một số lượng nhất định;
Người dùng nội bộ có thể miễn phí vận chuyển, không có giảm giá sản phẩm.
Phiên bản 1.0 của vấn đề
Giỏ hàng của người dùng thông thường
Giỏ hàng của người dùng VIP
Giỏ hàng của người dùng nội bộ
Client
So sánh mã, chúng ta có thể thấy rằng 70% mã của ba loại giỏ hàng là trùng lặp. Lý do rất đơn giản, mặc dù cách tính phí vận chuyển và giảm giá khác nhau cho các người dùng khác nhau, nhưng logic khởi tạo giỏ hàng, tính tổng giá trị, tổng phí vận chuyển, tổng giảm giá và giá thanh toán của toàn bộ giỏ hàng đều giống nhau.
Phiên bản đã sửa
Vấn đề của phiên bản 1.0 là: cùng một đoạn mã nên chỉ xuất hiện ở một nơi.
Nếu chúng ta đã hiểu rõ định nghĩa của lớp trừu tượng và phương thức trừu tượng, có thể nghĩ đến việc có thể định nghĩa đoạn mã lặp lại trong lớp trừu tượng và chỉ cần triển khai phần mã khác nhau cho ba giỏ hàng?
Thực tế, mẫu thiết kế này chính là mẫu Template Method.
Dưới đây là phiên bản tối ưu dựa trên mẫu thiết kế Factory + Template Method.
Template Method Pattern dựa trên cơ chế kế thừa: nó cho phép bạn thay đổi một phần của thuật toán bằng cách mở rộng nội dung của lớp con. Strategy Pattern dựa trên cơ chế tổ hợp: bạn có thể thay đổi một phần hành vi của đối tượng bằng cách cung cấp các chiến lược khác nhau cho hành vi tương ứng. Template Method hoạt động trên cấp độ lớp, do đó nó là tĩnh. Strategy hoạt động trên cấp độ đối tượng, cho phép thay đổi hành vi trong thời gian chạy.