Mục đích
Decorator Pattern là một mấu thiết kế thiết kế cấu trúc, cho phép thêm một số chức năng bổ sung vào một đối tượng mà không làm thay đổi cấu trúc của nó. Decorator Pattern linh hoạt hơn việc tạo ra các lớp con mới để mở rộng chức năng.
- Đối tượng trang trí và đối tượng thật có cùng giao diện. Điều này cho phép đối tượng khách hàng tương tác với đối tượng trang trí như với đối tượng thật.
- Đối tượng trang trí chứa một tham chiếu đến đối tượng thật.
- Đối tượng trang trí chấp nhận tất cả các yêu cầu từ khách hàng. Nó chuyển tiếp các yêu cầu này cho đối tượng thật.
- Đối tượng trang trí có thể thêm một số chức năng bổ sung trước hoặc sau khi chuyển tiếp các yêu cầu này. Điều này đảm bảo rằng chúng ta có thể thêm chức năng bổ sung từ bên ngoài mà không cần thay đổi cấu trúc của đối tượng đã cho. Trong thiết kế hướng đối tượng, thường sử dụng kế thừa để mở rộng chức năng của một lớp đã cho.
Trường hợp sử dụng
- Nếu bạn muốn sử dụng một đối tượng mà không cần chỉnh sửa mã nguồn và muốn thêm chức năng bổ sung cho đối tượng trong thời gian chạy, bạn có thể sử dụng Decorator Pattern.
- Nếu việc mở rộng chức năng của một đối tượng bằng cách kế thừa là khó thực hiện hoặc không khả thi, bạn có thể sử dụng mấu thiết kế này.
Cấu trúc
Mô tả cấu trúc
- Component (Thành phần): Khai báo giao diện chung cho các lớp trang trí và đối tượng được trang trí.
- Concrete Component (Thành phần cụ thể): Là lớp của đối tượng được trang trí. Nó xác định hành vi cơ bản, nhưng các lớp trang trí có thể thay đổi hành vi này.
- Base Decorator (Trang trí cơ bản): Lớp này có một biến tham chiếu đến đối tượng được trang trí. Kiểu của biến này nên được khai báo là giao diện thành phần chung, để nó có thể tham chiếu đến thành phần cụ thể và các trang trí. Lớp trang trí cơ bản sẽ chuyển tiếp tất cả các hoạt động cho đối tượng được trang trí.
- Concrete Decorators (Trang trí cụ thể): Xác định các chức năng bổ sung có thể được thêm vào thành phần. Các lớp trang trí cụ thể sẽ ghi đè các phương thức của lớp trang trí cơ bản và thực hiện các hành vi bổ sung trước hoặc sau khi gọi phương thức của lớp cha.
- Client (Khách hàng): Có thể sử dụng nhiều lớp trang trí để trang trí thành phần, miễn là nó có thể tương tác với tất cả các đối tượng thông qua giao diện chung.
Mẫu mã cấu trúc
Component: Định nghĩa một giao diện cho đối tượng, cho phép thêm chức năng bổ sung.
ConcreteComponent: Thực hiện giao diện đã được định nghĩa bởi Component.
Decorator: Lớp trang trí trừu tượng, kế thừa từ Component, mở rộng chức năng của lớp Component bằng cách bên ngoài, nhưng đối với Component, không cần biết về sự tồn tại của Decorator.
ConcreteDecorator: Đối tượng trang trí cụ thể, thêm chức năng bổ sung cho Component.
Client: Có thể sử dụng nhiều lớp trang trí để trang trí thành phần, miễn là nó có thể tương tác với tất cả các đối tượng thông qua giao diện chung.
Kết quả:
Hành vi ban đầu
======================================
Hành vi ban đầu
Thêm thuộc tính: Thuộc tính mới 1
======================================
Hành vi ban đầu
Thêm thuộc tính: Thuộc tính mới 1
Thêm hành vi
Pseudocode
Trong ví dụ này, mô hình trang trí được sử dụng để nén và mã hóa dữ liệu nhạy cảm, đồng thời tách dữ liệu khỏi mã nguồn sử dụng dữ liệu.
Chương trình sử dụng một cặp trang trí để đóng gói đối tượng nguồn dữ liệu. Hai trang trí này thay đổi cách đọc và ghi dữ liệu từ đĩa:
- Trước khi dữ liệu được ghi vào đĩa, trang trí mã hóa và nén dữ liệu. Mà không làm thay đổi lớp gốc, dữ liệu đã được mã hóa và bảo vệ được ghi vào tệp.
- Sau khi dữ liệu được đọc từ đĩa, trang trí giải nén và giải mã dữ liệu. Trang trí và lớp nguồn dữ liệu cùng tuân thủ một giao diện chung, cho phép thay thế chúng trong mã khách hàng.
Ví dụ
Ví dụ sử dụng: Mô hình trang trí là một cấu hình tiêu chuẩn trong mã Java, đặc biệt là trong mã liên quan đến tải dữ liệu theo luồng.
Có một số ví dụ về trang trí trong thư viện lõi của Java:
java.io.InputStream
,OutputStream
,Reader
, vàWriter
có các hàm tạo nhận đối tượng của chính kiểu của nó làm tham số.java.util.Collections
; các phương thứccheckedXXX()
,synchronizedXXX()
, vàunmodifiableXXX()
.javax.servlet.http.HttpServletRequestWrapper
vàHttpServletResponseWrapper
Cách nhận biết: Trang trí có thể được nhận biết bằng cách tạo phương thức hoặc hàm tạo có tham số là chính lớp hoặc đối tượng hiện tại.
Mối quan hệ với các mẫu khác
- Adapter Pattern có thể thay đổi giao diện của đối tượng hiện có, trong khi Decorator Pattern có thể cung cấp chức năng mở rộng cho đối tượng mà không thay đổi giao diện của nó. Hơn nữa, Decorator hỗ trợ sự kết hợp đệ quy, trong khi Adapter không thể thực hiện điều này.
- Adapter có thể cung cấp giao diện khác cho đối tượng được đóng gói, trong khi Proxy có thể cung cấp cùng một giao diện cho đối tượng. Decorator có thể cung cấp giao diện tăng cường cho đối tượng.
- Chain of Responsibility Pattern và Decorator Pattern có cấu trúc lớp rất tương tự. Cả hai đều sử dụng kết hợp đệ quy để chuyển tiếp các hoạt động cần thực hiện cho một loạt các đối tượng. Tuy nhiên, có một số điểm khác biệt quan trọng giữa hai mô hình này.
- Chain of Responsibility cho phép các quản lý viên thực hiện công việc mà không phụ thuộc vào nhau và có thể dừng việc chuyển tiếp yêu cầu bất kỳ lúc nào. Trong khi đó, các Decorator cụ thể có thể thêm hành vi bổ sung trước hoặc sau khi gọi phương thức trên đối tượng được đóng gói. Decorator không thể ngăn chặn việc chuyển tiếp yêu cầu.
- Composite Pattern và Decorator Pattern có cấu trúc lớp rất giống nhau, vì cả hai đều dựa trên nguyên tắc kết hợp đệ quy để tổ chức một số lượng vô hạn các đối tượng.
- Decorator tương tự như Composite, nhưng chỉ có một thành phần con. Ngoài ra, có một sự khác biệt rõ ràng: Decorator thêm các trách nhiệm bổ sung cho đối tượng được đóng gói, trong khi Composite chỉ tổng hợp kết quả của các nút con.
- Tuy nhiên, các mô hình cũng có thể cộng tác với nhau: bạn có thể sử dụng Decorator để mở rộng hành vi của một đối tượng cụ thể trong cây Composite.
- Thiết kế sử dụng nhiều Composite Pattern và Decorator Pattern thường có lợi từ việc sử dụng Prototype Pattern. Bạn có thể sao chép cấu trúc phức tạp bằng cách sử dụng mô hình này thay vì xây dựng lại từ đầu.
- Decorator Pattern cho phép bạn thay đổi diện mạo của một đối tượng, trong khi Strategy Pattern cho phép bạn thay đổi bản chất của nó.
- Decorator Pattern và Proxy Pattern có cấu trúc tương tự nhau, nhưng mục đích của chúng rất khác nhau. Cả hai mô hình đều dựa trên nguyên tắc kết hợp, tức là một đối tượng nên chuyển một phần công việc cho một đối tượng khác. Sự khác biệt giữa hai mô hình là Proxy thường tự quản lý vòng đời của đối tượng dịch vụ của mình, trong khi Decorator luôn được kiểm soát bởi mã khách hàng.