访问者模式 + 组合模式
访问者模式(Visitor Pattern)和组合模式(Composite Pattern)是两种行为型和结构型的设计模式,它们可以有效地结合在一起,以便在复杂对象结构上执行操作,并且支持在不改变对象结构的情况下增加新行为。
-
访问者模式(Visitor Pattern)允许将一个操作(即访问行为)封装成一个对象,使得操作可以作用于不同的对象结构。通过引入访问者对象,具体的操作行为可以在不修改目标对象的情况下新增。该模式通常用于那些有复杂对象结构且需要对这些结构中的元素进行一系列操作的场景。
-
组合模式(Composite Pattern)则是一种结构型设计模式,允许将对象组合成树形结构来表示“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。组合模式让客户端可以统一对待单一对象和对象的集合。
结合使用这两种模式时,组合模式提供了树形结构的容器,而访问者模式则用于对这些树形结构进行遍历和操作。通过访问者模式,可以在不改变组合结构的情况下,增加对组合对象的操作。
1. 场景说明:组织结构
假设我们有一个公司组织结构,其中有不同的部门(如技术部、市场部等)和员工(如工程师、经理等)。每个部门或员工都有不同的职责和操作。我们希望通过组合模式来表示组织结构,并通过访问者模式来实现不同的操作,比如计算每个部门的薪资总和,或是打印出部门成员的职责。
2. 设计步骤
(1) 定义员工接口
首先,我们定义一个员工接口,该接口是所有员工(如经理、工程师等)的共同父类。每个员工将根据不同的操作,接受不同的访问者。
// 员工接口
public interface Employee {
void accept(Visitor visitor); // 接受访问者
}
(2) 定义具体员工类
我们定义几个具体的员工类,如经理和工程师,它们将实现accept
方法,接收访问者的访问。
// 经理类
public class Manager implements Employee {
private String name;
private double salary;
public Manager(String name, double salary) {
this.name = name;
this.salary = salary;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
}
// 工程师类
public class Engineer implements Employee {
private String name;
private double salary;
public Engineer(String name, double salary) {
this.name = name;
this.salary = salary;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
}
(3) 定义部门类:组合模式
接着,我们定义一个部门类,它将包含多个员工,并且作为一个容器对象,可以包含其他部门或员工。部门实现了Employee
接口,并接受访问者。
// 部门类
public class Department implements Employee {
private String departmentName;
private List<Employee> employees = new ArrayList<>();
public Department(String departmentName) {
this.departmentName = departmentName;
}
public void addEmployee(Employee employee) {
employees.add(employee);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
// 让部门中的每个员工接受访问者
for (Employee employee : employees) {
employee.accept(visitor);
}
}
public String getDepartmentName() {
return departmentName;
}
public List<Employee> getEmployees() {
return employees;
}
}
(4) 定义访问者接口
然后,我们定义一个访问者接口,该接口声明了对具体员工和部门的访问方法。每个访问者将定义一个具体的操作行为,如计算薪资总和、打印员工信息等。
// 访问者接口
public interface Visitor {
void visit(Manager manager); // 访问经理
void visit(Engineer engineer); // 访问工程师
void visit(Department department); // 访问部门
}
(5) 定义具体访问者:薪资总和计算
例如,我们定义一个具体的访问者:薪资总和计算,该访问者会计算整个公司或部门的薪资总和。
// 薪资总和计算访问者
public class SalarySumVisitor implements Visitor {
private double totalSalary = 0;
@Override
public void visit(Manager manager) {
totalSalary += manager.getSalary(); // 累加经理的薪水
}
@Override
public void visit(Engineer engineer) {
totalSalary += engineer.getSalary(); // 累加工程师的薪水
}
@Override
public void visit(Department department) {
// 可以计算部门的薪资总和
System.out.println("Calculating salary sum for department: " + department.getDepartmentName());
}
public double getTotalSalary() {
return totalSalary;
}
}
(6) 客户端代码
客户端代码创建了一个公司组织结构,使用访问者模式进行访问,并计算薪资总和。
public class Client {
public static void main(String[] args) {
// 创建员工
Employee manager1 = new Manager("Alice", 120000);
Employee engineer1 = new Engineer("Bob", 80000);
Employee engineer2 = new Engineer("Charlie", 75000);
// 创建部门
Department techDept = new Department("Tech Department");
techDept.addEmployee(manager1);
techDept.addEmployee(engineer1);
techDept.addEmployee(engineer2);
// 创建访问者
SalarySumVisitor salaryVisitor = new SalarySumVisitor();
// 访问整个部门,计算薪资总和
techDept.accept(salaryVisitor);
System.out.println("Total Salary of Tech Department: " + salaryVisitor.getTotalSalary());
}
}
3. 运行结果:
Calculating salary sum for department: Tech Department
Total Salary of Tech Department: 275000.0
4. 优点:
组合模式的优势:
- 统一接口: 组合模式使得客户端可以通过统一的接口对单个对象和组合对象进行操作,从而简化了客户端代码。
- 树形结构: 通过组合模式,可以方便地构建树形结构,表示“部分-整体”的关系,如公司组织结构、文件系统等。
- 灵活扩展: 新的员工或部门可以轻松地添加到现有结构中,且不会影响到其他部分的功能。
访问者模式的优势:
- 增加新操作: 访问者模式允许我们在不修改对象结构的情况下,增加新的操作。每个访问者类代表了一种操作行为,可以灵活扩展。
- 集中处理逻辑: 通过访问者模式,可以将不同对象的处理逻辑集中到访问者中,而不是分散到每个对象内部。
- 符合开闭原则: 新的操作(访问者)可以通过扩展来增加,而不需要修改现有的对象结构。
5. 结合的优势:
将访问者模式和组合模式结合使用,能够在不修改对象结构的情况下,灵活地增加操作行为,尤其适用于那些结构复杂、对象类型多样的系统。
- 对象结构和行为分离: 组合模式负责组织和管理对象的层次结构,访问者模式则负责对这些对象进行操作,二者职责分离,符合单一职责原则。
- 灵活扩展操作: 我们可以很容易地添加新的访问者来执行新的操作,而无需修改现有的对象结构(如员工、部门等)。
- 支持复杂操作: 在复杂的对象结构中(如包含多个部门、员工等),可以通过访问者模式集中处理每个节点的操作,同时保持对象结构的简洁性。
6. 总结:
将访问者模式和组合模式结合使用,能够有效地解耦对象结构和操作行为。组合模式负责提供树形结构表示部分与整体的关系,而访问者模式则负责在这些结构上执行各种操作,如计算薪资总和、打印员工信息等。结合这两种模式,可以使得系统在扩展新操作时更加灵活,同时保持较高的代码可维护性。