👨🎓 模式名称:代理模式(Proxy Pattern)
🧵 故事背景
小明的“校园万能生活平台”App上线后,大受欢迎!
然而没几天,他发现服务器响应变慢,数据库压力暴涨。查日志发现——
👾 有一群爬虫在疯狂请求接口!
GET /api/user_info?id=12345
GET /api/class_schedule
GET /api/favoritelist
...
小明都快疯了 😵💫!
于是,他请来了“小明安全屋团队”,做了两件事:
- 所有App API请求,都必须经过代理访问后端服务
- 在代理中加入身份验证、访问计数、缓存机制等功能
于是,“代理模式”就派上用场了!
🏗 没有代理模式(❌ 不加控制,裸奔服务器)
class RealServerAPI {
public:
void getUserInfo(const std::string& userId) {
std::cout << "📡 获取用户信息:" << userId << std::endl;
}
};
客户端直接调用:
RealServerAPI api;
api.getUserInfo("xiaoming123");
🔥 没有身份校验、没有日志、不能限速,爬虫来了你就完蛋!
不使用代理模式做限制的完整代码
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
class UserService {
public:
std::string getUserInfo(const std::string& userId) {
// 权限校验
if (blacklist.find(userId) != blacklist.end()) {
return "⛔ 你被禁止访问";
}
// 访问计数
accessCount[userId]++;
std::cout << "👀 用户 " << userId << " 已访问 " << accessCount[userId] << " 次\n";
// 缓存逻辑
if (cache.find(userId) != cache.end()) {
std::cout << "📦 从缓存中返回用户信息\n";
return cache[userId];
}
// 模拟数据库查询
std::string info = "📡 用户信息:" + userId;
cache[userId] = info;
return info;
}
private:
std::unordered_set<std::string> blacklist = { "hacker" };
std::unordered_map<std::string, int> accessCount;
std::unordered_map<std::string, std::string> cache;
};
int main() {
UserService* realService = new UserService();
// 客户端只面对 protectedService
std::cout << realService->getUserInfo("xiaoming") << "\n\n";
std::cout << realService->getUserInfo("xiaoming") << "\n\n";
std::cout << realService->getUserInfo("hacker") << "\n\n";
delete realService;
}
这里虽然做了限制,但是有如下缺点
😵💫 缺点:
- 职责太多:业务类同时负责权限校验、计数、缓存、数据处理,难以维护。
- 重复性强:每个接口都要复制这套逻辑。
- 可测试性差:单测很困难,因为功能耦合严重。
- 另外,有的时候类可能是第三方封闭库的一部分。这样我们就没办法做到上面的限制操作,因为我们没办法改别人的类
✅ 使用代理模式:增加身份验证 + 访问计数+ 缓存机制
1️⃣ 接口抽象:
class IUserService {
public:
virtual std::string getUserInfo(const std::string& userId) = 0;
virtual ~IUserService() = default;
};
2️⃣ 真实服务类:
class RealUserService : public IUserService {
public:
std::string getUserInfo(const std::string& userId) override {
return "📡 用户信息:" + userId;
}
};
3️⃣ 权限代理:
class PermissionProxy : public IUserService {
public:
PermissionProxy(IUserService* service) : service(service) {}
std::string getUserInfo(const std::string& userId) override {
if (blacklist.find(userId) != blacklist.end()) {
return "⛔ 你被禁止访问";
}
return service->getUserInfo(userId);
}
private:
IUserService* service;
std::unordered_set<std::string> blacklist = { "hacker" };
};
4️⃣ 计数代理:
class AccessCounterProxy : public IUserService {
public:
AccessCounterProxy(IUserService* service) : service(service) {}
std::string getUserInfo(const std::string& userId) override {
accessCount[userId]++;
std::cout << "👀 用户 " << userId << " 已访问 " << accessCount[userId] << " 次\n";
return service->getUserInfo(userId);
}
private:
IUserService* service;
std::unordered_map<std::string, int> accessCount;
};
5️⃣ 缓存代理:
class CacheProxy : public IUserService {
public:
CacheProxy(IUserService* service) : service(service) {}
std::string getUserInfo(const std::string& userId) override {
if (cache.find(userId) != cache.end()) {
std::cout << "📦 从缓存中返回\n";
return cache[userId];
}
std::string info = service->getUserInfo(userId);
cache[userId] = info;
return info;
}
private:
IUserService* service;
std::unordered_map<std::string, std::string> cache;
};
🏗 最终组装(链式代理):
int main() {
IUserService* realService = new RealUserService();
IUserService* cached = new CacheProxy(realService);
IUserService* counted = new AccessCounterProxy(cached);
IUserService* protectedService = new PermissionProxy(counted);
// 客户端只面对 protectedService
std::cout << protectedService->getUserInfo("xiaoming") << "\n\n";
std::cout << protectedService->getUserInfo("xiaoming") << "\n\n";
std::cout << protectedService->getUserInfo("hacker") << "\n";
delete protectedService;
delete counted;
delete cached;
delete realService;
}
完整代码
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
class IUserService {
public:
virtual std::string getUserInfo(const std::string& userId) = 0;
virtual ~IUserService() = default;
};
class RealUserService : public IUserService {
public:
std::string getUserInfo(const std::string& userId) override {
return "📡 用户信息:" + userId;
}
};
class PermissionProxy : public IUserService {
public:
PermissionProxy(IUserService* service) : service(service) {}
std::string getUserInfo(const std::string& userId) override {
if (blacklist.find(userId) != blacklist.end()) {
return "⛔ 你被禁止访问";
}
return service->getUserInfo(userId);
}
private:
IUserService* service;
std::unordered_set<std::string> blacklist = { "hacker" };
};
class AccessCounterProxy : public IUserService {
public:
AccessCounterProxy(IUserService* service) : service(service) {}
std::string getUserInfo(const std::string& userId) override {
accessCount[userId]++;
std::cout << "👀 用户 " << userId << " 已访问 " << accessCount[userId] << " 次\n";
return service->getUserInfo(userId);
}
private:
IUserService* service;
std::unordered_map<std::string, int> accessCount;
};
class CacheProxy : public IUserService {
public:
CacheProxy(IUserService* service) : service(service) {}
std::string getUserInfo(const std::string& userId) override {
if (cache.find(userId) != cache.end()) {
std::cout << "📦 从缓存中返回\n";
return cache[userId];
}
std::string info = service->getUserInfo(userId);
cache[userId] = info;
return info;
}
private:
IUserService* service;
std::unordered_map<std::string, std::string> cache;
};
int main() {
IUserService* realService = new RealUserService();
IUserService* cached = new CacheProxy(realService);
IUserService* counted = new AccessCounterProxy(cached);
IUserService* protectedService = new PermissionProxy(counted);
// 客户端只面对 protectedService
std::cout << protectedService->getUserInfo("xiaoming") << "\n\n";
std::cout << protectedService->getUserInfo("xiaoming") << "\n\n";
std::cout << protectedService->getUserInfo("hacker") << "\n";
delete protectedService;
delete counted;
delete cached;
delete realService;
}
✅ 优势总结
比较维度 | 硬编码处理 | 代理模式处理 |
---|---|---|
结构清晰 | ❌ 混乱 | ✅ 职责分明 |
易于扩展 | ❌ 修改原类代码 | ✅ 添加新代理类即可 |
测试性好 | ❌ 很难隔离测试 | ✅ 每个代理功能可独立测试 |
灵活组装 | ❌ 所有逻辑耦合 | ✅ 可按需组合代理链 |
避免重复代码 | ❌ 每个接口都要写一遍 | ✅ 写一次代理,可全局复用 |