设计模式以从复杂对象中提取信息

时间:2018-02-08 17:12:06

标签: design-patterns

假设一个包含部门列表的类公司。每个部门都有一份员工清单。

现在我想从Company对象中提取各种信息。例如,我想提取所有男性员工。或者提取加拿大的所有部门。无论出现什么新的需求,我都想创建一个新对象来提取这些信息,而无需修改我的业务对象(公司,部门,员工)。我想尊重开放/封闭的原则。

在这种情况下,最好的设计模式是什么?

我看了访客模式。这是我发现的。

1-访问方法返回void。这意味着我的访问者需要在内部保留所有提取的对象。这意味着我不能将访问者用作单身人士。

2-每位访客都需要知道如何循环部门/员工。它导致了大量重复的代码。我可以将该代码移动到抽象类中。我觉得抽象可能变得丑陋和大。

1 个答案:

答案 0 :(得分:1)

  

我想尊重开放/封闭的原则。

如果你想尊重这个原则,你必须决定什么是开放的,什么是封闭的。完全开放意味着您将数据与逻辑分开。在这种情况下,你的“对象”被简化为简单的数据容器,可能没有逻辑的getter和setter。然后将逻辑放在其他地方。 这与面向对象编程相反,因为面向对象编程将数据和逻辑结合在一起。

所以我会设计我的公司和部门,以便通过定义标准来查询对象。公司对其部门进行循环并应用标准的方式对我来说是关闭的。标准是开放部分。

从客户的角度来看,它会看起来像这样。

List<Employee> employees = company.getEmployees(e -> e.getFristname().startsWith("R"));

然后将以这种方式实现这些类

<强>公司

public class Company {

    private List<Department> departments = new ArrayList<>();
    private String name;

    public Company(String name) {
        this.name = name;
    }

    public List<Department> getDepartments(Predicate<Department> departmentPredicate) {
        List<Department> matchingDepartments = new ArrayList<>();

        for (Department department : departments) {
            if (departmentPredicate.test(department)) {
                matchingDepartments.add(department);
            }
        }

        return matchingDepartments;
    }

    public List<Employee> getEmployees(Predicate<Employee> predicate) {
        return getEmployees(d -> true, predicate);
    }

    public List<Employee> getEmployees(Predicate<Department> departmentPredicate,
            Predicate<Employee> employeePredicate) {
        List<Employee> employees = new ArrayList<>();

        for (Department department : departments) {
            if (departmentPredicate.test(department)) {
                List<Employee> employeesOfDepartment = department.getEmployees(employeePredicate);
                employees.addAll(employeesOfDepartment);
            }
        }

        return employees;
    }

    public String getName() {
        return name;
    }

    public void add(Department department) {
        departments.add(department);
    }

}

<强>系

public class Department {

    private List<Employee> employees = new ArrayList<>();
    private String name;

    public Department(String name) {
        this.name = name;
    }

    public List<Employee> getEmployees(Predicate<Employee> predicate) {
        List<Employee> matchingEmployees = new ArrayList<>();

        for (Employee employee : employees) {
            if (predicate.test(employee)) {
                matchingEmployees.add(employee);
            }
        }

        return matchingEmployees;
    }

    public void add(Employee employee) {
        employees.add(employee);
    }

    public List<Employee> getEmployees() {
        return Collections.unmodifiableList(employees);
    }

}

<强>员工

public class Employee {

    private String fristname;
    private String lastname;

    public Employee(String fristname, String lastname) {
        this.fristname = fristname;
        this.lastname = lastname;
    }

    public String getFristname() {
        return fristname;
    }

    public String getLastname() {
        return lastname;
    }

    @Override
    public String toString() {
        return "Employee [fristname=" + fristname + ", lastname=" + lastname + "]";
    }

}

示例客户端代码可能如下所示

Company company = new Company("Stackoverflow");

Department javaDepartment = new Department("Java");

Employee reneLink = new Employee("René", "Link");
javaDepartment.add(reneLink);

Employee pMartin = new Employee("Martin", "P");
javaDepartment.add(pMartin);

Employee rayTayek = new Employee("Ray", "Tayek");
javaDepartment.add(rayTayek);

company.add(javaDepartment);

List<Employee> employees = company.getEmployees(e -> e.getFristname().startsWith("R"));
System.out.println(employees);

让它更开放

如果您现在尝试让课程更“开放”,您将看到旅程结束的地方。

您可以为集合引入getter并在客户端代码中使用流。像这样:

Predicate<? super Department> departmentPredicate = d -> true; // just a simple predicate

List<Employee> employees = company.getDepartments().stream()
                                    .filter(departmentPredicate)
                                    .map(Department::getEmployees)
                                    .flatMap(List::stream)
                                    .filter(e -> e.getFristname().startsWith("R"))
                                    .collect(Collectors.toList());

正如您所看到的,您将把许多迭代逻辑从CompanyDepartment类移到客户端代码中。

但这也意味着必须在每个客户端中测试迭代代码。如果您将其放在CompanyDepartment课程中,则只需要在那里进行测试。

我认为上面第一个例子中显示的查询方法是打开和关闭之间的公平平衡,它们比流示例更具可读性。

但是如果你想使用流api,你仍然可以在查询方法中使用它。