是的,如果您想映射树结构,复合模式确实是正确的选择。参考您的示例,复合设计模式意味着您的类 Employee 充当节点,类 ProjectManager 充当分支,类 Developer 充当叶。在这种情况下,复合模式的主要优点是它可以统一处理您的作品中的对象。因此,您可以使用这种特定的 GoF 设计模式来表示实例的整个层次结构。
您需要以下参与者:
-
abstract 类Employee 必须声明组合的接口,并在一定程度上实现共同的行为。
-
ProjectManager 类扩展了abstract 类Employee 并实现了一种行为来对待Employee 孩子,同上。在您的情况下为 ProjectManager 或 Developer 实例。
-
Developer 还扩展了 abstract 类 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 类图如下所示:
EmployeeTest.java的setUp()方法中的代码实现了如下的树形结构:
复合设计模式的主要缺点是您需要通过运行时检查来限制某些操作,因为客户端通常不知道他们处理的是ProjectManager(分支)还是Developer(叶)实例。