且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在Java中实现复合模式?

更新时间:2023-11-24 12:56:22

是的,如果要映射树结构,复合模式确实是正确的选择。参考您的示例,复合设计模式意味着您的类 Employee 充当节点,类 ProjectManager 行为作为分支,类 Developer 充当叶子。在这种情况下,复合图案的主要优点是它可以均匀地处理你作品的对象。因此,您可以使用此特定GoF设计模式表示实例的整个层次结构。

Yes, the composite pattern is indeed the right choice if you want to map tree structures. With reference to your example, the composite design pattern implies that your class Employee acts as a node, the class ProjectManager acts as a branch and the class Developer acts as a leaf. Within this context, the main advantage of the composite pattern is that it treats objects of your compositions uniformly. As a result, you can represent entire hierarchies of instances with this particular GoF design pattern.

您需要以下参与者:


  1. abstract Employee 必须声明组合和实现的接口在某种程度上的常见行为。

  2. ProjectManager 类扩展摘要 class Employee 并实现一种行为来处理 Employee children,id在您的情况下 ProjectManager 开发人员实例。

  3. 开发人员还扩展抽象员工并表示没有的叶子任何孩子。

  1. The abstract class Employee must declare the interface of the composition and implements a common behaviour to a certain degree.
  2. The ProjectManager class extends the abstract class Employee and implements a behaviour to treat Employee children, i.d. in your case ProjectManager or Developer instances.
  3. The Developer also extends the abstract class Employee and represents a leaf which does not have any children.

我使用你的示例代码来演示复合模式。请注意,它可能与您期望的结果不同,但您可以将其作为参考。

I used your example code to demonstrate the composite pattern. Please note that it may vary from your desired outcome, but you can take it as a reference.

Employee.java (节点):

package me.eckhart;

import java.util.List;

public abstract class Employee {

    private String name = null;
    public static final String OPERATION_NOT_SUPPORTED = "Operation not supported.";

    public String getName() {
        return name;
    }

    public Employee setName(String name) {
        if (name == null) throw new IllegalArgumentException("Argument 'name' is null.");
        this.name = name;
        return this;
    }

    public Employee addEmployee(Employee employee) {
        throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
    }

    public List<Employee> getEmployees() {
        throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
    }

    public Employee setEmployees(List<Employee> employees) {
        throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
    }

    public Employee setLanguagesList(List<ProgrammingLanguages> languagesList) {
        throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
    }

    public Employee addProgrammingLanguage(ProgrammingLanguages language) {
        throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
    }

    public List<ProgrammingLanguages> getLanguagesList() {
        throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED);
    }

    /* Composite operations. */

    public abstract int getWorkload(ProgrammingLanguages language);

    public abstract Employee setWorkload(int workload);

}

ProjectManager.java (分支):

package me.eckhart;

import java.util.ArrayList;
import java.util.List;

public class ProjectManager extends Employee {

    private List<Employee> employeeList = null;

    public ProjectManager() {
        this.employeeList = new ArrayList<>();
    }

    @Override
    public Employee addEmployee(Employee employee) {
        if (employee == null) throw new IllegalArgumentException("Argument 'employee' is null.");
        this.employeeList.add(employee);
        return this;
    }

    @Override
    public List<Employee> getEmployees() {
        return this.employeeList;
    }

    @Override
    public Employee setEmployees(List<Employee> employeeList) {
        if (employeeList == null) throw new IllegalArgumentException("Argument 'employeeList' is null.");
        this.employeeList = employeeList;
        return this;
    }

    /* Composite operations. */

    public int getWorkload(ProgrammingLanguages language) {
        int workload = 0;
        for (Employee employee : employeeList) {
            workload += employee.getWorkload(language);
        }
        return workload;
    }

    public Employee setWorkload(int workload) {
        throw new UnsupportedOperationException(Employee.OPERATION_NOT_SUPPORTED);
    }

}

开发人员.java (叶子):

package me.eckhart;

import java.util.ArrayList;
import java.util.List;

public class Developer extends Employee {

    private List<ProgrammingLanguages> languagesList = null;

    private int workload = 0;

    public Developer() {
        this.languagesList = new ArrayList<>();
    }

    @Override
    public Employee setLanguagesList(List<ProgrammingLanguages> languagesList) {
        this.languagesList = languagesList;
        return this;
    }

    @Override
    public Employee addProgrammingLanguage(ProgrammingLanguages language) {
        this.languagesList.add(language);
        return this;
    }

