访问者模式

定义

提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作,属于行为型模式(创建型、结构型、行为型)

解决问题

操作复杂对象结构,将数据与操作分离。稳定的数据结构和易变的操作耦合问题。

何时使用

需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。

应用实例

在医生开具处方单(药单)后,很多医院都存在如下处理流程:

  1. 划价人员拿到 处方单 之后根据 药品名称和数量 计算总价
  2. 药房工作人员根据 药品名称和数量 准备药品

  1. 我们可以将处方单看成一个药品信息的集合,里面包含了一种或多种不同类型的药品信息。
  2. 不同类型的工作人员(如划价人员和药房工作人员)在操作同一个药品信息集合时将提供不同的处理方式,而且可能还会增加新类型的工作人员来操作处方单。

如何解决

在被访问的类里面加一个对外提供接待访问者的接口。

类图

在访问者模式结构图中包含如下几个角色:

●Vistor(抽象访问者):抽象访问者为对象结构中每一个具体元素类ConcreteElement声明一个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型,具体访问者需要实现这些操作方法,定义对这些元素的访问操作。

●ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素。

●Element(抽象元素):抽象元素一般是抽象类或者接口,它定义一个accept()方法,该方法通常以一个抽象访问者作为参数。【稍后将介绍为什么要这样设计。】

●ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作。

● ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。它可以结合组合模式来实现,也可以是一个简单的集合对象,如一个List对象或一个Set对象。

关键代码

在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

类结构

抽象元素 药

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ytf.pattern.visitor.element;

import com.ytf.pattern.visitor.visitor.MedicalStaff;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/
public abstract class AbstractMedicine {
protected int price;
protected String name;

public int getPrice() {
return price;
}

public AbstractMedicine setPrice(int price) {
this.price = price;
return this;
}

public String getName() {
return name;
}

public AbstractMedicine setName(String name) {
this.name = name;
return this;
}

public abstract void accept(MedicalStaff medicalStaff);
}

具体元素

感冒药

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.ytf.pattern.visitor.element.impl;

import com.ytf.pattern.visitor.element.AbstractMedicine;
import com.ytf.pattern.visitor.visitor.MedicalStaff;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/
public class ColdMedicine extends AbstractMedicine {

@Override
public void accept(MedicalStaff medicalStaff) {
medicalStaff.visit(this);
}

public ColdMedicine(){
super();
this.name = "感冒药";
this.price = 10;
}
}

晕车药

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.ytf.pattern.visitor.element.impl;

import com.ytf.pattern.visitor.element.AbstractMedicine;
import com.ytf.pattern.visitor.visitor.MedicalStaff;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/
public class CarSickMedicine extends AbstractMedicine {
@Override
public void accept(MedicalStaff medicalStaff) {
medicalStaff.visit(this);
}

public CarSickMedicine(){
this.name = "晕车药";
this.price = 20;
}
}

对象结构

药单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.ytf.pattern.visitor.element.impl;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/

import com.ytf.pattern.visitor.element.AbstractMedicine;
import com.ytf.pattern.visitor.visitor.MedicalStaff;

import java.util.ArrayList;
import java.util.List;

/**
* 药单
*/
public class Prescription extends AbstractMedicine {

private List<AbstractMedicine> medicines = new ArrayList<>();

@Override
public void accept(MedicalStaff medicalStaff) {
medicines.forEach(medicine -> {
medicine.accept(medicalStaff);
});
}

public Prescription(){
ColdMedicine coldMedicine = new ColdMedicine();
CarSickMedicine carSickMedicine = new CarSickMedicine();
medicines.add(coldMedicine);
medicines.add(carSickMedicine);
}
}

抽象访问者 医护人员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ytf.pattern.visitor.visitor;

import com.ytf.pattern.visitor.element.impl.CarSickMedicine;
import com.ytf.pattern.visitor.element.impl.ColdMedicine;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/

/**
* 医护人员
*/
public interface MedicalStaff {
void visit(ColdMedicine coldMedicine);
void visit(CarSickMedicine carSickMedicine);
}

具体访问者

划价人员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ytf.pattern.visitor.visitor.impl;

import com.ytf.pattern.visitor.element.impl.CarSickMedicine;
import com.ytf.pattern.visitor.element.impl.ColdMedicine;
import com.ytf.pattern.visitor.visitor.MedicalStaff;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/
public class Pricing implements MedicalStaff {
@Override
public void visit(ColdMedicine coldMedicine) {
System.out.println("划价:" + coldMedicine.getName() + ",价格:" + coldMedicine.getPrice());
}

@Override
public void visit(CarSickMedicine carSickMedicine) {
System.out.println("划价:" + carSickMedicine.getName() + ",价格:" + carSickMedicine.getPrice());
}
}

取药人员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ytf.pattern.visitor.visitor.impl;

import com.ytf.pattern.visitor.element.impl.CarSickMedicine;
import com.ytf.pattern.visitor.element.impl.ColdMedicine;
import com.ytf.pattern.visitor.visitor.MedicalStaff;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/

/**
* 药房取药
*/
public class Pharmacy implements MedicalStaff {
@Override
public void visit(ColdMedicine coldMedicine) {
System.out.println("取药:" + coldMedicine.getName());
}

@Override
public void visit(CarSickMedicine carSickMedicine) {
System.out.println("取药:" + carSickMedicine.getName());
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ytf.pattern.visitor;

import com.ytf.pattern.visitor.element.impl.Prescription;
import com.ytf.pattern.visitor.visitor.impl.Pharmacy;
import com.ytf.pattern.visitor.visitor.impl.Pricing;

/**
* Created by yutianfang
* DATE: 18/3/18星期日.
*/
public class Client {
public static void main(String[] args){
// 药单
Prescription prescription = new Prescription();

// 划价
Pricing pricing = new Pricing();
prescription.accept(pricing);

// 取药
Pharmacy pharmacy = new Pharmacy();
prescription.accept(pharmacy);
}
}

输出

优点

  1. 符合单一职责原则。
  2. 优秀的扩展性。
  3. 灵活性。

缺点

  1. 具体元素对访问者公布细节,违反了迪米特原则。
  2. 具体元素变更比较困难。 增加具体元素时,访问者也需要添加。
  3. 违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

使用场景

  1. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。
打钱! 打钱! 打钱😡😡😡