GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Design pattern Structuaral Pattern Hướng dẫn Java Design Pattern – Decorator

Hướng dẫn Java Design Pattern – Decorator

Đăng vào 08/11/2018 . Được đăng bởi GP Coder . 44405 Lượt xem . Toàn màn hình

Một trong những khía cạnh quan trọng nhất trong quá trình phát triển một ứng dụng mà các lập trình viên phải đối đầu là sự thay đổi. Khi muốn thêm hoặc loại bỏ một tính năng của một đối tượng, điều đầu tiên chúng ta nghĩ đến là thừa kế (extends). Tuy nhiên, thừa kế không khả thi vì nó là static, chúng ta không thể thêm các lớp con mới vào một chương trình khi nó đã được biên dịch và thực thi. Để giải quyết vấn đề này, chúng ta có thể sử dụng Decorator Pattern được giới thiệu trong phần tiếp theo của bài viết này.

Nội dung

  • 1 Decorator Pattern là gì?
  • 2 Cài đặt Decorator Pattern như thế nào?
  • 3 Lợi ích của Decorator Pattern là gì?
  • 4 Sử dụng Decorator Pattern khi nào?
  • 5 So sánh Decorator và Adapter

Decorator Pattern là gì?

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Decorator pattern là một trong những Pattern thuộc nhóm cấu trúc (Structural Pattern). Nó cho phép người dùng thêm chức năng mới vào đối tượng hiện tại mà không muốn ảnh hưởng đến các đối tượng khác. Kiểu thiết kế này có cấu trúc hoạt động như một lớp bao bọc (wrap) cho lớp hiện có. Mỗi khi cần thêm tính năng mới, đối tượng hiện có được wrap trong một đối tượng mới (decorator class).

Decorator pattern sử dụng composition thay vì inheritance (thừa kế) để mở rộng đối tượng. Decorator pattern còn được gọi là Wrapper hay Smart Proxy.

Cài đặt Decorator Pattern như thế nào?

Decorator pattern hoạt động dựa trên một đối tượng đặc biệt, được gọi là decorator (hay wrapper). Nó có cùng một interface như một đối tượng mà nó cần bao bọc (wrap), vì vậy phía client sẽ không nhận thấy khi bạn đưa cho nó một wrapper thay vì đối tượng gốc.

Tất cả các wrapper có một trường để lưu trữ một giá trị của một đối tượng gốc. Hầu hết các wrapper khởi tạo trường đó với một đối tượng được truyền vào constructor của chúng.

Vậy làm thế nào để có thể thay đổi hành vi của đối tượng? Như đã đề cập, wrapper có cùng interface với các đối tượng đích. Khi bạn gọi một phương thức decorator, nó thực hiện cùng một phương thức trong một đối tượng được wrap và sau đó thêm một cái gì đó (tính năng mới) vào kết quả, công việc này tùy thuộc vào logic nghiệp vụ.

Các thành phần trong mẫu thiết kế Decorator:

  • Component: là một interface quy định các method chung cần phải có cho tất cả các thành phần tham gia vào mẫu này.
  • ConcreteComponent : là lớp hiện thực (implements) các phương thức của Component.
  • Decorator : là một abstract class dùng để duy trì một tham chiếu của đối tượng Component và đồng thời cài đặt các phương thức của Component  interface.
  • ConcreteDecorator : là lớp hiện thực (implements) các phương thức của Decorator, nó cài đặt thêm các tính năng mới cho Component.
  • Client : đối tượng sử dụng Component.

Ví dụ:

Để đơn giản hơn, chúng ta xem ví dụ về một hệ thống quản lý dự án, nơi nhân viên đang làm việc với các vai trò khác nhau, chẳng hạn như thành viên nhóm (team member), trưởng nhóm (team lead) và người quản lý (manager). Một thành viên trong nhóm chịu trách nhiệm thực hiện các nhiệm vụ được giao và phối hợp với các thành viên khác để hoàn thành nhiệm vụ nhóm. Mặt khác, một trưởng nhóm phải quản lý và cộng tác với các thành viên trong nhóm của mình và lập kế hoạch nhiệm vụ của họ. Tương tự như vậy, một người quản lý có thêm một số trách nhiệm đối với một trưởng nhóm như quản lý yêu cầu dự án, tiến độ, phân công công việc.

Sau đây là các thành phần tham gia vào hệ thống và hành vi của chúng:

  • Employee: thực hiện công việc (doTask), tham gia vào dự án (join), rời khỏi dự án (terminate).
  • Team member: báo cáo task được giao (report task), cộng tác với các thành viên khác (coordinate with others).
  • Team lead: lên kế hoạch (planning), hỗ trợ các thành viên phát triển (motivate), theo dõi chất lượng công việc và thời gian (monitor).
  • Manager: tạo các yêu cầu dự án (create requirement), giao nhiệm vụ cho thành viên (assign task), quản lý tiến độ dự án (progress management).

Với cách làm thông thường, chúng ta có sơ đồ như sau:

