行为型模式(Behavioral Pattern)是对不同的对象之间划分责任和算法的抽象化;
行为型模式共有11种:
■ 模板方法模式
■ 命令模式
■ 责任链模式
■ 策略模式
■ 迭代器模式
■ 中介者模式
■ 观察者模式
■ 备忘录模式
■ 访问者模式
■ 状态模式
■ 解释器模式
一、访问者模式的简介
■ 封装一些作用于某种数据结构中的各元素的操作;
它可以在不改变数据结构的前提下定义作用于这些元素的新的操作;

image.png
访问者模式5个角色:
■ 抽象访问者(Visitor)角色:
该角色声明一个或多个访问操作,定义访问者可以访问哪些元素;
■ 具体访问者(Concrete Visitor)角色:
该角色实现抽象访问者角色中的各个访问操作。
■ 抽象元素(Element)角色:
该角色声明一个接受操作,接受一个访问者对象。
■ 具体元素(Concrete Element)角色:
该角色实现抽象元素中的接受操作。
■ 结构对象(Object Structure)角色:
该角色有以下责任,可以遍历结构中的所有元素;
如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,也可以设计一个复合对象或者一个集合,如List或Set;
/**
* 抽象元素
*/
public abstract class Element {
/**
* 接受操作
* @param vi
*/
public abstract void accept(Visitor vi);
}
/**
* 抽象访问者
*/
public interface Visitor {
/**
* 可以访问哪些对象
* @param ce1 ce2
*/
void visit(ConcreteElement1 ce1);
void visit(ConcreteElement2 ce2);
}
/**
* 具体元素
*/
public class ConcreteElement1 extends Element {
/**
* 接受操作
* @param vi
*/
@Override
public void accept(Visitor vi) {
vi.visit(this);
}
/**
* 业务逻辑方法
*/
public void operation(){
System.out.println("访问元素1");
}
}
/**
* 具体元素
*/
public class ConcreteElement2 extends Element{
/**
* 接受操作
* @param vi
*/
@Override
public void accept(Visitor vi) {
vi.visit(this);
}
/**
* 业务逻辑方法
*/
public void operation(){
System.out.println("访问元素2");
}
}
/**
* 具体访问者
*/
public class ConcreteVisitor implements Visitor{
/**
* 访问元素1
* @param ce1
*/
@Override
public void visit(ConcreteElement1 ce1) {
ce1.operation();
}
/**
* 访问元素2
* @param ce2
*/
@Override
public void visit(ConcreteElement2 ce2) {
ce2.operation();
}
}
import java.security.SecureRandom;
import java.util.Vector;
/**
* 结构角色
*/
public class ObjectStructure {
private Vector<Element> elements;
public ObjectStructure() {
this.elements = new Vector<Element>();
}
/**
* 执行访问操作
* @param vi
*/
public void action(Visitor vi){
for(Element e : elements){
e.accept(vi);
}
}
/**
* 添加新元素
*/
public void add(Element e){
elements.add(e);
}
/**
* 元素生成器,这里通过一个工厂方法进行模拟
*/
public void createElements(){
SecureRandom random = new SecureRandom();
for(int i = 0; i < 10; i++){
if(random.nextInt(100) > 50){
/**
* 添加元素1
*/
this.add(new ConcreteElement1());
}else{
/**
* 添加元素2
*/
this.add(new ConcreteElement2());
}
}
}
}
/**
* 场景应用
*/
public class Client {
public static void main(String[] args){
/**
* 创建一个结构对象
*/
ObjectStructure os = new ObjectStructure();
/**
* 生成元素
*/
os.createElements();
/**
* 创建一个访问者对象
*/
Visitor visitor = new ConcreteVisitor();
/**
* 访问者对结构进行访问(执行访问)
*/
os.action(visitor);
}
}
# 控制台输出:
访问元素2
访问元素2
访问元素2
访问元素2
访问元素1
访问元素2
访问元素2
访问元素2
访问元素2
访问元素1
二、访问者模式的优缺点
访问者模式的优点:
■ 访问者模式使得增加新的操作变得很容易,增加新的操作只需要增加新的访问者类;
■ 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个元素类中;
■ 访问者模式可以跨过几个类的等级结构访问属于不同等级结构的成员类;
■ 每一个单独的访问者对象都集中了相关的行为,将执行操作的状态积累在自己内部,益于系统的维护;
访问者模式的缺点:
■ 增加新的元素类变得很困难;
每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作;
■ 破坏封装;
访问者模式要求访问者对象访问并调用每一个元素对象的操作,这隐含了一个对所有元素对象的要求,即必须暴露一些自己的操作和内部状态,否则访问者的访问就变得没有意义。由于访问者对象自己会积累访问操作所需的状态,从而使得这些状态不再存储在元素对象中,破坏了类的封装性。
■ 违背了依赖倒置原则;
访问者依赖的是具体的元素,而不是抽象的元素,这破坏了依赖倒置的原则,特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难;
访问者模式的应用场景:
■ 一个对象结构包含很多类对象,它们有不同的接口,当对这些对象实施依赖于具体类的操作时,即使用迭代器模式不能胜任的场景下,可以采用访问者模式;
■ 需要对一个对象结构中的对象进行很多不同并且不相关的操作,避免操作污染类;
■ 业务规则要求遍历多个不同的对象,这本身也是访问者模式的出发点,迭代器模式只能访问同类或同接口的数据,而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,执行不同的操作;
三、访问者模式的实例
# 使用访问者模式完成对计算机中各种硬件的访问;

