相信自己,请一定要相信自己
上一章简单介绍了命令模式(十七), 如果没有看过, 请观看上一章
一. 访问者模式
引用 菜鸟教程里面访问者模式介绍: https://www.runoob.com/design-pattern/visitor-pattern.html
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。
通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。
根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作
一.一 介绍
意图: 主要将数据结构与数据操作分离。
主要解决: 稳定的数据结构和易变的操作耦合问题。
何时使用: 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决: 在被访问的类里面加一个对外提供接待访问者的接口。
关键代码: 在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例: 您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项: 访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
组成角色 | 具体 | 关系 |
抽象访问者(Visitor)角色 | Action | 一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素 |
具体访问者(ConcreteVisitor)角色 | FailAction, SuccessAction | 实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么 |
抽象元素(Element)角色 | Person | 声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数 |
具体元素(ConcreteElement)角色 | Man WoMan | 实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作 |
对象结构(Object Structure)角色 | ObjectStructure | 是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现 |
二. 访问者实例
二. 一 抽象访问者 Action
public abstract class Action {
public abstract void getManResult (Man man);
public abstract void getWomanResult(WoMan woMan);
}
二.二 具体访问者实现
二.二.一 差
@Slf4j
public class FailAction extends Action{
@Override
public void getManResult(Man man) {
log.info("男生{}评价差",man.getName());
}
@Override
public void getWomanResult(WoMan woMan) {
log.info("女生评价{}差",woMan.getName());
}
}
二.二.二 优
@Slf4j
public class SuccessAction extends Action{
@Override
public void getManResult(Man man) {
log.info("男生{}评价优秀",man.getName());
}
@Override
public void getWomanResult(WoMan woMan) {
log.info("女生评价{}优秀",woMan.getName());
}
}
二.二.三 待评价
@Slf4j
public class WaitAction extends Action{
@Override
public void getManResult(Man man) {
log.info("男生{} 待评价",man.getName());
}
@Override
public void getWomanResult(WoMan woMan) {
log.info("女生{}待评价",woMan.getName());
}
}
二.三 抽象元素 Person
@EqualsAndHashCode(of = {"name"})
public abstract class Person {
private String name;
public abstract void accept(Action action);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
二.四 具体元素
二.四.一 男人
public class Man extends Person{
public Man (String name) {
super.setName(name);
}
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
二.四.二 女人
public class WoMan extends Person{
public WoMan (String name) {
super.setName(name);
}
@Override
public void accept(Action action) {
action.getWomanResult(this);
}
}
二.五 对象结构 ObjectStructure
public class ObjectStructure {
private List<Person> personList = new ArrayList<>();
public void accept( Person person) {
personList.add(person);
}
public void remove(Person person) {
personList.remove(person);
}
public void display (Action action) {
for (Person person : personList) {
person.accept(action);
}
}
}
二.六 测试
@Test
public void oneTest() {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.accept(new Man("张三"));
// 是成功的
objectStructure.display(new SuccessAction());
objectStructure.remove(new Man("张三"));
objectStructure.accept(new Man("李四"));
objectStructure.display(new FailAction());
objectStructure.remove(new Man("李四"));
objectStructure.accept(new WoMan("王二"));
objectStructure.accept(new WoMan("麻子"));
objectStructure.display(new WaitAction());
}
优点:
- 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
- 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点:
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造 成了具体元素变更比较困难
- 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.