Java

I want to implement a composite pattern in Java

order to map a software development organization. So let's say there are multiple project managers and multiple developers. Each developer is assigned exactly one project manager, and each developer can program in different programming languages. Project managers lead developers and know exactly their workload.

I'm not one hundred percent sure of this design pattern, but I think it's the perfect use case for this scenario, isn't it?

The result should be as follows:

I want to request a project manager to check the workload of all developers who can code in a specific programming language for example. Java

...

Here's what I have so far:

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

      

Developer:

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

}

      

Unfortunately I am getting a compiler error in the class ProjectManager

, any idea why?

Thanks in advance.

+3


source to share


3 answers


Yes, the composite pattern is indeed the right choice if you want to map tree structures. As for your example, the composite design pattern assumes that your class Employee

acts as a node, the class ProjectManager

acts as a branch, and the class Developer

acts as a leaf. In this context, the main advantage of a composite painting is that it treats the objects in your compositions evenly. As a result, you can represent entire hierarchies of instances with this GoF design pattern.

You need the following members:

  • The class abstract

    Employee

    must declare a composition interface and implement common behavior to some extent.
  • The class ProjectManager

    extends the class abstract

    Employee

    and implements behavior to handle Employee

    children, id in your cases, ProjectManager

    or Developer

    .
  • Developer

    also extends the class abstract

    Employee

    and represents a sheet that has no children.

I used your example code to demonstrate a composite template. Please note that this may differ from your desired result, but you can consider it a link.

Employee.java

(node):

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

(branch):

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

(sheet):

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

(enumeration):

package me.eckhart;

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

      

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

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

    }
}

      

The UML class diagram looks like this:

UML_class_diagram_composite_design_pattern_GoF

The code in the method setUp()

EmployeeTest.java

implements the following tree structure:

Tree_structure_Composite_Pattern

The main disadvantage of the composite design pattern is that you need to restrict certain operations to runtime checks, since clients usually don't know if they are dealing with a ProjectManager

(branch) or Developer

(leaf) instance.

0


source


I'm not one hundred percent sure of this design pattern, but I think it's the perfect use case for this scenario, isn't it?

The GoF Composite structure looks like this:

GoF Composite pattern



As you can see, it Operation()

is common to all elements. This will be your script getWorkload()

.

However, it is somewhat inconsistent with the pattern as it implies that it Manager

has a workload of its employees. It's the other way around in real life, at least with a good manager. I would suggest changing the name of the method to something like getEffortUnderMyResponsibility()

to imply responsibility for doing the work, rather than actually doing that work. It's true for programmers that they actually do it; for managers, they are responsible for its implementation.

+1


source


The method is getWorkload()

not defined in the Employee class and you are trying to access it.

To resolve the compilation error, you must add this method to Employee - I would add it as abstract

, to force any (new) subclass to execute it.

By the way, this is not a composition pattern - it is inheritance. You can (and should) read more about this.

0


source







All Articles