image.png
/**
* 抽象元素角色 : 计算机的各种硬件
*/
public abstract class Hardware {
/**
* 型号
*/
String type;
public Hardware(String type){
this.type = type;
}
public String getType() {
return type;
}
/**
* 运转
*/
public abstract void run();
/**
* 接受计算机访问者
* @param computerVisitor
*/
public abstract void accept(ComputerVisitor computerVisitor);
}
/**
* 计算机的CPU
*/
public class CPU extends Hardware{
public CPU(String type) {
super(type);
}
/**
* 运转
*/
@Override
public void run() {
System.out.println("型号为" + type + "的CPU在运转");
}
/**
* 接受计算机访问者
* @param computerVisitor
*/
@Override
public void accept(ComputerVisitor computerVisitor) {
computerVisitor.vistCPU(this);
}
}
/**
* 计算机的硬盘
*/
public class Harddisk extends Hardware{
public Harddisk(String type) {
super(type);
}
/**
* 运转
*/
@Override
public void run() {
System.out.println("型号为" + type + "的硬盘在运转");
}
/**
* 接受计算机访问者
* @param computerVisitor
*/
@Override
public void accept(ComputerVisitor computerVisitor) {
computerVisitor.vistHarddisk(this);
}
}
/**
* 计算机硬件的访问者
*/
public interface ComputerVisitor {
/**
* 访问CPU
* @param cpu
*/
void vistCPU(CPU cpu);
/**
* 访问硬盘
* @param harddisk
*/
void vistHarddisk(Harddisk harddisk);
}
/**
* 计算机硬件运转的访问者
*/
public class RunVisitor implements ComputerVisitor{
@Override
public void vistCPU(CPU cpu) {
cpu.run();
}
@Override
public void vistHarddisk(Harddisk harddisk) {
harddisk.run();
}
}
/**
* 计算机硬件型号的访问者
*/
public class TypeVisitor implements ComputerVisitor{
@Override
public void vistCPU(CPU cpu) {
System.out.println("CPU型号 : " + cpu.getType());
}
@Override
public void vistHarddisk(Harddisk harddisk) {
System.out.println("硬盘型号 : " + harddisk.getType());
}
}
/**
* 结构对象角色 : 计算机,由CPU和硬盘组成
*/
public class Computer {
private Hardware cpu;
private Hardware harddisk;
public Computer() {
this.cpu = new CPU("Intel Core i7-620");
this.harddisk = new Harddisk("Seagate 500G 7200转");
}
/**
* 访问
* @param computerVisitor
*/
public void accept(ComputerVisitor computerVisitor){
cpu.accept(computerVisitor);
harddisk.accept(computerVisitor);
}
}
/**
* 应用程序
*/
public class ClientDemo {
public static void main(String[] args){
Computer computer = new Computer();
ComputerVisitor typeVisitor = new TypeVisitor();
ComputerVisitor runVisitor = new RunVisitor();
computer.accept(typeVisitor);
computer.accept(runVisitor);
}
}
# 控制台输出:
CPU型号 : Intel Core i7-620
硬盘型号 : Seagate 500G 7200转
型号为Intel Core i7-620的CPU在运转
型号为Seagate 500G 7200转的硬盘在运转
参考:
摘录 《设计模式(Java版)》韩敬海主编;(微信读书APP中有资源,可以直接阅读)
网友评论