简介
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.
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
访问者模式(Visitor Pattern)是一种将数据结构与数据操作分离的设计模式。其基本思想是:对于系统中拥有固定类型数的对象结构(元素),可以通过在其内提供一个 accept 方法用来接受访问者对象的访问。不同的访问者对同一元素的访问内容不同,使得相同的元素集合可以产生不同的数据结果。解耦了数据结构与操作,且数据操作不会改变元素状态。
访问者模式 解耦数据结构与数据操作原理:元素内部提供一个 accept 方法,该方法可以接收不同的访问者对象,然后在内部将自己(元素)转发到接收到的访问者对象的 visit 方法内。访问者内部对应类型的 visit 方法就会得到回调执行,对元素进行操作。也就是通过两次动态分发(第一次是对访问者的分发accept
,第二次是对元素的分发visit
),才最终将一个具体的元素传递到一个具体的访问者。
访问者模式 核心:结构数据结构与数据操作,使得对元素的操作具备优秀的扩展性。可以通过扩展不同的数据操作类型(访问者)实现对相同元素集的不同的操作。
主要解决
当系统中存在类型数目稳定(固定)的一类数据结构时,可以通过 访问者模式 方便地实现对该类型所有数据结构的不同操作,而又不会数据产生任何副作用(脏数据)。
简单理解:想对集合中的不同类型数据(类型数量稳定)进行多种操作,则使用 访问者模式。
优缺点
优点
- 解耦了数据结构与数据操作,使得操作集合可以独立变化;
- 扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作;
- 元素类型无需一致,访问者均可操作;
- 各角色职责分离,符合 单一职责原则;
缺点
- 无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进来,则访问者类必须增加对应元素类型的操作,违背了 开闭原则 ;
- 具体元素变更困难:具体元素增加属性,删除属性等操作会导致对应的访问者类需要进行相应的修改,尤其当有大量访问者类时,修改范围太大;
- 违背 依赖倒置原则:为了达到”区别对待“,访问者依赖的是具体元素类型,而不是抽象;
使用场景
- 数据结构稳定,作用于数据结构的操作经常变化的场景;
- 需要数据结构与数据操作分离的场景;
- 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景;
模式讲解
首先来看下 访问者模式 的通用 UML 类图:
访问者模式从 UML 类图中,我们可以看到,访问者模式 主要包含五种角色:
-
抽象访问者(Visitor):接口或抽象类,该类地冠以了对每一个具体元素(Element)的访问行为
visit
,其参数就是具体的元素(Element)对象。理论上来说,Visitor 的方法个数与元素(Element)个数是相等的。如果元素(Element)个数经常变动,会导致 Visitor 的方法也要进行变动,此时,该情形并不适用 访问者模式; - 具体访问者(ConcreteVisitor):实现对具体元素的操作;
-
抽象元素(Element):接口或抽象类,定义了一个接受访问者访问的方法
accept
,表示所有元素类型都支持被访问者访问; -
具体元素(Concrete Element):具体元素类型,提供接受访问者的具体实现。通常的实现都为:
visitor.visit(this)
; - 结构对象(ObjectStruture):该类内部维护了元素集合,并提供方法接受访问者对该集合所有元素进行操作;
以下是 访问者模式 的通用代码:
class Client {
public static void main(String[] args) {
ObjectStructure collection = new ObjectStructure();
System.out.println("ConcreteVisitorA handle elements:");
IVisitor visitorA = new ConcreteVisitorA();
collection.accept(visitorA);
System.out.println("------------------------------------");
System.out.println("ConcreteVisitorB handle elements:");
IVisitor visitorB = new ConcreteVisitorB();
collection.accept(visitorB);
}
// 抽象元素
interface IElement {
void accept(IVisitor visitor);
}
// 具体元素
static class ConcreteElementA implements IElement {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public String operationA() {
return this.getClass().getSimpleName();
}
}
// 具体元素
static class ConcreteElementB implements IElement {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public int operationB() {
return new Random().nextInt(100);
}
}
// 抽象访问者
interface IVisitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
// 具体访问者
static class ConcreteVisitorA implements IVisitor {
@Override
public void visit(ConcreteElementA element) {
String result = element.operationA();
System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
}
@Override
public void visit(ConcreteElementB element) {
int result = element.operationB();
System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
}
}
// 具体访问者
static class ConcreteVisitorB implements IVisitor {
@Override
public void visit(ConcreteElementA element) {
String result = element.operationA();
System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
}
@Override
public void visit(ConcreteElementB element) {
int result = element.operationB();
System.out.println(String.format("result from %s: %s", element.getClass().getSimpleName(), result));
}
}
// 结构对象
static class ObjectStructure {
private List<IElement> mList = new ArrayList<>();
{
this.mList.add(new ConcreteElementA());
this.mList.add(new ConcreteElementB());
}
public void accept(IVisitor visitor) {
for (IElement element : this.mList) {
element.accept(visitor);
}
}
}
}
运行结果如下:
ConcreteVisitorA handle element:
result from ConcreteElementA: ConcreteElementA
result from ConcreteElementB: 58
------------------------------------
ConcreteVisitorB handle element:
result from ConcreteElementA: ConcreteElementA
result from ConcreteElementB: 83
举个例子
例子:假设现 NBA 某球队近期将对各个球员能力进行统计,统计表同时提供给教练和球队老板查看。假设教练只对球员身体素质,场均得分感兴趣,而老板对球员身价,粉丝数量感兴趣。请用代码进行实现。
分析:为了简单,我们假设支队球员 A 和球员 B 进行能力统计。则题目相当于是统计 A 和 B 的相关 数据 提交给教练和老板 查看。也就是对数据和对数据的操作,非常适合使用 访问者模式 进行实现。球员A 和 B 相当于元素(Element),教练和老板相当于访问者(Visitor),球队经理相当于结构对象,负责统计数据并提交。
具体代码如下:
class Client {
public static void main(String[] args) {
Manager manager = new Manager();
System.out.println("Coach look up:");
IViewer coach = new Coach();
manager.show(coach);
System.out.println("--------------------");
System.out.println("Boss look up:");
IViewer boss = new Boss();
manager.show(boss);
}
static abstract class Player {
// 姓名
private String mName;
// 身体素质
private String mFitness;
// 场均得分
private int mScorePerGame;
// 粉丝数量
private int mFans;
// 身价
private int mSalary;
public Player(String name, int salary, String fitness, int scorePerGame, int fans) {
this.mName = name;
this.mSalary = salary;
this.mFitness = fitness;
this.mScorePerGame = scorePerGame;
this.mFans = fans;
}
public String getName() {
return this.mName;
}
public String getFitness() {
return this.mFitness;
}
public int getScorePerGame() {
return this.mScorePerGame;
}
public int getFans() {
return this.mFans;
}
public int getSalary() {
return this.mSalary;
}
abstract void accept(IViewer viewer);
}
static class PlayerA extends Player {
public PlayerA(String name, int salary, String fitness, int scorePerGame, int fans) {
super(name, salary, fitness, scorePerGame, fans);
}
@Override
public void accept(IViewer viewer) {
viewer.show(this);
}
}
static class PlayerB extends Player {
public PlayerB(String name, int salary, String fitness, int scorePerGame, int fans) {
super(name, salary, fitness, scorePerGame, fans);
}
@Override
public void accept(IViewer viewer) {
viewer.show(this);
}
}
interface IViewer {
void show(PlayerA player);
void show(PlayerB player);
}
static class Coach implements IViewer {
@Override
public void show(PlayerA player) {
this.getInterested(player);
}
@Override
public void show(PlayerB player) {
this.getInterested(player);
}
private void getInterested(Player player) {
String name = player.getName();
String fitness = player.getFitness();
int scorePerGame = player.getScorePerGame();
int fans = player.getFans();
System.out.println(String.format("%s [body: %s,scorePerGame: %d]", name, fitness, scorePerGame));
}
}
static class Boss implements IViewer {
@Override
public void show(PlayerA player) {
this.getInterested(player);
}
@Override
public void show(PlayerB player) {
this.getInterested(player);
}
private void getInterested(Player player) {
String name = player.getName();
int salary = player.getSalary();
int fans = player.getFans();
System.out.println(String.format("%s [salary: $%d ,fans: %d]", name, salary, fans));
}
}
static class Manager {
private List<? extends Player> mPlayers = Arrays.asList(
new PlayerA("John", 2000000, "good", 20, 500000),
new PlayerA("Bill", 20000000, "excellent", 30, 15000000));
public void show(IViewer viewer) {
for (Player player : this.mPlayers) {
player.accept(viewer);
}
}
}
}
运行结果如下:
Coach look up:
John [body: good,scorePerGame: 20]
Bill [body: excellent,scorePerGame: 30]
--------------------
Boss look up:
John [salary: $2000000 ,fans: 500000]
Bill [salary: $20000000 ,fans: 15000000]
网友评论