目录
访问者模式【Visitor Pattern】,什么是访问者模式?作用?主要角色?优缺点?应用场景?访问者模式实现案例?
什么是访问者模式?
访问者模式(Visitor Pattern)是一种行为设计模式,它将操作与对象结构分离,允许你在不改变对象结构的情况下,为对象添加新的操作。访问者模式的核心思想是:将作用于某种数据结构中的各个元素的操作封装在访问者中,使得操作可以独立于这些元素而变化。
访问者模式的作用?
访问者模式的作用在于分离对象结构和操作,使得你可以在不改变数据结构的前提下为其增加新的功能。
例如:如果你有一个表示几何图形的对象结构,通过访问者模式,你可以轻松添加新的操作(如计算面积、绘制图形等)而不需要修改这些图形对象。
访问者模式的主要角色
(1)Visitor(访问者)
为对象结构中的每个具体元素定义操作的接口,访问者接口通常包含一组访问方法,每个方法对应一个具体元素类型。
(2)ConcreteVisitor(具体访问者)
具体访问者实现访问者接口中的操作,并为每种具体元素类型实现其访问逻辑。
(3)Element(元素)
定义一个接受访问者的方法,通常称为 accept(Visitor visitor),使得访问者能够访问其数据结构中的各个元素。
(4)ConcreteElement(具体元素)
实现 Element 接口,并在 accept 方法中调用访问者的对应方法。
(5)ObjectStructure(对象结构)
是一个包含元素的集合或复杂的对象结构,通常会提供一个遍历其元素的方法,以便访问者可以对结构中的所有元素进行操作。
访问者模式优缺点
优点
(1)遵循开放-关闭原则
允许在不修改现有代码的情况下添加新的操作。
(2)简化复杂对象的操作
通过将操作逻辑集中在访问者中,使得每个元素的访问操作更加清晰,避免在元素类中添加过多方法。
(3)统一操作逻辑
访问者可以对一组不相关的对象进行操作,从而实现操作的一致性。
缺点
(1)难以增加新的元素类型
访问者模式在添加新的操作时很灵活,但如果要增加新的元素类型,所有的访问者类都需要修改,违反了开放关闭原则。
(2)破坏封装性
访问者模式需要对象暴露其内部结构,可能破坏类的封装性。
(3)复杂性增加
在小型系统或简单结构中,使用访问者模式可能会增加不必要的复杂性。
访问者模式应用场景
(1)需要为一个对象结构中的元素添加新的操作,但不希望修改这些元素的类。
(2)对象结构相对稳定,但操作可能经常变化。
(3)存在多个不同的、不相关的操作需要在同一组元素上执行。例如:在不同类型的对象上执行打印、导出等操作。
访问者模式实现案例
某公司有七百多技术人员,分布在全国各地,组织架构你在组合模式中也看到了,很常见的家长领
导型模式,每个技术人员的岗位都是固定的,你在组织机构在那棵树下,充当的是什么叶子节点都是非常明确的,每一个员工的信息比如名字、性别、薪水等都是记录在数据库中,现在有这样一个需求,我要把公司中的所有人员信息都打印汇报上去。
1、定义访问者(Visitor,员工工资、报表)
抽象访问者
package com.uhhe.common.design.visitor;
/**
* 访问者,我要去访问人家的数据了
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 17:31
*/
public interface IVisitor {
/**
* 首先定义可以访问普通员工
*
* @param commonEmployee 普通员工
*/
void visit(CommonEmployee commonEmployee);
/**
* 其次定义,我还可以访问部门经理
*
* @param manager 部门经理
*/
void visit(Manager manager);
}
展示统计访问者
package com.uhhe.common.design.visitor;
/**
* 展示统计报表
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 19:04
*/
public interface ITotalVisitor extends IVisitor {
/**
* 统计所有员工工资总和
*/
void totalSalary();
}
展示报表访问者
package com.uhhe.common.design.visitor;
/**
* 负责展示报表的产生
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 19:04
*/
public interface IShowVisitor extends IVisitor {
/**
* 展示报表
*/
void report();
}
2、具体的访问者(ConcreteVisitor)
该接口定义其下的实现类能够访问哪些类,子接口定义了具体访问者的任务和责任。
实现展示统计访问者
package com.uhhe.common.design.visitor;
/**
* 统计报表,该访问者起汇总作用,把容器中的数据一个一个遍历,然后汇总
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 19:07
*/
public class TotalVisitor implements ITotalVisitor {
/**
* 部门经理的工资系数是5
*/
private final static int MANAGER_COEFFICIENT = 5;
/**
* 员工的工资系数是2
*/
private final static int COMMON_EMPLOYEE_COEFFICIENT = 2;
/**
* 普通员工的工资总和
*/
private int commonTotalSalary = 0;
/**
* 部门经理的工资总和
*/
private int managerTotalSalary = 0;
@Override
public void totalSalary() {
System.out.println("本公司的月工资总额是" + (this.commonTotalSalary + this.managerTotalSalary));
}
@Override
public void visit(CommonEmployee commonEmployee) {
// 访问普通员工,计算工资总额
this.commonTotalSalary = this.commonTotalSalary + commonEmployee.getSalary() * COMMON_EMPLOYEE_COEFFICIENT;
}
@Override
public void visit(Manager manager) {
// 访问部门经理,计算工资总额
this.managerTotalSalary = this.managerTotalSalary + manager.getSalary() * MANAGER_COEFFICIENT;
}
}
实现展示报表访问者
package com.uhhe.common.design.visitor;
/**
* 展示报表,该访问者的工作就是看到什么数据展示什么数据
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 19:05
*/
public class ShowVisitor implements IShowVisitor {
private String info = "";
@Override
public void report() {
System.out.println(this.info);
}
@Override
public void visit(CommonEmployee commonEmployee) {
// 访问普通员工,组装信息
this.info = this.info + this.getBasicInfo(commonEmployee) + "工作:" + commonEmployee.getJob() + "\t\n";
}
@Override
public void visit(Manager manager) {
// 访问经理,然后组装信息
this.info = this.info + this.getBasicInfo(manager) + "业绩: " + manager.getPerformance() + "\t\n";
}
private String getBasicInfo(Employee employee) {
// 组装出基本信息
String info = "姓名:" + employee.getName() + "\t";
info = info + "性别:" + (employee.getSex() == Employee.FEMALE ? "女" : "男") + "\t";
info = info + "薪水:" + employee.getSalary() + "\t";
return info;
}
}
3、员工抽象(Element)
package com.uhhe.common.design.visitor;
import lombok.Data;
/**
* 在一个单位里谁都是员工,甭管你是部门经理还是小兵
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 10:50
*/
@Data
public abstract class Employee {
/**
* 0代表是女性
*/
public final static int FEMALE = 0;
/**
* 1代表是男性
*/
public final static int MALE = 1;
/**
* 甭管是谁,都有工资
*/
private String name;
/**
* 只要是员工那就有薪水
*/
private int salary;
/**
* 性别很重要
*/
private int sex;
/**
* 允许一个访问者过来访问
*
* @param visitor 访问者
*/
public abstract void accept(IVisitor visitor);
}
4、员工实现类(ConcreteElement)
员工公共属性
package com.uhhe.common.design.visitor;
/**
* 员工公共属性
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 18:31
*/
public class CommonEmployee extends Employee {
/**
* 工作内容,这个非常重要,以后的职业规划就是靠这个了
*/
private String job;
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public void accept(IVisitor visitor) {
// 允许访问者过来访问
visitor.visit(this);
}
}
部门经理
package com.uhhe.common.design.visitor;
/**
* 部门经理
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 18:35
*/
public class Manager extends Employee {
/**
* 这类人物的职责非常明确:业绩
*/
private String performance;
public String getPerformance() {
return performance;
}
public void setPerformance(String performance) {
this.performance = performance;
}
@Override
public void accept(IVisitor visitor){
// 部门经理允许访问者访问
visitor.visit(this);
}
}
5、老板来看汇报了
package com.uhhe.common.design.visitor;
import java.util.ArrayList;
import java.util.List;
/**
* 老板来看汇报了
*
* @author nizhihao
* @version 1.0.0
* @date 2023/3/2 18:39
*/
public class Client {
/**
* 访问者模式【Visitor Pattern】
* <p>
* 角色的职责:
* ①抽象访问者(Visitor): 抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是 visit 方法的参数定义哪些对象是可以被访问的
* ②具体访问者(ConcreteVisitor): 访问者访问到一个类后该怎么干(哎,这个别读歪了),要做什么事情
* ③抽象元素(Element): 接口或者抽象类,声明接受那一类型的访问者访问,程序上是通过 accept 方法中的参数来定义
* ④具体元素(ConcreteElement): 实现 accept 方法,通常是 visitor.visit(this),基本上都形成了一个套路了
* ⑤结构对象(ObjectStructure): 容纳多个不同类、不同接口的容器,比如 List、Set、Map 等,在项目中,一般很少抽象出来这个角色
* <p>
* 拦截器:
* 核心作用是“围墙”作用,拦截器对被拦截的对象进行检查,符合规则的对象则开门放进去,继续执行下一个逻辑,
* 不符合规则的则弹回(其实这也是过滤器的作用);拦截器还有一个作用是修改数据,对于符合规则数据可以进行修改,
* 以便继续后序的逻辑。具备了这两个功能,拦截器的雏形就有了,访问者模式就可以实现简单的拦截器角色
*/
public static void main(String[] args) {
// 展示报表访问者
IShowVisitor showVisitor = new ShowVisitor();
// 汇总报表的访问者
ITotalVisitor totalVisitor = new TotalVisitor();
for (Employee emp : mockEmployee()) {
// 接受展示报表访问者
emp.accept(showVisitor);
// 接受汇总表访问者
emp.accept(totalVisitor);
}
// 展示报表
showVisitor.report();
// 汇总报表
totalVisitor.totalSalary();
}
/**
* 模拟出公司的人员情况,我们可以想象这个数据室通过持久层传递过来的
*
* @return 公司人员情况
*/
public static List<Employee> mockEmployee() {
List<Employee> empList = new ArrayList<>();
// 产生张三这个员工
CommonEmployee zhangSan = new CommonEmployee();
zhangSan.setJob("编写Java程序,绝对的蓝领、苦工加搬运工");
zhangSan.setName("张三");
zhangSan.setSalary(1800);
zhangSan.setSex(Employee.MALE);
empList.add(zhangSan);
// 产生李四这个员工
CommonEmployee liSi = new CommonEmployee();
liSi.setJob("页面美工,审美素质太不流行了!");
liSi.setName("李四");
liSi.setSalary(1900);
liSi.setSex(Employee.FEMALE);
empList.add(liSi);
// 再产生一个经理
Manager wangWu = new Manager();
wangWu.setName("王五");
wangWu.setPerformance("基本上是负值,但是我会拍马屁呀");
wangWu.setSalary(18750);
wangWu.setSex(Employee.MALE);
empList.add(wangWu);
return empList;
}
}