设计模式21-Visitor

本文最后更新于:2 years ago

访问者模式

  在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于 行为型模式 。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

如果想了解访问者模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-访问者模式

结构图


优缺点

优点:

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

缺点:

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

使用场景

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

实现代码

模拟一个场景,外设店中的一套外设配置(键盘+鼠标+显示屏),不同的人去购买的这套外设厂家所给与的折扣是不一样的,对于学生的折扣比个人去购买折扣会大很多。并且购买这套配置的话,键盘、鼠标、显示器的折扣是不一样的

首先定义一个ComputerPart抽象类,这里我们将创建两个方法,accept和getPrice

1
2
3
4
abstract class ComputerPart {
abstract void accept(Visitor v);
abstract double getPrice();
}

accept()用于接收访问者,当你调用我这个方法时,需要你传入一个Visitor的对象,而这个Visitor对象已经实现了相应的方法(visitKeyboard、visitMouse、visitMonitor)
getPrice用于查看对于用户这个类(Student)打完折扣后总价为多少

接着创建Keyboard、Mouse、Monitor三个类,继承ComputerPart类,重写上面的两个类,

1
2
3
4
5
6
7
8
9
10
11
12
class Keyboard extends ComputerPart {

@Override
void accept(Visitor v) {
v.visitKeyboard(this);
}

@Override
double getPrice() {
return 1000;
}
}

对于accept对象,对于传入的Visitor对象,我们调用已经由Visitor对象自己实现的visitKeyboard()方法(Mouse、Monitor同),实现重写;getPrice我们返回外设的原价

创建一个Visitor接口,接口中创建三个类,由子类自己实现这些方法

1
2
3
4
5
6
7
interface Visitor {
void visitKeyboard(Keyboard keyboard);

void visitMouse(Mouse mouse);

void visitMonitor(Monitor monitor);
}

调用者自己实现相应的折扣,并返回总价

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StudentVisitor implements Visitor {
double totalPrice = 0.0;

@Override
public void visitKeyboard(Keyboard keyboard) {
totalPrice += keyboard.getPrice() * 0.8;
}

@Override
public void visitMouse(Mouse mouse) {
totalPrice += mouse.getPrice() * 0.85;
}

@Override
public void visitMonitor(Monitor monitor) {
totalPrice += monitor.getPrice() * 0.85;
}
}

代码如下:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class Main {
public static void main(String[] args) {
StudentVisitor student = new StudentVisitor();
new Computer().accept(student);
System.out.println(student.totalPrice);
}

}

class Computer {
ComputerPart keyboard = new Keyboard();
ComputerPart mouse = new Mouse();
ComputerPart monitor = new Monitor();

public void accept(Visitor v) {
this.keyboard.accept(v);
this.mouse.accept(v);
this.monitor.accept(v);
}
}

abstract class ComputerPart {
abstract void accept(Visitor v);
abstract double getPrice();
}

class Keyboard extends ComputerPart {

@Override
void accept(Visitor v) {
v.visitKeyboard(this);
}

@Override
double getPrice() {
return 1000;
}
}

class Mouse extends ComputerPart {

@Override
void accept(Visitor v) {
v.visitMouse(this);
}

@Override
double getPrice() {
return 800;
}
}

class Monitor extends ComputerPart {

@Override
void accept(Visitor v) {
v.visitMonitor(this);
}

@Override
double getPrice() {
return 1200;
}
}

interface Visitor {
void visitKeyboard(Keyboard keyboard);

void visitMouse(Mouse mouse);

void visitMonitor(Monitor monitor);
}

class StudentVisitor implements Visitor {
double totalPrice = 0.0;

@Override
public void visitKeyboard(Keyboard keyboard) {
totalPrice += keyboard.getPrice() * 0.8;
}

@Override
public void visitMouse(Mouse mouse) {
totalPrice += mouse.getPrice() * 0.85;
}

@Override
public void visitMonitor(Monitor monitor) {
totalPrice += monitor.getPrice() * 0.85;
}
}

class PersonVisitor implements Visitor {
double totalPrice = 0.0;

@Override
public void visitKeyboard(Keyboard keyboard) {
totalPrice += keyboard.getPrice() * 0.9;
}

@Override
public void visitMouse(Mouse mouse) {
totalPrice += mouse.getPrice() * 0.95;
}

@Override
public void visitMonitor(Monitor monitor) {
totalPrice += monitor.getPrice() * 0.95;
}
}

输出结果:

1
2500.0

Visitor模式结构是固定死的,你是不能改的,能改的也只有变化数据操作上(StudentVisitor、PersonVisitor)。所以当内部结构固定的情况下可以用Visitor模式

Visitor模式用的不多,这个模式用的地方比较窄,主要用在语言编辑器这方面(这个方向用的比较多)了解即可。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!