访问者模式
访问者模式(Visitor Pattern) 的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改,接受这个操作的数据结构则可以保持不变。
1访问者模式的定义
访问者模式的英文原文:
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
意思是:封装一些作用于某种数据结构中的各种元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
访问者模式涉及5个角色:
- 抽象访问者角色:该角色声明一个或多个访问操作,定义访问者可以访问哪些元素。
- 具体访问者角色:该角色实现抽象访问者中的各个访问操作。
- 抽象元素角色;该角色声明一个接受操作,接受一个访问者对象。
- 具体元素角色:该角色实现抽象元素中的接受操作。
- 结构对象角色:该角色可以遍历结构中所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,也可以设计一个复合对象或一个集合,如List或set。
![](https://img2020.cnblogs.com/blog/1989411/202105/1989411-20210510143529886-292946834.png)
访问者模式的类图
创建抽象元素类
Element.java
package com.eric.行为型模式_Part2.访问者模式.引例.element;
import com.eric.行为型模式_Part2.访问者模式.引例.vistor.Visitor;
/**
* @author Eric
* @ProjectName my_design_23
* @description 抽象元素角色
* @CreateTime 2020-12-14 08:38:03
*/
public abstract class Element {
//接受操作
public abstract void accept(Visitor vi);
}
创建抽象元素的元素类
ConcreteElement1.java
package com.eric.行为型模式_Part2.访问者模式.引例.element;
import com.eric.行为型模式_Part2.访问者模式.引例.vistor.Visitor;
/**
* @author Eric
* @ProjectName my_design_23
* @description 具体元素1
* @CreateTime 2020-12-14 08:39:42
*/
public class ConcreteElement1 extends Element {
//接受操作
@Override
public void accept(Visitor vi) {
vi.visit(this);
}
//业务逻辑方法
public void operation()
{
System.out.println("访问元素1!");
}
}
ConcreteElement2.java
package com.eric.行为型模式_Part2.访问者模式.引例.element;
import com.eric.行为型模式_Part2.访问者模式.引例.vistor.Visitor;
/**
* @author Eric
* @ProjectName my_design_23
* @description 具体元素1
* @CreateTime 2020-12-14 08:39:42
*/
public class ConcreteElement2 extends Element {
//接受操作
@Override
public void accept(Visitor vi) {
vi.visit(this);
}
//业务逻辑方法
public void operation()
{
System.out.println("访问元素2!");
}
}
创建抽象访问者
Visitor.java
package com.eric.行为型模式_Part2.访问者模式.引例.vistor;
import com.eric.行为型模式_Part2.访问者模式.引例.element.ConcreteElement1;
import com.eric.行为型模式_Part2.访问者模式.引例.element.ConcreteElement2;
/**
* @author Eric
* @ProjectName my_design_23
* @description 抽象访问者
* @CreateTime 2020-12-14 08:42:02
*/
public interface Visitor {
//可以访问哪些对象
public void visit(ConcreteElement1 e1);
public void visit(ConcreteElement2 e2);
}
创建具体访问者
ConcreteVisitor.java
package com.eric.行为型模式_Part2.访问者模式.引例.vistor;
import com.eric.行为型模式_Part2.访问者模式.引例.element.ConcreteElement1;
import com.eric.行为型模式_Part2.访问者模式.引例.element.ConcreteElement2;
/**
* @author Eric
* @ProjectName my_design_23
* @description 具体访问者角色
* @CreateTime 2020-12-14 08:46:57
*/
public class ConcreteVisitor implements Visitor{
//访问元素1
@Override
public void visit(ConcreteElement1 e1) {
e1.operation();
}
//访问元素2
@Override
public void visit(ConcreteElement2 e2) {
e2.operation();
}
}
创建结构对象
ObjectStructure.java
package com.eric.行为型模式_Part2.访问者模式.引例;
import com.eric.行为型模式_Part2.访问者模式.引例.element.ConcreteElement1;
import com.eric.行为型模式_Part2.访问者模式.引例.element.ConcreteElement2;
import com.eric.行为型模式_Part2.访问者模式.引例.element.Element;
import com.eric.行为型模式_Part2.访问者模式.引例.vistor.Visitor;
import java.util.Random;
import java.util.Vector;
/**
* @author Eric
* @ProjectName my_design_23
* @description 结构对象角色
* @CreateTime 2020-12-14 08:48:20
*/
public class ObjectStructure {
private Vector<Element> elements ;
//构造函数
public ObjectStructure()
{
elements = new Vector<Element>();
}
//执行访问操作
public void action(Visitor vi)
{
int i = 1;
for (Element e : elements) {
System.out.print("访问次数"+(i++)+":");
e.accept(vi);
}
}
//添加新元素
public void add(Element e)
{
elements.add(e);
}
//元素生成器
//这里通过一个工厂方法进行模拟
public void createElements()
{
Random rand = new Random();
for(int i=0;i<10;i++)
{
if(rand.nextInt(100)>50){
//添加元素1
this.add(new ConcreteElement1());
}else{
//添加元素2
this.add(new ConcreteElement2());
}
}
}
}
创建测试类
Client.java
package com.eric.行为型模式_Part2.访问者模式.引例;
import com.eric.行为型模式_Part2.访问者模式.引例.vistor.ConcreteVisitor;
/**
* @author Eric
* @ProjectName my_design_23
* @description 场景应用
* @CreateTime 2020-12-14 08:56:01
*/
public class Client {
public static void main(String[] args) {
//创建一个结构对象
ObjectStructure objectStructure = new ObjectStructure();
//生成元素
objectStructure.createElements();
//创建一个访问者对象
ConcreteVisitor concreteVisitor = new ConcreteVisitor();
//访问者对结构进行访问(执行访问)
objectStructure.action(concreteVisitor);
}
}
测试结果
![](https://img2020.cnblogs.com/blog/1989411/202105/1989411-20210510143530321-1157811495.png)
2访问者模式的应用
a.访问者模式的优缺点
访问者模式的优点
- 访问者模式使得增加新的操作变得很容易,增加新的操作只需要增加新的访问者类。
- 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个元素类中。
- 访问者模式可以跨过几个类的等级结构访问属于不同等级结构的成员类。
- 累积状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态累计在自己的内部,而不是分散到很多的元素对象中,益于系统的维护。
访问者模式的缺点
- 增加新的元素类变得很困难。每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并且在每一个具体访问者类中增加相应的具体操作。
- 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这隐含了一个对所有元素对象的要求,即必须暴露一些自己的操作和内部状态,否则访问者的访问就变的没有意义。 由于访问者对象自己会积累访问操作的所需的状态,从而使得这些状态不在存储于元素对象中,破坏了类的封装性。
- 违背了依赖倒置原则。访问者依赖的是具体的元素,而不是抽象的元素,这破坏了依赖倒置的原则,特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难。
b.访问者模式的应用场景
- 一个对象结构包含很多类对象,他们有不同的接口,当对这些对象实施依赖于具体类的操作时,即使用迭代器模式不能胜任的场景下,可以采用访问者模式。
- 需要对一个对象结构中的对象进行很多不同并且不相关的操作,避免操作污染类。
- 业务规则要求遍历对个不同的对象,这本身也是访问者模式的出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,执行不同的操作。
3访问者模式的实例
使用访问者模式完成对计算机各个硬件的访问。
![](https://img2020.cnblogs.com/blog/1989411/202105/1989411-20210510143530746-793909419.png)
计算机硬件的类图
创建硬件抽象类
Hardware.java
package com.eric.行为型模式_Part2.访问者模式.例1;
import com.eric.行为型模式_Part2.中介者模式.例1.Person;
/**
* @author Eric
* @ProjectName my_design_23
* @description 硬件的抽象类
* @CreateTime 2020-12-14 12:11:57
*/
public abstract class Hardware {
String type ;//型号
//构造器
public Hardware(String type )
{
this.type = type;
}
public String getType()
{return this.type;}
//运转
public abstract void run();
//接受计算机的访问者
public abstract void accept(ComputerVisitor computerVisitor);
}
创建具体硬件
CPU.java
package com.eric.行为型模式_Part2.访问者模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description CPU
* @CreateTime 2020-12-14 12:16:52
*/
public class CPU extends Hardware {
public CPU(String type) {
super(type);
}
@Override
public void run() {
System.out.println("型号为"+type+"的CPU正在运行...");
}
@Override
public void accept(ComputerVisitor computerVisitor) {
computerVisitor.visitCPU(this);
}
}
HardDisk.java
package com.eric.行为型模式_Part2.访问者模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 硬盘
* @CreateTime 2020-12-14 12:18:22
*/
public class HardDisk extends Hardware {
public HardDisk(String type) {
super(type);
}
@Override
public void run() {
System.out.println("型号为"+type+"的硬盘正在运行...");
}
@Override
public void accept(ComputerVisitor computerVisitor) {
computerVisitor.visitHardDisk(this);
}
}
创建电脑访问者抽象类
ComputerVisitor.java
package com.eric.行为型模式_Part2.访问者模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 计算机硬件的访问者
* @CreateTime 2020-12-14 12:15:17
*/
public interface ComputerVisitor {
void visitCPU(CPU cpu);//访问CPU
void visitHardDisk(HardDisk hardDisk);//访问硬盘
}
创建电脑访问者的具体实现类
RunVisitor.java
package com.eric.行为型模式_Part2.访问者模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 计算机运转的访问者
* @CreateTime 2020-12-14 12:21:10
*/
public class RunVisitor implements ComputerVisitor {
@Override
public void visitCPU(CPU cpu) {
cpu.run();
}
@Override
public void visitHardDisk(HardDisk hardDisk) {
hardDisk.run();
}
}
TypeVisitor.java
package com.eric.行为型模式_Part2.访问者模式.例1;
import com.eric.创建型模式.建造者模式.例1.builder.ComputerBuilder;
/**
* @author Eric
* @ProjectName my_design_23
* @description 计算机硬件的访问者
* @CreateTime 2020-12-14 12:19:45
*/
public class TypeVisitor implements ComputerVisitor {
@Override
public void visitCPU(CPU cpu) {
System.out.println("CPU型号:"+cpu.getType());
}
@Override
public void visitHardDisk(HardDisk hardDisk) {
System.out.println("硬盘型号:"+hardDisk.getType());
}
}
创建电脑类
Computer.java
package com.eric.行为型模式_Part2.访问者模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 计算机
* @CreateTime 2020-12-14 12:21:52
*/
public class Computer {
private Hardware cpu;
private Hardware hardDisk;
public Computer()
{
this.cpu = new CPU("Tntel Core i7-620");
this.hardDisk = new HardDisk("Seagate 500G 7200转");
}
public void accept(ComputerVisitor computerVisitor)
{
cpu.accept(computerVisitor);
hardDisk.accept(computerVisitor);
}
}
创建测试类
Client.java
package com.eric.行为型模式_Part2.访问者模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 测试类‘
* @CreateTime 2020-12-14 12:24:41
*/
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
//类型访问者
ComputerVisitor typeVisitor = new TypeVisitor();
//运行的硬件访问者
ComputerVisitor runVisitor = new RunVisitor();
computer.accept(typeVisitor);
System.out.println("-------------------------");
computer.accept(runVisitor);
}
}
测试结果
![](https://img2020.cnblogs.com/blog/1989411/202105/1989411-20210510143531092-880237489.png)