用于将数据结构与数据操作分离,使得可以在不修改数据结构的情况下,定义新的操作。访问者模式的核心思想是,将数据结构和操作进行解耦,从而使得新增操作时不必修改数据结构,只需添加新的访问者。主要目的是在不改变数据结构的情况下,为数据结构中的元素添加新的操作,从而满足开闭原则(对扩展开放,对修改封闭)
访问者模式使用场景
- 数据结构稳定,但操作频繁变化
当数据结构相对稳定,但经常需要新增或修改不同类型的操作时,访问者模式可以将操作与数据结构分离,使得新增操作不影响现有的数据结构 - 操作多态性
当存在一组不同类型的元素,每个元素需要支持不同类型的操作时,访问者模式可以将操作封装成具体访问者类,每个具体访问者类负责一种操作 - 避免污染数据结构
当希望在不改变现有数据结构的前提下,向数据结构中添加新的操作时,访问者模式可以避免污染数据结构。 - 扩展性要求高
当需要添加新类型的元素或操作时,访问者模式使得扩展变得相对容易,只需要新增具体元素类和具体访问者类。 - 解耦操作和数据结构
当数据结构和操作之间的耦合度较高时,访问者模式可以解耦这两者,使得各自可以独立变化。 - 复杂的对象结构
当对象结构非常复杂,且其中的元素类型较多时,通过访问者模式可以将不同类型的操作分布到不同的具体访问者类中,使代码更加清晰
访问者模式的主要角色
- 访问者(Visitor):
定义了对数据结构中各个元素进行访问的抽象方法,每个方法对应一个具体元素的操作。访问者接口可能会定义多个访问方法,每个方法处理不同类型的元素。 - 具体访问者(Concrete Visitor):
实现了访问者接口中定义的方法,对具体的元素进行操作。 - 元素(Element):
定义了一个accept方法,该方法接受一个访问者对象作为参数,将自身传递给访问者,使访问者可以对自己进行操作。 - 具体元素(Concrete Element):
实现了元素接口的accept方法,将自身传递给访问者,以便访问者可以对自己进行操作。 - 对象结构(Object Structure):
维护一个元素的集合,提供了迭代元素的方法,通常可以使用集合类来实现。
java代码实例
元素
java
public interface Animal {
void accept(AnimalVisitor animalVisitor );
}
具体元素
java
// 具体动物类
class Lion implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitLion(this);
}
}
class Elephant implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitElephant(this);
}
}
class Dolphin implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visitDolphin(this);
}
}
访问者接口
java
public interface AnimalVisitor {
//拜访狮子
void visitLion(Lion lion);
//拜访大象
void visitDolphin(Dolphin dolphin);
//拜访海豚
void visitElephant(Elephant elephant);
}
具体访问者
java
//动物清洁工
public class AnimalCleaningVisitor implements AnimalVisitor{
@Override
public void visitLion(Lion lion) {
System.out.println("cleaning the lion's enclosure");
}
@Override
public void visitDolphin(Dolphin dolphin) {
System.out.println("cleaning the dolphin's enclosure");
}
@Override
public void visitElephant(Elephant elephant) {
System.out.println("cleaning the elephant's enclosure");
}
}
//动物饲养员
public class AnimalFeedingVisitor implements AnimalVisitor{
@Override
public void visitLion(Lion lion) {
System.out.println("Feeding the lion's,ying-ying-ying");
}
@Override
public void visitDolphin(Dolphin dolphin) {
System.out.println("Feeding the dolphin's,ying-ying-ying");
}
@Override
public void visitElephant(Elephant elephant) {
System.out.println("Feeding the elephant's,ying-ying-ying");
}
}
客户端
java
public static void main(String[] args) {
Animal[] animals = new Animal[]{ new Lion(), new Elephant(), new Dolphin() };
AnimalVisitor feedingVisitor = new AnimalFeedingVisitor();
AnimalVisitor cleaningVisitor = new AnimalCleaningVisitor();
for (Animal animal : animals) {
animal.accept(feedingVisitor);
animal.accept(cleaningVisitor);
}
}
输出
java
Feeding the lion's,ying-ying-ying
cleaning the lion's enclosure
Feeding the elephant's,ying-ying-ying
cleaning the elephant's enclosure
Feeding the dolphin's,ying-ying-ying
cleaning the dolphin's enclosure
访问者模式优缺点
访问者模式适用于某些特定的场景,特别是在操作和数据结构之间的耦合性较高,且数据结构相对稳定的情况下。在使用访问者模式时,需要权衡其优点和缺点,并根据实际需求进行选择。
优点
- 新增操作容易
访问者模式使得新增操作变得相对容易,只需要新增一个新的访问者类,而无需修改现有的元素类。 - 解耦操作和数据结构
访问者模式将操作(访问者类)与数据结构(元素类)解耦,使得操作可以独立变化,不影响数据结构 - 增加灵活性
可以在不改变数据结构的情况下,对数据结构中的元素进行不同类型的操作,从而增加了灵活性。 - 符合单一职责原则
访问者模式将具体操作封装到具体访问者类中,符合单一职责原则,使得每个类的职责更加清晰。 - 适用于稳定的数据结构
访问者模式适用于数据结构相对稳定,而操作需要频繁变化的场景
缺点
- 增加类的数量
访问者模式引入了多个具体访问者类,可能会增加类的数量,使得代码变得复杂 - 破坏封装性
访问者模式需要元素类暴露接受访问者的方法,这可能会破坏元素类的封装性 - 增加扩展难度
新增一种元素类型时,需要修改所有具体访问者类,这可能会增加扩展的难度。 - 不适用于元素变化频繁的场景
如果元素的类型经常变化,频繁新增元素,使用访问者模式可能会增加维护成本 - 不适用于简单场景
在简单的场景下,使用访者模式可能会引入不必要的复杂性