Bất cứ khi nào một thành viên trong nhóm trở thành một Team Lead, chúng ta phải tạo một đối tượng mới của Team Lead và đối tượng trước đó tham chiếu vào nhân viên đó (Team Member trong nhóm) có thể bị hủy hoặc lưu trữ. Đó không phải là cách tiếp cận được khuyến nghị khi nhân viên vẫn là một phần của tổ chức của bạn. Tương tự như trường hợp với Manager, khi một nhân viên trở thành người quản lý từ một Team Lead / Team Member.

Một trường hợp khác là khi một nhân viên có thể thực hiện trách nhiệm của một Team Member trong nhóm cũng như trách nhiệm của Team Lead hoặc một Manager. Trong trường hợp đó, bạn cần tạo hai đối tượng cho cùng một nhân viên là hoàn toàn sai.

Trong các kịch bản này, một Team Member/ Team Lead có thể có thêm trách nhiệm trong lúc thực hiện (run-time). Và trách nhiệm của họ có thể được chỉ định / thu hồi trong lúc run-time.

Hãy xem sơ đồ bên dưới để thấy được cách chúng ta áp dụng Decorator Pattern như thế nào trong trường hợp này.

Như bạn thấy, với Decorator hệ thống của chúng ta linh hoạt hơn rất nhiều. Chúng ta có thể dễ dàng gán một nhân viên sang vai trò TeamMember, TeamLeader, Manager.

EmployeeComponent.java


package com.gpcoder.patterns.structural.decorator;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public interface EmployeeComponent {

	String getName();

	void doTask();

	void join(Date joinDate);

	void terminate(Date terminateDate);
	
	default String formatDate(Date theDate) {
		DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
		return sdf.format(theDate);
	}
	
	default void showBasicInformation() {
		System.out.println("-------");
		System.out.println("The basic information of " + getName());
		
		join(Calendar.getInstance().getTime());
		
		Calendar terminateDate = Calendar.getInstance();
		terminateDate.add(Calendar.MONTH, 6);
		terminate(terminateDate.getTime());
	}
}

EmployeeConcreteComponent.java


package com.gpcoder.patterns.structural.decorator;

import java.util.Date;

public class EmployeeConcreteComponent implements EmployeeComponent {
	
	private String name;
	
	public EmployeeConcreteComponent (String name) {
		this.name = name;
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public void join(Date joinDate) {
		System.out.println(this.getName() + " joined on " + formatDate(joinDate));
	}

	@Override
	public void terminate(Date terminateDate) {
		System.out.println(this.getName() + " terminated on " + formatDate(terminateDate));		
	}

	@Override
	public void doTask() {
		// Unassigned task
	}
}

EmployeeDecorator.java


package com.gpcoder.patterns.structural.decorator;

import java.util.Date;

public abstract class EmployeeDecorator implements EmployeeComponent {

	protected EmployeeComponent employee;

	protected EmployeeDecorator(EmployeeComponent employee) {
		this.employee = employee;
	}

	@Override
	public String getName() {
		return employee.getName();
	}

	@Override
	public void join(Date joinDate) {
		employee.join(joinDate);
	}

	@Override
	public void terminate(Date terminateDate) {
		employee.terminate(terminateDate);
	}
}

Manager.java


package com.gpcoder.patterns.structural.decorator;

public class Manager extends EmployeeDecorator {

	protected Manager(EmployeeComponent employee) {
		super(employee);
	}

	public void createRequirement() {
		System.out.println(this.employee.getName() + " is create requirements.");
	}

	public void assignTask() {
		System.out.println(this.employee.getName() + " is assigning tasks.");
	}

	public void manageProgress() {
		System.out.println(this.employee.getName() + " is managing the progress.");
	}

	@Override
	public void doTask() {
		employee.doTask();
		createRequirement();
		assignTask();
		manageProgress();
	}
}

TeamLeader.java


package com.gpcoder.patterns.structural.decorator;

public class TeamLeader extends EmployeeDecorator {

	protected TeamLeader(EmployeeComponent employee) {
		super(employee);
	}

	public void planing() {
		System.out.println(this.employee.getName() + " is planing.");
	}

	public void motivate() {
		System.out.println(this.employee.getName() + " is motivating his members.");
	}

	public void monitor() {
		System.out.println(this.employee.getName() + " is monitoring his members.");
	}

	@Override
	public void doTask() {
		employee.doTask();
		planing();
		motivate();
		monitor();
	}
}

TeamMember.java


package com.gpcoder.patterns.structural.decorator;

public class TeamMember extends EmployeeDecorator {

	protected TeamMember(EmployeeComponent employee) {
		super(employee);
	}

	public void reportTask() {
		System.out.println(this.employee.getName() + " is reporting his assigned tasks.");
	}

	public void coordinateWithOthers() {
		System.out.println(this.employee.getName() + " is coordinating with other members of his team.");
	}

	@Override
	public void doTask() {
		employee.doTask();
		reportTask();
		coordinateWithOthers();
	}
}

Client.java


package com.gpcoder.patterns.structural.decorator;

public class Client {

