Giới thiệu Decorator pattern
1. Vấn đề
Khi phát triển phần mềm việc thêm hành vi (method) cho object thường xảy ra. Để giải quyết vấn đề này ta có thể bổ sung hành vi cho object bằng cách thêm method vào class hoặc tạo 1 sub class có thêm method. Bổ sung method cho class thì chúng ta cần kiểm thử lại code cho cả class. Hơn nữa cách kế thừa cũng có nhược điểm là nó chỉ thêm hành vi tĩnh chứ không giúp object có để sử dụng hành vi mới trong quá trình chạy (dynamic).
2. Ví dụ
Để hiểu rõ hơn vấn đề chúng ta xem ví dụ về gửi thông báo đến user bên dưới.
Có 1 thư viện thông báo giúp gửi message tới user gọi là Notification Library và 1 Application sử dụng thư viện trên để gửi thông báo đến user trên máy tính.
Sau một thời gian sử dụng, user mong muốn ngoài việc gửi thông báo trên máy tính còn muốn nhận thông báo qua SMS, Facebook, Slack. Để bổ sung thêm 3 tính năng chúng ta tạo 3 subclass kế thừa từ thư viện Notifier như hình bên dưới.
Nhưng nếu làm như này chúng ta chỉ có thể notify chỉ có thể notify đến 1 trong 3 kiểu là SMS, Facebook, Slack. Nếu muốn thông báo đến nhóm 1 vài kênh cần thông báo như <SMS, slack> , <SMS,facebook> , <slack, facebook> …
Bổ sung bằng cách mặc dù không phải sửa code nhưng sẽ bị lặp code và cây kế thừa trở nên phức tạp.
Một cách khác nữa là chúng ta thêm 3 method để bổ sung 3 tính năng: sendSMS, sendFacebook, sendSlack. Nếu thực hiện theo cách này thì sẽ đáp ứng được yêu cầu nhưng mà phải đi test lại toàn bộ các code liên quan Notifier.
3. Ý tưởng và giải pháp
Decorator là design pattern sẽ giúp chúng ta xử lý vấn đề trên. Ý tưởng của Decorator là thêm các hành vi mới cho object bằng cách đặt đối tượng bên trong các đối tượng cụ thể đã bao gồm các hành vi mới.
Thay vì sử dụng kế thừa để thêm hành vi vào object thì ta tạo một object mới chứa các hành vi mới và đặt đối object cần thêm hành vào object mới. class BaseDecorator sẽ class mới, nó dùng để chứa class cũ Notifier. Class SMSDecorator, FacebookDecorator và SlackDecorator là 3 class kế thừa từ BaseDecorator, ở method send mỗi class sẽ có thể gửi thông báo lên kênh của mình.
Áp dụng Decorator pattern này giúp ta vừa bổ sung thêm tính năng và cũng không cần sửa code( dẫn đến không cần test lại source code cũ). Class Application cũng không thay đổi, chúng ta chỉ muốn gửi thông báo trên kênh nào thì chỉ cần sử dụng thêm cờ kiểm tra ở phía client.
4. Cấu trúc của Decorator
- Component: interface chung cho cả đối tượng mới sẽ chứa đối tượng cũ và đối tượng cũ được chứa.
- Concrete Component: là class của các đối tượng cũ cần được chứa.
- Base Decorator: Cung cấp 1 tham chiếu đến Concrete Decorator và implement các method chung của Concrete Decorator.
- Concrete Decorator: là các class bổ sung thêm hành vi mới cho, nó sẽ override lại các method của Base Decorator nếu cần.
5. Ứng dụng
- Mẫu Decorator được ứng dụng trong thực thế khi cần bổ sung hành vi mới trong quá trình runtime mà không phá vỡ cấu trúc code hiện tại.
- Khi việc bổ sung hành vi bằng kế thừa kiến chương trình phức tạp, khó phát triển.
Tài liệu tham khảo:
- https://refactoring.guru/design-patterns/decorator
- https://gpcoder.com/4574-huong-dan-java-design-pattern-decorator/