访问者模式
基本介绍
1.定义:把对数据的操作都封装到访问者类中,无需改变结构类,调用不同的访问者访问这个对象都会呈现出不同的处理方式
2.目的:解决类结构不变但操作处理逻辑易变的问题
3.原理:在被访问的类里面加一个对外提供接待访问者的接口
角色
1.Visitor:抽象访问者,接口 / 抽象类,为每个 ConcreteElement 类声明一个 visit 操作
2.ConcreteVisitor:具体访问者,实现 Visitor 中声明的 visit
3.ObjectStructure:对象结构,聚合 Element 对象的集合,存放 ConcreteElement,提供方法,允许 Client 传入 Visitor 对象,对集合中的 ConcreteElement 进行操作
4.Element:抽象元素,定义一个 accept 方法,接收一个 Visitor 对象
5.ConcreteElement:具体元素,实现 accept 方法
事项
1.优点
(1)符合单一职责原则,扩展性、灵活性高,可以对功能进行统一,添加一个 ConcreteVisitor 类,只需增加一个 ConcreteElement 类即可在客户端调用,不需要改动任何其他类的代码
(2)解决数据结构和操作耦合性问题,封装作用于某种数据结构的各元素的操作,可以在不改变数据结构的前提下,定义作用于这些元素的新的操作,将数据结构与数据操作分离
2.缺点
(1)违反迪米特法则:ConcreteElement 对 Visitor 公布细节
(2)违反依赖倒转原则:Visitor 依赖 ConcreteElement,而不是 Element
3.应用场景
(1)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类
(2)对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
桥接模式、 访问者模式
1.桥接模式
(1)将抽象部分(属性)与它的实现部分分离,使它们都可以独立地变化
(2)桥接模式主要用于将对象的抽象部分和实现部分隔离,使得各自承担责任,可独立变化
(3)桥接模式添加的方法彼此不相同,但相关
(4)桥接模式没有对象结构,通过在 Abstraction 中聚合 Implementor,构造器 / setter 方法传入 ConcreteImplementor,实现新方法的添加
2.访问者模式
(1)表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素的类的前提下定义作用于这些元素的新操作
(2)访问者模式主要用于为某对象结构中包含的多类对象灵活添加新功能
(3)访问者模式添加的新功能彼此不相同且不相关
(4)访问者模式通过对象结构遍历所有具体元素
分派
1.定义:根据对象的类型而对方法进行的选择
2.概念
(1)方法的宗量:方法的接收者与方法的参数
(2)静态类型 / 明显类型:变量被声明时的类型
(3)实际类型:变量所引用的对象的真实类型
3.根据分派发生的时期,分成静态分派、动态分派
(1)静态分派:发生在编译时期,静态分派根据静态类型信息发生,方法重载就是静态分派的最典型的应用,即编译时多态
(2)动态分派:发生在运行时期,运行时,根据参数的类型,选择合适的重载方法,面向对象的语言利用动态分派来实现方法置换产生的多态性,即运行时多态
4.根据分派基于多少宗量,可以把分派划分为单分派和多分派(双分派是多分派的一种形式)
(1)单分派是根据一个宗量来对方法进行选择
(2)多分派则是根据多个宗量来对方法进行选择
6.Java语言的分派
(1)静态分派属于多分派
(2)动态分派属于单分派
访问者模式是伪动态双分派
1.第一次动态单分派
(1)在Client 中,将 ConcreteVisitor 作为参数传入 ConcreteElement 的方法
(2)ConcreteElement 所有方法的接收参数都是 Visitor 类型,方法的参数类型不会影响虚拟机对方法的选择,而是由 ConcreteElement 的实际类型来决定的
2.第二次动态单分派
(1)在第一次分派所述的方法中,ConcreteVisitor 将 ConcreteElement 作为参数,传入 ConcreteVisitor 的方法
(2)在运行时根据 ConcreteElement 的具体类型,选择调用 ConcreteVisitor 的方法,不同的方法接受不同的 ConcreteElement 类型
3.两次动态单分派结合起来,就完成了一次伪动态双分派
4.过程总结
(1)先确定了调用哪个 ConcreteElement 的 accept 方法,然后再确定了调用 Visitor 中针对哪个 ConcreteElement 的 visit 方法
(2)传递 this(上述的 ConcreteElement)进 (上述的Visitor)visit 方法,Visitor 根据 this 的类型,找到了需要调用的方法
代码示例
import java.util.ArrayList;
import java.util.List;
public class Client {//客户端
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
ConcreteVisitor1 concreteVisitor1 = new ConcreteVisitor1();
ConcreteVisitor2 concreteVisitor2 = new ConcreteVisitor2();
ConcreteElement1 concreteElement1 = new ConcreteElement1(1);
ConcreteElement2 concreteElement2 = new ConcreteElement2(2);
objectStructure.add(concreteElement1);
objectStructure.add(concreteElement2);
System.out.println("元素 1 + 元素 2");
System.out.println("访问者 1 的权重:" + objectStructure.accept(concreteVisitor1));
System.out.println("访问者 2 的权重:" + objectStructure.accept(concreteVisitor2));
objectStructure.remove(concreteElement1);
System.out.println("元素 1");
System.out.println("访问者 1 的权重:" + objectStructure.accept(concreteVisitor1));
System.out.println("访问者 2 的权重:" + objectStructure.accept(concreteVisitor2));
objectStructure.remove(concreteElement2);
System.out.println("无元素");
System.out.println("访问者 1 的权重:" + objectStructure.accept(concreteVisitor1));
System.out.println("访问者 2 的权重:" + objectStructure.accept(concreteVisitor2));
}
}
abstract class Visitor {//抽象访问者
public abstract int visit1(ConcreteElement1 concreteElement1);
public abstract int visit2(ConcreteElement2 concreteElement2);
}
class ConcreteVisitor1 extends Visitor {//具体访问者1
@Override
public int visit1(ConcreteElement1 concreteElement1) {
return concreteElement1.getData() + 1;
}
@Override
public int visit2(ConcreteElement2 concreteElement2) {
return concreteElement2.getData() + 2;
}
}
class ConcreteVisitor2 extends Visitor {//具体访问者2
@Override
public int visit1(ConcreteElement1 concreteElement1) {
return concreteElement1.getData() + 3;
}
@Override
public int visit2(ConcreteElement2 concreteElement2) {
return concreteElement2.getData() + 4;
}
}
abstract class Element {//抽象元素
public int weight;
public Element(int weight) {
this.weight = weight;
}
public int getData() {
return weight;
}
public abstract int accept(Visitor visitor);
}
class ConcreteElement1 extends Element {//具体元素1
public ConcreteElement1(int weight) {
super(weight);
}
@Override
public int accept(Visitor visitor) {
return visitor.visit1(this);
}
}
class ConcreteElement2 extends Element {//具体元素2
public ConcreteElement2(int weight) {
super(weight);
}
@Override
public int accept(Visitor visitor) {
return visitor.visit2(this);
}
}
class ObjectStructure {//对象结构
private List<Element> elements = new ArrayList<>();
public int accept(Visitor visitor) {
int sum = 0;
for (Element element : elements) {
sum += element.accept(visitor);
}
return sum;
}
public void add(Element element) {
elements.add(element);
}
public void remove(Element element) {
elements.remove(element);
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战