Trước tiên, chúng ta hãy cùng cafedev giới thiệu mọi thứ về Composite Design Pattern và phần code ví dụ chi tiết nhằm giúp ace dễ hiểu khi áp dụng trên các ngôn ngữ khác nhau. Ace có thể tham khảo thêm các bài khác tại series Design Pattern tại đây.

Mẫu tổng hợp(Composite pattern) là một mẫu thiết kế phân vùng và mô tả một nhóm đối tượng được xử lý giống như một thể hiện đơn lẻ của cùng một loại đối tượng. Mục đích của kết hợp là “biên soạn” các đối tượng thành cấu trúc cây để thể hiện phân cấp một phần toàn bộ. Nó cho phép bạn có một cấu trúc cây và yêu cầu mỗi nút trong cấu trúc cây thực hiện một nhiệm vụ.

  • Theo mô tả của Gof, “Soạn các đối tượng thành cấu trúc cây để thể hiện các cấu trúc phân cấp một phần toàn bộ . Composite cho phép khách hàng xử lý các đối tượng riêng lẻ và bố cục của các đối tượng một cách đồng nhất ”.
  • Khi xử lý dữ liệu có cấu trúc cây, các lập trình viên thường phải phân biệt giữa nút lá và nút nhánh. Điều này làm cho code phức tạp hơn và do đó, dễ xảy ra lỗi. Giải pháp là một giao diện cho phép xử lý các đối tượng phức tạp và nguyên thủy một cách đồng nhất.
  • Trong lập trình hướng đối tượng, composite là một đối tượng được thiết kế như một thành phần của một hoặc nhiều đối tượng giống nhau, tất cả đều có chức năng tương tự. Đây được gọi là mối quan hệ “có-một” giữa các đối tượng.
  • Khái niệm chính là bạn có thể thao tác một thể hiện duy nhất của đối tượng giống như thao tác với một nhóm chúng. Các phép toán bạn có thể thực hiện trên tất cả các đối tượng tổng hợp thường có mối quan hệ mẫu số chung nhỏ nhất.
    Mô hình tổng hợp có bốn người tham gia:
    1. Thành phần(Component) – Thành phần khai báo giao diện cho các đối tượng trong thành phần và để truy cập và quản lý các thành phần con của nó. Nó cũng thực hiện hành vi mặc định cho giao diện chung cho tất cả các lớp nếu thích hợp.
    2. Lá(Leaf) – Lá xác định hành vi cho các đối tượng nguyên thủy trong thành phần. Nó đại diện cho các đối tượng lá trong bố cục.
    3. Composite – Composite lưu trữ các thành phần con và thực hiện các hoạt động liên quan đến con trong giao diện thành phần.
    4. Máy khách(Client) – Máy khách thao tác các đối tượng trong thành phần thông qua giao diện(interface) thành phần.
  • Máy khách sử dụng giao diện(interface) lớp thành phần để tương tác với các đối tượng trong cấu trúc thành phần. Nếu người nhận là một lá thì yêu cầu được xử lý trực tiếp. Nếu người nhận là một tổ hợp, thì nó thường chuyển tiếp yêu cầu đến các thành phần con của nó, có thể thực hiện các hoạt động bổ sung trước và sau khi chuyển tiếp.
    Ví dụ về cuộc sống thực
    Trong một tổ chức, nó có những người quản lý chung và dưới những người quản lý chung, có thể có những người quản lý và dưới những người quản lý có thể có những người phát triển. Bây giờ bạn có thể thiết lập một cấu trúc cây và yêu cầu mỗi nút thực hiện thao tác chung như getSalary().
    Mẫu thiết kế tổng hợp xử lý mỗi nút theo hai cách:
    1) Kết hợp(Composite)Composite có nghĩa là nó có thể có các đối tượng khác bên dưới nó.
    2) lá(leaf) – lá có nghĩa là nó không có đối tượng bên dưới nó.

1. Cấu trúc cây:


Hình trên cho thấy một cấu trúc đối tượng Composite điển hình. Như bạn thấy, có thể có nhiều con với một phụ huynh duy nhất tức là Composite, nhưng mỗi trẻ chỉ có một phụ huynh.
Interface Component.java

public interface Employee 
{ 
    public void showEmployeeDetails(); 
} 

Leaf.java

public class Developer implements Employee 
{ 
    private String name; 
    private long empId; 
    private String position; 
  
    public Developer(long empId, String name, String position) 
    { 
        this.empId = empId; 
        this.name = name; 
                this.position = position; 
    } 
      
    @Override
    public void showEmployeeDetails()  
    { 
        System.out.println(empId+" " +name+); 
    } 
} 

Leaf.java

public class Manager implements Employee 
{ 
    private String name; 
    private long empId; 
        private String position; 
  
    public Manager(long empId, String name, String position) 
    { 
        this.empId = empId; 
        this.name = name; 
                this.position = position; 
    } 
      
    @Override
    public void showEmployeeDetails()  
    { 
        System.out.println(empId+" " +name); 
    } 
} 

Composite.java

import java.util.ArrayList; 
import java.util.List; 
  
public class CompanyDirectory implements Employee 
{ 
    private List<Employee> employeeList = new ArrayList<Employee>(); 
       
    @Override
    public void showEmployeeDetails()  
    { 
        for(Employee emp:employeeList) 
        { 
            emp.showEmployeeDetails(); 
        } 
    } 
       
    public void addEmployee(Employee emp) 
    { 
        employeeList.add(emp); 
    } 
       
    public void removeEmployee(Employee emp) 
    { 
        employeeList.remove(emp); 
    } 
} 

Client.java

