# 访问者模式(Visitor Pattern)
代码地址
- Github:DesignPattern/src/main/java/com/design/visitor (opens new window)
- Gitee(码云):DesignPattern/src/main/java/com/design/visitor (opens new window)
# 1. 介绍
在访问者模式中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作
# 1.1. 特点
待补充
# 1.2. 结构
- 抽象节点(Node):声明一个接受操作,接受一个访问者对象作为一个参数
- 具体节点(ConcreteNode):实现了抽象节点所规定的接受操作
- 结构对象(ObjectStructure):也可以同时充当节点的作用,有如下的责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如 List 或 Set
- 抽象访问者(Visitor):声明了一个或者多个方法操作,形成所有的具体访问者角色必须实现的接口
- 具体访问者(ConcreteVisitor):实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作
# 1.3. 优缺点
- 意图:主要将数据结构与数据操作分离
- 主要解决:稳定的数据结构和易变的操作耦合问题
- 何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中
- 如何解决:在被访问的类里面加一个对外提供接待访问者的接口
- 关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者
- 使用场景:
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
- 节点是固定的,访问者根据本身的情况去实现节点扩展的功能
- 优点:
- 符合单一职责原则
- 优秀的扩展性
- 灵活性
- 缺点:
- 具体元素对访问者公布细节,违反了迪米特原则
- 具体元素变更比较困难
- 违反了依赖倒置原则,依赖了具体类,没有依赖抽象
- 应用实例:根据购买物品的访问者,来进行实现折扣,物品是不变的,变的是访问者,折扣的实现在访问者处理,这就是访问者模式 注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器
# 2. 代码
使用一个购买组装电脑各种零件,对个人及公司访问者不同优惠的例子
# 2.1. Node
/**
* 计算机抽象节点
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public abstract class ComputerNode {
public abstract void accept(Visitor visitor);
/**
* 原始价格
* @return
*/
public double getPrice() {
return 0;
}
}
# 2.1. ConcreteNode
/**
* 计算机CPU具体节点
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class CpuComputerNodeImpl extends ComputerNode {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public double getPrice() {
return 1500;
}
}
/**
* 计算机内存条具体节点
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class MemoryComputerNodeImpl extends ComputerNode {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public double getPrice() {
return 500;
}
}
/**
* 计算机主板具体节点
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class BoardComputerNodeImpl extends ComputerNode {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public double getPrice() {
return 1000;
}
}
# 2.2. ObjectStructure
/**
* 计算机结构对象,也可以同时充当具体节点的作用
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class ComputerStructure extends ComputerNode {
/*ComputerNode computerNode = new CpuComputerNodeImpl();
ComputerNode memoryComputerNode = new MemoryComputerNodeImpl();
ComputerNode boardComputerNode = new BoardComputerNodeImpl();*/
protected ComputerNode[] computerNodeArray;
public ComputerStructure() {
computerNodeArray = new ComputerNode[] {new CpuComputerNodeImpl(),
new MemoryComputerNodeImpl(), new BoardComputerNodeImpl()};
}
@Override
public void accept(Visitor visitor) {
/*this.computerNode.accept(visitor);
this.memoryComputerNode.accept(visitor);
this.boardComputerNode.accept(visitor);*/
for (ComputerNode computerNode : computerNodeArray) {
computerNode.accept(visitor);
}
// 整个结构对象,也同时充当节点的作用就需要调用
visitor.visit(this);
}
@Override
public double getPrice() {
// 计算机除去CPU内存主板后其他东西的总和
return 1000;
}
}
# 2.3. Visitor
/**
* 访问者接口
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public interface Visitor {
void visit(CpuComputerNodeImpl cpuComputerNode);
void visit(MemoryComputerNodeImpl memoryComputerNode);
void visit(BoardComputerNodeImpl boardComputerNode);
void visit(ComputerStructure computerStructure);
}
# 2.4. ConcreteVisitor
/**
* 个人访问者,CPU 95折,内存8折,主板9折,其他8折
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class PersonelVisitorImpl implements Visitor {
private double totalPrice = 0.0;
@Override
public void visit(CpuComputerNodeImpl cpuComputerNode) {
totalPrice = totalPrice + cpuComputerNode.getPrice() * 0.95;
}
@Override
public void visit(MemoryComputerNodeImpl memoryComputerNode) {
totalPrice = totalPrice + memoryComputerNode.getPrice() * 0.8;
}
@Override
public void visit(BoardComputerNodeImpl boardComputerNode) {
totalPrice = totalPrice + boardComputerNode.getPrice()* 0.9;
}
/**
* 计算机除去CPU内存主板后其他东西的总和
*
* @param computerStructure
*/
public void visit(ComputerStructure computerStructure) {
totalPrice = totalPrice * 0.8;
}
public double getTotalPrice() {
return totalPrice;
}
}
/**
* 公司访问者,CPU 85折,内存7折,主板8折,其他6折
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class CorpVisitorImpl implements Visitor {
private double totalPrice = 0.0;
@Override
public void visit(CpuComputerNodeImpl cpuComputerNode) {
totalPrice = totalPrice + cpuComputerNode.getPrice() * 0.85;
}
@Override
public void visit(MemoryComputerNodeImpl memoryComputerNode) {
totalPrice = totalPrice + memoryComputerNode.getPrice() * 0.7;
}
@Override
public void visit(BoardComputerNodeImpl boardComputerNode) {
totalPrice = totalPrice + boardComputerNode.getPrice() * 0.8;
}
/**
* 总结点本身触发
*
* @param computerStructure
*/
public void visit(ComputerStructure computerStructure) {
totalPrice = totalPrice * 0.6;
}
public double getTotalPrice() {
return totalPrice;
}
}
# 2.5. Main
/**
* 访问者模式,一个购买组装电脑各种零件,对个人及公司访问者不同优惠的例子
*
* @author wliduo[i@dolyw.com]
* @date 2023/2/3 17:33
*/
public class Main {
public static void main(String[] args) {
// 整个结构对象,也同时充当节点的作用
ComputerStructure computerStructure = new ComputerStructure();
// 个人购买
PersonelVisitorImpl personelVisitor = new PersonelVisitorImpl();
computerStructure.accept(personelVisitor);
System.out.println(personelVisitor.getTotalPrice());
// 公司购买
CorpVisitorImpl corpVisitor = new CorpVisitorImpl();
computerStructure.accept(corpVisitor);
System.out.println(corpVisitor.getTotalPrice());
}
}
2180.0
1455.0