一、基础简介
1、定义
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。
2、使用场景
- 一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
3、优缺点
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
4、模式结构分析
- Visitor(抽象访问者):抽象访问者为对象结构中每一个具体元素类ConcreteElement声明一个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型,具体访问者需要实现这些操作方法,定义对这些元素的访问操作。
- ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素。
- Element(抽象元素):抽象元素一般是抽象类或者接口,它定义一个accept()方法,该方法通常以一个抽象访问者作为参数。
- ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作。
- ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。它可以结合组合模式来实现,也可以是一个简单的集合对象,如一个List对象或一个Set对象。
二、实例实现
1、实例场景
比如说,男人和女人面对成功与失败,会有不同的反应或结论:
- 1)男人成功时,背后都有一个伟大的女人;女人成功时,背后都有一群男人
- 2)男人失败时,闷头喝酒,谁也不用劝;女人失败时,眼泪汪汪,谁也劝不了
这里,Element就是“人类”,ConcreteElementA和ConcreteElementB就是“Man”和“Woman”,而Visitor就是不同的“状态”,ConcreteVisitor就是“成功”、“失败”等具体的状态场景。
- 使用“访问者模式”,如果需要新增一个状态(比如结婚、挫折等状态),只需要增加一个ConcreteVisitor即可。
2、Element(抽象元素)
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:06
*
* @description Element(抽象元素)
*/
public abstract class Person {
// 用来获得“行为”对象的
abstract void accept(Action visitor);
}
3、ConcreteElement(具体元素)
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:11
*
* @description ConcreteElement(具体元素)
*/
public class Man extends Person {
@Override
void accept(Action visitor) {
visitor.getManReact(this);
}
}
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:11
*
* @description ConcreteElement(具体元素)
*/
public class Woman extends Person {
@Override
void accept(Action visitor) {
visitor.getWomanReact(this);
}
}
4、Visitor(抽象访问者)
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:07
*
* @description Visitor(抽象访问者)
*/
public interface Action {
// 得到Man的反应
void getManReact(Man concreteElementA);
// 得到Woman的反应
void getWomanReact(Woman concreteElementB);
}
5、双分派技术
在这里,Action的 getManReact(Man concreteElementA) 方法与 Man 里的
** void accept(Action visitor) {**
** visitor.getManReact(this);
}**方法,充分利用双分派技术,实现了处理与数据结构的分离。
结合该实例的完整代码理解该技术:
- 首先,在客户端,将“具体的状态”作为参数传递给“具体的Element”,完成第一分派;
// 客户端 PersonList persons = new PersonList(); persons.add(new Man()); persons.add(new Woman()); // 成功时 Success s = new Success(); persons.display(s);
// persons里的方法 // 查看显示 public void display(Action visitor) { for (Person p : list) { p.accept(visitor); } }
- 然后,“具体的Element”类——Man,调用作为参数的“具体的状态”中的方法“void getManReact(Man concreteElementA);” ,同时将自己(this)作为参数传递进去。完成第二次分派。
public class Man extends Person { @Override void accept(Action visitor) { visitor.getManReact(this); } }
6、ConcreteVisitor(具体访问者)
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:14
*
* @description ConcreteVisitor(具体访问者)
*/
public class Failure implements Action {
@Override
public void getManReact(Man concreteElementA) {
System.out.println("男人失败时,闷头喝酒,谁也不用劝");
}
@Override
public void getWomanReact(Woman concreteElementB) {
System.out.println("女人失败时,眼泪汪汪,谁也劝不了");
}
}
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:14
*
* @description ConcreteVisitor(具体访问者)
*/
public class Success implements Action {
@Override
public void getManReact(Man concreteElementA) {
System.out.println("男人成功时,背后都有一个伟大的女人");
}
@Override
public void getWomanReact(Woman concreteElementB) {
System.out.println("女人成功时,背后都有一群男人");
}
}
7、ObjectStructure(对象结构):对象结构是一个元素的集合
package com.mfc.design.访问者模式;
import java.util.ArrayList;
import java.util.List;
/**
* @author MouFangCai
* @date 2019/10/28 16:20
*
* @description ObjectStructure(对象结构):对象结构是一个元素的集合
*/
public class PersonList {
private List<Person> list = new ArrayList<>();
public void add(Person element) {
list.add(element);
}
public void delete(Person element) {
list.remove(element);
}
// 查看显示
public void display(Action visitor) {
for (Person p : list) {
p.accept(visitor);
}
}
}
8、客户端
package com.mfc.design.访问者模式;
/**
* @author MouFangCai
* @date 2019/10/28 16:23
* @description
*/
public class Client_visitor {
public static void main(String[] args) {
PersonList persons = new PersonList();
persons.add(new Man());
persons.add(new Woman());
// 成功时
Success s = new Success();
persons.display(s);
// 失败时
Failure f = new Failure();
persons.display(f);
}
}
9、结果展示
男人成功时,背后都有一个伟大的女人
女人成功时,背后都有一群男人
男人失败时,闷头喝酒,谁也不用劝
女人失败时,眼泪汪汪,谁也劝不了Process finished with exit code 0