    @Override
    public List<ProgrammingLanguages> getLanguagesList() {
        return this.languagesList;
    }

    /* Composite operations. */

    public Employee setWorkload(int workload) {
        if (workload < -1) throw new IllegalArgumentException("Workload cannot be negative.");
        this.workload = workload;
        return this;
    }

    public int getWorkload(ProgrammingLanguages language) {
        if (this.languagesList.contains(language)) return workload;
        return 0;
    }

}

ProgrammingLanguages .java (枚举):

package me.eckhart;

public enum ProgrammingLanguages {
    JAVA,
    JAVASCRIPT,
    C,
    PHP,
    SWIFT,
    PYTHON
}

我创建了一个单元测试来演示如何访问一种特定编程语言的工作负载。

I created a unit test to demonstrate how you can access the workload for one particular programming language.

EmployeeTest.java (JUnit 4.11):

EmployeeTest.java (JUnit 4.11):

package me.eckhart;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class EmployeeTest {

    protected Employee projectManagerIt;

    @Before
    public void setUp() throws Exception {

        Employee webDevSr = new Developer();
        webDevSr.setName("Jane").addProgrammingLanguage(ProgrammingLanguages.JAVASCRIPT).addProgrammingLanguage(ProgrammingLanguages.PYTHON).setWorkload(10);
        Employee webDevJr = new Developer();
        webDevJr.setName("Alex").addProgrammingLanguage(ProgrammingLanguages.PHP).setWorkload(15);
        Employee projectManagerWebDev = new ProjectManager();
        projectManagerWebDev.setName("James").addEmployee(webDevSr).addEmployee(webDevJr);

        Employee softwareDevSr = new Developer();
        softwareDevSr.setName("Martin").addProgrammingLanguage(ProgrammingLanguages.C).addProgrammingLanguage(ProgrammingLanguages.JAVA).setWorkload(35);
        Employee softwareDevJr = new Developer();
        softwareDevJr.setName("John").addProgrammingLanguage(ProgrammingLanguages.JAVA).setWorkload(30);
        Employee projectManagerBackend = new ProjectManager();
        projectManagerBackend.setName("Tom").addEmployee(softwareDevSr).addEmployee(softwareDevJr);

        Employee freelanceSoftwareDev = new Developer();
        freelanceSoftwareDev.setName("Marco").addProgrammingLanguage(ProgrammingLanguages.JAVA).addProgrammingLanguage(ProgrammingLanguages.PYTHON).addProgrammingLanguage(ProgrammingLanguages.C).setWorkload(25);
        Employee freelanceWebDev = new Developer();
        freelanceWebDev.setName("Claudio").addProgrammingLanguage(ProgrammingLanguages.SWIFT).addProgrammingLanguage(ProgrammingLanguages.JAVASCRIPT).addProgrammingLanguage(ProgrammingLanguages.PHP).setWorkload(10);
        Employee freelanceProjectManager = new ProjectManager();
        freelanceProjectManager.setName("Angie").addEmployee(freelanceSoftwareDev).addEmployee(freelanceWebDev);

        projectManagerIt = new ProjectManager();
        projectManagerIt.setName("Peter").addEmployee(projectManagerWebDev).addEmployee(projectManagerBackend).addEmployee(freelanceProjectManager);

    }

    @Test
    public void testComposite() throws Exception {

        Assert.assertEquals(90, projectManagerIt.getWorkload(ProgrammingLanguages.JAVA));
        Assert.assertEquals(20, projectManagerIt.getWorkload(ProgrammingLanguages.JAVASCRIPT));
        Assert.assertEquals(60, projectManagerIt.getWorkload(ProgrammingLanguages.C));
        Assert.assertEquals(25, projectManagerIt.getWorkload(ProgrammingLanguages.PHP));
        Assert.assertEquals(10, projectManagerIt.getWorkload(ProgrammingLanguages.SWIFT));
        Assert.assertEquals(35, projectManagerIt.getWorkload(ProgrammingLanguages.PYTHON));

    }
}

UML类图如下所示:

The UML class diagram looks like this:

中的代码setUp() 的方法EmployeeTest.java 实现以下树结构:

复合设计模式的主要缺点是您需要使用运行时检查限制某些操作,因为客户通常不知道他们是在处理 ProjectManager (分支)还是 Developer (叶子)实例。

The main disadvantage of the composite design pattern is that you need to restrict certain operations with runtime-checks, since clients usually do not know whether they are dealing with a ProjectManager (branch) or a Developer (leaf) instance.