public class Company 
{ 
    public static void main (String[] args) 
    { 
        Developer dev1 = new Developer(100, "Lokesh Sharma", "Pro Developer"); 
        Developer dev2 = new Developer(101, "Vinay Sharma", "Developer"); 
        CompanyDirectory engDirectory = new CompanyDirectory(); 
        engDirectory.addEmployee(dev1); 
        engDirectory.addEmployee(dev2); 
          
        Manager man1 = new Manager(200, "Kushagra Garg", "SEO Manager"); 
        Manager man2 = new Manager(201, "Vikram Sharma ", "Kushagra's Manager"); 
          
        CompanyDirectory accDirectory = new CompanyDirectory(); 
        accDirectory.addEmployee(man1); 
        accDirectory.addEmployee(man2); 
      
        CompanyDirectory directory = new CompanyDirectory(); 
        directory.addEmployee(engDirectory); 
        directory.addEmployee(accDirectory); 
        directory.showEmployeeDetails(); 
    } 
} 

Sơ đồ UML cho Mẫu thiết kế tổng hợp:


Code chạy đầy đủ cho ví dụ trên:

// A Java program to demonstrate working of 
// Composite Design Pattern with example  
// of a company with different 
//  employee details 
  
import java.util.ArrayList; 
import java.util.List; 
  
  
// A common interface for all employee 
interface Employee 
{ 
    public void showEmployeeDetails(); 
} 
  
class Developer implements Employee 
{ 
    private String name; 
    private long empId; 
    private String position; 
      
    public Developer(long empId, String name, String position) 
    { 
        // Assign the Employee id, 
        // name and the position 
        this.empId = empId; 
        this.name = name; 
        this.position = position; 
    } 
      
    @Override
    public void showEmployeeDetails()  
    { 
        System.out.println(empId+" " +name+ " " + position ); 
    } 
} 
  
class Manager implements Employee 
{ 
    private String name; 
    private long empId; 
    private String position; 
   
    public Manager(long empId, String name, String position) 
    { 
        this.empId = empId; 
        this.name = name; 
        this.position = position; 
    } 
       
    @Override
    public void showEmployeeDetails()  
    { 
        System.out.println(empId+" " +name+ " " + position ); 
    } 
} 
  
  
// Class used to get Employee List 
// and do the opertions like  
// add or remove Employee 
  
class CompanyDirectory implements Employee 
{ 
    private List<Employee> employeeList = new ArrayList<Employee>(); 
        
    @Override
    public void showEmployeeDetails()  
    { 
        for(Employee emp:employeeList) 
        { 
            emp.showEmployeeDetails(); 
        } 
    } 
        
    public void addEmployee(Employee emp) 
    { 
        employeeList.add(emp); 
    } 
        
    public void removeEmployee(Employee emp) 
    { 
        employeeList.remove(emp); 
    } 
} 
  
// Driver class 
public class Company 
{ 
    public static void main (String[] args) 
    { 
        Developer dev1 = new Developer(100, "Lokesh Sharma", "Pro Developer"); 
        Developer dev2 = new Developer(101, "Vinay Sharma", "Developer"); 
        CompanyDirectory engDirectory = new CompanyDirectory(); 
        engDirectory.addEmployee(dev1); 
        engDirectory.addEmployee(dev2); 
           
        Manager man1 = new Manager(200, "Kushagra Garg", "SEO Manager"); 
        Manager man2 = new Manager(201, "Vikram Sharma ", "Kushagra's Manager"); 
           
        CompanyDirectory accDirectory = new CompanyDirectory(); 
        accDirectory.addEmployee(man1); 
        accDirectory.addEmployee(man2); 
       
        CompanyDirectory directory = new CompanyDirectory(); 
        directory.addEmployee(engDirectory); 
        directory.addEmployee(accDirectory); 
        directory.showEmployeeDetails(); 
    } 
} 

Đầu ra:

100 Lokesh Sharma Pro Developer
101 Vinay Sharma Developer
200 Kushagra Garg SEO Manager
201 Vikram Sharma  Kushagra's Manager

2. Khi nào sử dụng Composite Design Pattern

  • Composite Pattern nên được sử dụng khi khách hàng cần bỏ qua sự khác biệt giữa bố cục của các đối tượng và các đối tượng riêng lẻ. Nếu các lập trình viên nhận thấy rằng họ đang sử dụng nhiều đối tượng theo cùng một cách và thường có mã gần giống nhau để xử lý từng đối tượng trong số chúng, thì composite là một lựa chọn tốt, trong trường hợp này sẽ ít phức tạp hơn nếu coi các vật liệu gốc và vật liệu tổng hợp là đồng nhất.
    1. Số lượng đối tượng ít hơn làm giảm mức sử dụng bộ nhớ và giúp chúng ta tránh xa các lỗi liên quan đến bộ nhớ như java.lang.OutOfMemoryError .
    2. Mặc dù việc tạo một đối tượng trong Java thực sự rất nhanh, chúng ta vẫn có thể giảm thời gian thực thi chương trình của mình bằng cách chia sẻ các đối tượng.

3. Khi nào không sử dụng Composite Design Pattern?

  1. Composite Design Pattern làm cho việc hạn chế loại thành phần của hỗn hợp khó hơn. Vì vậy, nó không nên được sử dụng khi bạn không muốn biểu diễn một hệ thống phân cấp đầy đủ hoặc một phần của các đối tượng.
  2. Composite Design Pattern có thể làm cho thiết kế trở nên quá chung chung. Việc hạn chế các thành phần của composite sẽ khó hơn. Đôi khi bạn muốn một tổng hợp chỉ có một số thành phần nhất định. Với Composite, bạn không thể dựa vào type system để thực thi những ràng buộc đó cho bạn. Thay vào đó, bạn sẽ phải sử dụng kiểm tra thời gian chạy.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!