一、前言
在日常开发中,我们经常需要对一组对象执行不同的操作,但又不希望在这些对象中添加太多的职责。这时候,访问者模式(Visitor Pattern)就像一位灵活的“快递员”,带着不同的“操作包裹”,穿梭在对象之间,完成任务。
本文将用快递员送货作为Java 实战示例,一步步带你搞懂这个访问者的设计模式。
二、访问者模式
访问者模式是一种行为型设计模式,它将要执行的操作封装到访问者对象中,使得你可以在不改变数据结构的前提下,定义作用于这些结构的新操作。
换句话说:访问者把操作从对象身上“移出去”,让对象接受访问者来“访问自己”,由访问者来做事。
类比快递员
想象你是一个快递员(访问者),你每天要送快递到不同的地方:
- 到 公司,你送的是办公用品;
- 到 学校,你送的是教科书;
- 到 医院,你送的是药品。
每个地点(元素)都有不同的处理方式,但你不需要改变它们的结构,只需要实现“到访”这些地方时的具体逻辑即可。
三、结构类图表示
@startuml
interface Element {
+accept(v: Visitor): void
}
interface Visitor {
+visitCompany(c: Company): void
+visitSchool(s: School): void
}
class Company implements Element {
+accept(v: Visitor): void
}
class School implements Element {
+accept(v: Visitor): void
}
class ConcreteVisitor implements Visitor {
+visitCompany(c: Company): void
+visitSchool(s: School): void
}
@enduml
四、代码用例说明
话不多说,让我们用java代码来对例子做实际的展示。
1. Element 接口
public interface Place {
void accept(Visitor visitor);
}
2. 具体元素类:公司和学校
public class Company implements Place {
public void accept(Visitor visitor) {
visitor.visitCompany(this);
}
public String getOfficeSupplies() {
return "办公用品";
}
}
public class School implements Place {
public void accept(Visitor visitor) {
visitor.visitSchool(this);
}
public String getTextbooks() {
return "教科书";
}
}
3. Visitor 接口和实现类
public interface Visitor {
void visitCompany(Company company);
void visitSchool(School school);
}
public class DeliveryVisitor implements Visitor {
@Override
public void visitCompany(Company company) {
System.out.println("送达:" + company.getOfficeSupplies());
}
@Override
public void visitSchool(School school) {
System.out.println("送达:" + school.getTextbooks());
}
}
4. 客户端调用
import java.util.*;
public class Client {
public static void main(String[] args) {
List<Place> places = Arrays.asList(new Company(), new School());
Visitor visitor = new DeliveryVisitor();
for (Place place : places) {
place.accept(visitor);
}
}
}
5. 输出结果:
送达:办公用品
送达:教科书
五、学习总结
访问者模式的本质,就是把“逻辑”从“元素”身上移出去,用一个“访问者”类专门来处理。
只要你记住这个例子:“快递员拜访各个地点,每个地点有自己的逻辑,但快递员说了算”,你就能在设计系统时恰如其分地使用它了。
六、适用场景
- 数据结构稳定,但对其操作经常变动的系统。
- 编译器中的语法树遍历。
- 对象结构包含很多不同类,且你需要对它们执行各种无关操作时。