👨🎓 模式名称:访问者模式(Visitor)
👦 故事背景:
小明的系统上线后,业务越来越多。统计员小李的任务是每周统计:
- 用户活跃数
- 总订单收入
- 好评评价数
但是这些业务对象(User, Order, Review)分属不同模块、由不同人维护,结构不同,统计逻辑也不同。
小李一开始采用了最直接的方法 ------ 在每个类里写统计逻辑:
❌ 没有使用访问者模式的代码(耦合高、不易扩展)
1. 各业务类都写了 analyze() 方法,分析逻辑分散在每个类中
csharp
#include <iostream>
#include <vector>
#include <string>
class User {
public:
std::string name;
int loginDays;
bool isActive() const {
return loginDays > 30;
}
void analyze(int& activeUserCount) {
if (isActive()) {
activeUserCount++;
}
}
};
class Order {
public:
int price;
void analyze(int& totalRevenue) {
totalRevenue += price;
}
};
class Review {
public:
int score;
void analyze(int& positiveReviewCount) {
if (score >= 4) {
positiveReviewCount++;
}
}
};
2. 小李的分析函数 👇
csharp
void doAnalysis(std::vector<User>& users, std::vector<Order>& orders, std::vector<Review>& reviews) {
int activeUserCount = 0;
int totalRevenue = 0;
int positiveReviewCount = 0;
for (auto& u : users) u.analyze(activeUserCount);
for (auto& o : orders) o.analyze(totalRevenue);
for (auto& r : reviews) r.analyze(positiveReviewCount);
std::cout << "活跃用户数:" << activeUserCount << std::endl;
std::cout << "总收入:" << totalRevenue << std::endl;
std::cout << "好评数:" << positiveReviewCount << std::endl;
}
❌ 这会有什么问题?
| 问题点 | 描述 |
|---|---|
| ❌ 逻辑分散 | 每个类都负责"分析"自己的数据,导致功能散落四处 |
| ❌ 扩展困难 | 如果要加一个"导出为 Excel 的功能",又要修改每个类 |
| ❌ 违背开闭原则 | 每个新的分析需求都意味着要修改所有业务类 |
| ❌ 违反单一职责 | 原本只负责数据的业务类,现在还要处理"统计、导出"等行为 |
✅ 使用访问者模式:结构统一、处理分离
🧱 抽象访问者:数据分析器
csharp
class Order;
class User;
class Review;
class AnalyticsVisitor {
public:
virtual void visit(Order* order) = 0;
virtual void visit(User* user) = 0;
virtual void visit(Review* review) = 0;
virtual ~AnalyticsVisitor() = default;
};
🧱 抽象业务对象(被访问者)
csharp
class VisitableEntity {
public:
virtual void accept(AnalyticsVisitor* visitor) = 0;
virtual ~VisitableEntity() = default;
};
🧱 具体业务对象
csharp
class Order : public VisitableEntity {
public:
int price = 20;
std::string category = "外卖";
void accept(AnalyticsVisitor* visitor) override {
visitor->visit(this);
}
};
class User : public VisitableEntity {
public:
std::string name = "张三";
int loginDays = 45;
void accept(AnalyticsVisitor* visitor) override {
visitor->visit(this);
}
};
class Review : public VisitableEntity {
public:
int score = 4;
std::string comment = "不错的服务";
void accept(AnalyticsVisitor* visitor) override {
visitor->visit(this);
}
};
🧱 具体访问者:统计分析逻辑
csharp
class DataAnalyzer : public AnalyticsVisitor {
public:
int orderCount = 0;
int totalRevenue = 0;
int activeUsers = 0;
int positiveReviews = 0;
void visit(Order* order) override {
orderCount++;
totalRevenue += order->price;
}
void visit(User* user) override {
if (user->loginDays > 30)
activeUsers++;
}
void visit(Review* review) override {
if (review->score >= 4)
positiveReviews++;
}
void report() {
std::cout << "订单数:" << orderCount << std::endl;
std::cout << "总收入:" << totalRevenue << std::endl;
std::cout << "活跃用户数:" << activeUsers << std::endl;
std::cout << "好评数:" << positiveReviews << std::endl;
}
};
🚀 使用示例
csharp
int main() {
std::vector<VisitableEntity*> system = {
new Order(), new User(), new Review(),
new Order(), new User(), new Review()
};
DataAnalyzer analyzer;
for (auto entity : system) {
entity->accept(&analyzer);
}
analyzer.report();
for (auto e : system) delete e;
return 0;
}
✅ 使用访问者模式的优势
| 优势 | 描述 |
|---|---|
| 👑 开闭原则 | 新增分析逻辑时,只加新的 Visitor 类,不动原有结构 |
| 🤝 统一接口 | 所有业务对象统一接受访问者 |
| 🧹 逻辑清晰 | 分析逻辑集中在访问者类中,业务对象职责单一 |
| 🛠️ 易扩展 | 可以创建多个访问者用于不同目的(如导出、展示) |