Hướng dẫn Java Design Pattern – Decorator – GP Coder (Lập trình Java)

Related Articles

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.

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 giải trí dựa trên một đối tượng người tiêu dùng đặc biệt quan trọng, được gọi là decorator ( hay wrapper ). Nó có cùng một interface như một đối tượng người tiêu dùng mà nó cần phủ bọc ( wrap ), vì thế phía client sẽ không nhận thấy khi bạn đưa cho nó một wrapper thay vì đối tượng người tiêu dùng gốc .Tất cả những wrapper có một trường để tàng trữ một giá trị của một đối tượng người tiêu dùng gốc. Hầu hết những wrapper khởi tạo trường đó với một đối tượng người tiêu dùng được truyền vào constructor của chúng .Vậy làm thế nào để hoàn toàn có thể đổi khác hành vi của đối tượng người dùng ? Như đã đề cập, wrapper có cùng interface với những đối tượng người tiêu dùng đích. Khi bạn gọi một phương pháp decorator, nó thực thi cùng một phương pháp trong một đối tượng người dùng được wrap và sau đó thêm một cái gì đó ( tính năng mới ) vào hiệu quả, việc làm này tùy thuộc vào logic nhiệm vụ .

Các thành phần trong mẫu phong cách 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 thuần hơn, tất cả chúng ta xem ví dụ về một mạng lưới hệ thống quản trị dự án Bất Động Sản, nơi nhân viên cấp dưới đang thao tác với những vai trò khác nhau, ví dụ điển hình như thành viên nhóm ( team thành viên ), trưởng nhóm ( team lead ) và người quản trị ( manager ). Một thành viên trong nhóm chịu nghĩa vụ và trách nhiệm thực thi những trách nhiệm được giao và phối hợp với những thành viên khác để hoàn thành xong trách nhiệm nhóm. Mặt khác, một trưởng nhóm phải quản trị và cộng tác với những thành viên trong nhóm của mình và lập kế hoạch trách nhiệm của họ. Tương tự như vậy, một người quản trị có thêm 1 số ít nghĩa vụ và trách nhiệm so với một trưởng nhóm như quản trị nhu yếu dự án Bất Động Sản, quy trình tiến độ, phân công việc làm .Sau đây là những thành phần tham gia vào mạng lưới 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ì, tất cả 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, tất cả chúng ta phải tạo một đối tượng người dùng mới của Team Lead và đối tượng người dùng trước đó tham chiếu vào nhân viên cấp dưới đó ( Team Member trong nhóm ) hoàn toàn có thể bị hủy hoặc tàng trữ. Đó không phải là cách tiếp cận được khuyến nghị khi nhân viên cấp dưới vẫn là một phần của tổ chức triển khai của bạn. Tương tự như trường hợp với Manager, khi một nhân viên cấp dưới trở thành người quản trị từ một Team Lead / Team Member .Một trường hợp khác là khi một nhân viên cấp dưới hoàn toàn có thể triển khai nghĩa vụ và trách nhiệm của một Team Member trong nhóm cũng như nghĩa vụ và 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 người dùng cho cùng một nhân viên cấp dưới là trọn vẹn sai .Trong những ngữ cảnh này, một Team Member / Team Lead hoàn toàn có thể có thêm nghĩa vụ và trách nhiệm trong lúc triển khai ( run-time ). Và nghĩa vụ và trách nhiệm của họ hoàn toàn có thể được chỉ định / tịch thu trong lúc run-time .Hãy xem sơ đồ bên dưới để thấy được cách tất cả chúng ta vận dụng Decorator Pattern như thế nào trong trường hợp này .

Như bạn thấy, với Decorator mạng lưới hệ thống của tất cả chúng ta linh động hơn rất nhiều. Chúng ta hoàn toàn có thể thuận tiện gán một nhân viên cấp dưới 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:

4.9

Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé!

Shares

Bình luận

phản hồi

More on this topic

Comments

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Advertismentspot_img

Popular stories