【问题标题】:How to implement a composite pattern in Java?如何在 Java 中实现复合模式?
【发布时间】:2015-08-01 03:48:52
【问题描述】:

我想在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 类中出现编译器错误,知道为什么吗?

提前致谢。

【问题讨论】:

    标签: java design-patterns architecture


    【解决方案1】:

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

    Composite的GoF结构如下:

    如您所见,Operation() 在所有元素中都很常见。这将是您方案的 getWorkload() 方法。

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

    【讨论】:

      【解决方案2】:

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

      您需要以下参与者:

      1. abstractEmployee 必须声明组合的接口,并在一定程度上实现共同的行为。
      2. ProjectManager 类扩展了abstractEmployee 并实现了一种行为来对待Employee 孩子,同上。在您的情况下为 ProjectManagerDeveloper 实例。
      3. Developer 还扩展了 abstractEmployee 并表示没有任何子节点的叶子。

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

      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.javasetUp()方法中的代码实现了如下的树形结构:

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

      【讨论】:

      • 哇。感谢您的全面回答!
      【解决方案3】:

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

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

      顺便说一句,这不是组合模式 - 那是继承。您可以(并且应该)read more 对此。

      【讨论】:

      • 谢谢!这真的很有帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      相关资源
      最近更新 更多