	public static void main(String[] args) {
		System.out.println("NORMAL EMPLOYEE: ");
		EmployeeComponent employee = new EmployeeConcreteComponent("GPCoder");
		employee.showBasicInformation();
		employee.doTask();

		System.out.println("\nTEAM LEADER: ");
		EmployeeComponent teamLeader = new TeamLeader(employee);
		teamLeader.showBasicInformation();
		teamLeader.doTask();

		System.out.println("\nMANAGER: ");
		EmployeeComponent manager = new Manager(employee);
		manager.showBasicInformation();
		manager.doTask();

		System.out.println("\nTEAM LEADER AND MANAGER: ");
		EmployeeComponent teamLeaderAndManager = new Manager(teamLeader);
		teamLeaderAndManager.showBasicInformation();
		teamLeaderAndManager.doTask();
	}
}

Output của chương trình:


NORMAL EMPLOYEE: 
-------
The basic information of GPCoder
GPCoder joined on 04/11/2018
GPCoder terminated on 04/05/2019

TEAM LEADER: 
-------
The basic information of GPCoder
GPCoder joined on 04/11/2018
GPCoder terminated on 04/05/2019
GPCoder is planing.
GPCoder is motivating his members.
GPCoder is monitoring his members.

MANAGER: 
-------
The basic information of GPCoder
GPCoder joined on 04/11/2018
GPCoder terminated on 04/05/2019
GPCoder is create requirements.
GPCoder is assigning tasks.
GPCoder is managing the progress.

TEAM LEADER AND MANAGER: 
-------
The basic information of GPCoder
GPCoder joined on 04/11/2018
GPCoder terminated on 04/05/2019
GPCoder is planing.
GPCoder is motivating his members.
GPCoder is monitoring his members.
GPCoder is create requirements.
GPCoder is assigning tasks.
GPCoder is managing the progress.

Lợi ích của Decorator Pattern là gì?

  • Tăng cường khả năng mở rộng của đối tượng, bởi vì những thay đổi được thực hiện bằng cách implement trên các lớp mới.
  • Client sẽ không nhận thấy sự khác biệt khi bạn đưa cho nó một wrapper thay vì đối tượng gốc.
  • Một đối tượng có thể được bao bọc bởi nhiều wrapper cùng một lúc.
  • Cho phép thêm hoặc xóa tính năng của một đối tượng lúc thực thi (run-time).

Sử dụng Decorator Pattern khi nào?

  • Khi muốn thêm tính năng mới cho các đối tượng mà không ảnh hưởng đến các đối tượng này.
  • Khi không thể mở rộng một đối tượng bằng cách thừa kế (inheritance). Chẳng hạn, một class sử dụng từ khóa final, muốn mở rộng class này chỉ còn cách duy nhất là sử dụng decorator.
  • Trong một số nhiều trường hợp mà việc sử dụng kế thừa sẽ mất nhiều công sức trong việc viết code. Ví dụ trên là một trong những trường hợp như vậy.

So sánh Decorator và Adapter

Giống nhau:

  • Cả hai đều là structural pattern như định nghĩa của GOF.
  • Cả hai sử dụng cách composition để cài đặt.

Khác nhau:

  • Decorator cho phép thêm một tính năng mới vào một object nhưng không được phép sử dụng thừa kế. Nó cho phép thay đổi lúc thực thi (run-time). Adapter được sử dụng khi bạn có một interface, và bạn muốn ánh xạ interface đó đến một đối tượng khác có vai trò chức năng tương tự, nhưng là một interface khác.
  • Decorator có xu hướng hoạt động trên một đối tượng. Adapter có xu hướng hoạt động trên nhiều đối tượng (có thể wrap nhiều interface).

Tài liệu tham khảo:

  • https://refactoring.guru/design-patterns/decorator
  • https://sourcemaking.com/design_patterns/decorator
  • https://www.javatpoint.com/decorator-pattern
  • https://www.tutorialspoint.com/design_pattern/decorator_pattern.htm
  • https://techblog.vn/design-pattern-decorator-pattern
4.7
20
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Design pattern, Structuaral Pattern Được gắn thẻ: Design pattern, Structuaral Pattern

Hướng dẫn Java Design Pattern – Composite
Hướng dẫn Java Design Pattern – Facade

Có thể bạn muốn xem:

  • Hướng dẫn Java Design Pattern – Factory Method (12/09/2018)
  • Hướng dẫn Java Design Pattern – Memento (25/12/2018)
  • Hướng dẫn Java Design Pattern – Flyweight (22/11/2018)
  • Hướng dẫn Java Design Pattern – Abstract Factory (15/09/2018)
  • Hướng dẫn Java Design Pattern – Proxy (30/11/2018)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (98059 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97700 lượt xem)
  • Giới thiệu Design Patterns (87764 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (86434 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83839 lượt xem)

Nội dung bài viết

  • 1 Decorator Pattern là gì?
  • 2 Cài đặt Decorator Pattern như thế nào?
  • 3 Lợi ích của Decorator Pattern là gì?
  • 4 Sử dụng Decorator Pattern khi nào?
  • 5 So sánh Decorator và Adapter

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link