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

How to implement a composite pattern in Java?

我想在 Java 中实现一个复合模式,以便映射一个软件开发组织。因此,我们假设有多个项目经理和多个开发人员。每个开发人员都被分配给一个项目经理,并且每个开发人员都能够使用各种编程语言进行编码。项目经理领导开发人员并准确了解他们的工作量。

我对这个设计模式不是百分百确定,但我认为它是这个场景的完美用例,不是吗?

结果应该是这样的:

我想询问项目经理,查看所有能够使用特定编程语言进行编码的开发人员的工作量,例如Java.

这是我目前的情况:

Employee.java:

public class Employee {

    private String name = null;

    public Employee() {
        name = "Noob";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

ProgrammingLanguages.java:

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

ProjectManager.java:

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

public class ProjectManager extends Employee {

private List<Employee> employeeList = null;

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

public List<Employee> getEmployees() {
    return employeeList;
}

public void setEmployees(List<Employee> employees) {
    employeeList = employees;
}

public int getTotalWorkload() {
    int workload = 0;
    for (Employee employee : employeeList) {
        workload += employee.getWorkload(); // Error! Cannot resolve method 'getWorkload()'
    }
    return workload;
}
}

开发者:

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

public class Developer extends Employee {

private List<ProgrammingLanguages> languagesList = null;

private int workload = 0;

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

public void setLanguagesList(List<ProgrammingLanguages> languagesList) {
    this.languagesList = languagesList;
}

public void addProgrammingLanguage(ProgrammingLanguages language) {
    languagesList.add(language);
}

public List<ProgrammingLanguages> getLanguagesList() {
    return languagesList;
}

public void setWorkload(int workload) {
    this.workload = workload;
}

public int getWorkload() {
    return workload;
}

}

不幸的是,我在 ProjectManager class 中遇到编译器错误,知道为什么吗?

提前致谢。

getWorkload() 方法未在 class Employee 中定义,您正在尝试访问它。

为了解决编译错误,您应该将此方法添加到 Employee - 我将其添加为 abstract 以强制任何(新)子 class 实现它。

顺便说一句,那不是组合模式——那是继承。您可以(并且应该)read more 了解它。

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

您需要以下参与者:

  1. abstractclassEmployee必须声明组合的接口并在一定程度上实现共同的行为。
  2. ProjectManager class 扩展了 abstract class Employee 并实施了治疗 Employee 儿童的行为,i.d.在您的情况下 ProjectManagerDeveloper 个实例。
  3. Developer 也扩展了 abstract class Employee 并表示没有任何子节点的叶子。

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

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);
    }

}

Developer.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
}

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

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 class 图如下所示:

EmployeeTest.javasetUp()方法中的代码实现了如下树结构:

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

I am not a hundred percent sure about this design pattern, but I think that it is the perfect use case for this scenario, isn't it?

Composite的GoF结构如下:

如您所见,Operation()在所有元素中都是通用的。那将是您方案的 getWorkload() 方法。

但是,它与模式有些不一致,因为它暗示 Manager 的工作负载由她的员工组成。在现实生活中恰恰相反,至少对于一位优秀的经理而言。我建议将方法名称更改为 getEffortUnderMyResponsibility() 之类的名称,以暗示完成工作的责任,而不是实际完成工作。对于程序员来说,他们确实这样做了;对于管理者来说,他们有责任完成它。