一、访问者模式
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
封装一些作用于某中数据结构中的各个元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新操作
二、介绍
意图:主要将数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
三、结构
访问者模式包含以下主要角色:
1、抽象访问者:定义了对每个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。
2、具体访问者角色:给出对每一个元素类访问时所产生的具体行为
3、抽象元素角色:定义一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。
4、具体元素角色:提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的该访问元素的方法。
5、对象结构角色:定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,提供访问者访问
四、代码实现
给宠物喂食:
访问者角色:给宠物喂食的人
具体访问者角色:主人、其他人
抽象元素类角色:动物抽象类
具体元素对象:宠物狗、宠物猫
结构对象角色:主人的家
定义抽象访问者:
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Person
* @Description: 抽象访问者角色类
* @Date: 2022/4/5 14:06
**/
public interface Person {
void feed(Cat cat);
void feed(Dog dog);
}
定义具访问者
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Owner
* @Description: 具体访问者角色(其他人)
* @Date: 2022/4/5 14:11
**/
public class SomeOne implements Person{
@Override
public void feed(Cat cat) {
System.out.println("其他人喂食:猫");
}
@Override
public void feed(Dog dog) {
System.out.println("其他人喂食:狗");
}
}
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Owner
* @Description: 具体访问者角色(自己)
* @Date: 2022/4/5 14:11
**/
public class Owner implements Person{
@Override
public void feed(Cat cat) {
System.out.println("主人喂食:猫");
}
@Override
public void feed(Dog dog) {
System.out.println("主人喂食:狗");
}
}
定义抽象元素角色
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Animal
* @Description: 抽象元素角色类
* @Date: 2022/4/5 14:04
**/
public interface Animal {
// 接受访问者访问的功能
void accept(Person person);
}
定义具体元素角色
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Cat
* @Description: 具体元素角色类(宠物猫)
* @Date: 2022/4/5 14:09
**/
public class Cat implements Animal{
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("好好吃:猫");
}
}
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Dog
* @Description: TODO
* @Date: 2022/4/5 14:10
**/
public class Dog implements Animal{
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("好好吃:汪汪");
}
}
定义一个对象结构类
package VP_01_Vistitor;
import java.util.*;
/**
* @Author: {LZG}
* @ClassName: Home
* @Description: 对象结构类
* @Date: 2022/4/5 14:13
**/
public class Home {
// 声明一个集合对象,用来存储元素对象
private List<Animal> list =new ArrayList<Animal>();
// 添加元素功能
public void add(Animal animal){
list.add(animal);
}
public void action(Person person){
// 遍历结合,获取每一个元素
for (Animal animal : list) {
animal.accept(person);
}
}
}
客户
package VP_01_Vistitor;
/**
* @Author: {LZG}
* @ClassName: Client
* @Description: TODO
* @Date: 2022/4/5 14:17
**/
public class Client {
public static void main(String[] args) {
// 创建Home对象
Home home = new Home();
// 添加元素到Home对象
home.add(new Dog());
home.add(new Cat());
// 创建一个主人对象
Owner owner = new Owner();
// 让主人喂食
home.action(owner);
}
}
测试结果
五、优缺点
优点:
1、扩展性好。在不修改对象结构中元素的情况下,为对象结构中的元素添加新的功能
2、复用性好。通过访问者来定义整个对象结构通用功能,从而提高复用程度
3、分离无关行为。通过方法者来分离无关的行为,把相关的行为封装在一起,构成一个访问者。这样每一个访问者的功能都比较单一
缺点:
1、对象的结构变化困难。在访问者模式中,每增加一个新的元素,都要在每一个具体访问者类中增加响应的具体操作,这违背了开闭原则。
2、违反了依赖倒置原则。访问者模式依赖具体类,而没有依赖抽象类