🌟 代理模式超详细讲解
1. 什么是代理模式?
一句话理解:代理模式就是找一个"替身"或"中介"来代替真实对象做事。
生活比喻:
- 🎬 明星经纪人 - 你想见明星?先通过经纪人!
- 🏠 房产中介 - 你想租房?通过中介!
- 📦 快递代收点 - 你不在家?快递放代收点!
2. 为什么需要代理模式?
没有代理的情况(直接访问):
csharp
// 真实对象 - 数据库
class Database
{
public void Query(string sql)
{
Console.WriteLine($"执行SQL: {sql}");
// 直接访问数据库...
}
}
// 客户端直接使用
class Program
{
static void Main()
{
var db = new Database();
db.Query("SELECT * FROM Users"); // 直接访问,没有控制
}
}
问题:没有权限检查、没有日志、没有缓存...
使用代理的情况:
csharp
// 数据库代理
class DatabaseProxy
{
private Database _realDatabase = new Database();
public void Query(string sql)
{
// 1. 权限检查
if (!CheckPermission()) return;
// 2. 记录日志
LogQuery(sql);
// 3. 缓存检查
if (CheckCache(sql)) return;
// 4. 真正执行
_realDatabase.Query(sql);
// 5. 更新缓存
UpdateCache(sql);
}
}
3. 代理模式的三要素
🎯 核心角色
csharp
// 1. 抽象主题 (Subject) - 定义规则
public interface IDatabase
{
void Query(string sql);
}
// 2. 真实主题 (Real Subject) - 真正干活的
public class RealDatabase : IDatabase
{
public void Query(string sql)
{
Console.WriteLine($"🏃♂️ 真实数据库执行: {sql}");
}
}
// 3. 代理 (Proxy) - 中介
public class DatabaseProxy : IDatabase
{
private RealDatabase _realDb; // 持有真实对象的引用
public DatabaseProxy()
{
_realDb = new RealDatabase();
}
public void Query(string sql)
{
Console.WriteLine("🛡️ 代理开始工作...");
// 在调用真实对象前后可以添加各种功能
BeforeQuery(sql);
_realDb.Query(sql); // 调用真实对象
AfterQuery(sql);
Console.WriteLine("🛡️ 代理工作完成");
}
private void BeforeQuery(string sql)
{
Console.WriteLine($" 📝 记录日志: {sql}");
Console.WriteLine(" 🔒 检查权限...");
}
private void AfterQuery(string sql)
{
Console.WriteLine(" 💾 更新缓存...");
}
}
4. 代理模式的几种类型
① 虚拟代理 - 延迟创建
csharp
// 大图片加载 - 不用立即加载,等需要时再加载
class ImageProxy
{
private RealImage _realImage;
private string _filename;
public ImageProxy(string filename)
{
_filename = filename;
Console.WriteLine($"🖼️ 创建图片代理,但还没真正加载图片");
}
public void Display()
{
// 延迟加载:第一次显示时才真正加载
if (_realImage == null)
{
Console.WriteLine("⏳ 第一次显示,开始加载真实图片...");
_realImage = new RealImage(_filename);
}
_realImage.Display();
}
}
② 保护代理 - 权限控制
csharp
// 文件访问代理 - 控制谁可以访问什么
class FileProxy
{
private RealFile _realFile;
private User _user;
public FileProxy(string filename, User user)
{
_realFile = new RealFile(filename);
_user = user;
}
public void Read()
{
if (_user.CanRead)
{
_realFile.Read();
}
else
{
Console.WriteLine("❌ 你没有读取权限!");
}
}
}
③ 缓存代理 - 提高性能
csharp
// 计算器代理 - 缓存计算结果
class CalculatorProxy
{
private RealCalculator _calculator = new RealCalculator();
private Dictionary<string, int> _cache = new Dictionary<string, int>();
public int Compute(int x, int y)
{
string key = $"{x}+{y}";
// 检查缓存
if (_cache.ContainsKey(key))
{
Console.WriteLine($"♻️ 从缓存返回结果: {_cache[key]}");
return _cache[key];
}
// 缓存中没有,真正计算
Console.WriteLine("🧮 实际计算中...");
int result = _calculator.Compute(x, y);
// 存入缓存
_cache[key] = result;
return result;
}
}
5. 完整示例:网络请求代理
让我们用一个完整的例子来理解:
csharp
// 1. 抽象主题
public interface IHttpClient
{
string Get(string url);
}
// 2. 真实主题 - 真正发送网络请求
public class RealHttpClient : IHttpClient
{
public string Get(string url)
{
Console.WriteLine($"🌐 真实网络请求: {url}");
// 模拟网络请求
Thread.Sleep(2000); // 模拟2秒延迟
return $"响应数据 from {url}";
}
}
// 3. 代理 - 智能网络客户端
public class SmartHttpProxy : IHttpClient
{
private RealHttpClient _realClient;
private Dictionary<string, string> _cache = new Dictionary<string, string>();
public string Get(string url)
{
Console.WriteLine($"🤖 智能代理处理请求: {url}");
// 1. 检查缓存
if (_cache.ContainsKey(url))
{
Console.WriteLine(" 💫 从缓存返回数据!");
return _cache[url];
}
// 2. 检查URL是否合法
if (!IsValidUrl(url))
{
Console.WriteLine(" ❌ 非法URL!");
return string.Empty;
}
// 3. 真正发送请求(延迟初始化)
if (_realClient == null)
{
_realClient = new RealHttpClient();
}
Console.WriteLine(" ⏳ 发送网络请求...");
string result = _realClient.Get(url);
// 4. 缓存结果
_cache[url] = result;
Console.WriteLine(" ✅ 请求完成并缓存");
return result;
}
private bool IsValidUrl(string url)
{
return url.StartsWith("https://");
}
}
// 使用示例
class Program
{
static void Main()
{
Console.WriteLine("=== 直接使用真实对象 ===");
IHttpClient realClient = new RealHttpClient();
var result1 = realClient.Get("https://api.example.com/data");
Console.WriteLine("\n=== 使用代理对象 ===");
IHttpClient proxyClient = new SmartHttpProxy();
// 第一次请求 - 会真正发送网络请求
var result2 = proxyClient.Get("https://api.example.com/data");
// 第二次相同请求 - 从缓存返回
var result3 = proxyClient.Get("https://api.example.com/data");
// 非法URL - 被代理拦截
var result4 = proxyClient.Get("http://bad-url.com");
}
}
输出结果:
=== 直接使用真实对象 ===
🌐 真实网络请求: https://api.example.com/data
(等待2秒...)
=== 使用代理对象 ===
🤖 智能代理处理请求: https://api.example.com/data
⏳ 发送网络请求...
🌐 真实网络请求: https://api.example.com/data
(等待2秒...)
✅ 请求完成并缓存
🤖 智能代理处理请求: https://api.example.com/data
💫 从缓存返回数据!
🤖 智能代理处理请求: http://bad-url.com
❌ 非法URL!
好的!用一个非常生活化的例子来解释代理模式:
🌟 明星与经纪人的例子
场景描述
想象一个明星(真实对象)和他的经纪人(代理)。粉丝(客户端)想联系明星,但必须通过经纪人。
csharp
// 抽象主题 - 定义明星和经纪人的共同接口
public interface IStar
{
void MeetFans(); // 见面会
void SignContract(); // 签合同
}
// 真实主题 - 明星本人
public class RealStar : IStar
{
public void MeetFans()
{
Console.WriteLine("🌟 明星亲自与粉丝见面!");
}
public void SignContract()
{
Console.WriteLine("🌟 明星亲自签署合同!");
}
}
// 代理 - 经纪人
public class AgentProxy : IStar
{
private RealStar _star;
public AgentProxy()
{
_star = new RealStar(); // 经纪人认识明星
}
public void MeetFans()
{
// 经纪人先筛选
Console.WriteLine("🤵 经纪人:先检查粉丝资格...");
Console.WriteLine("🤵 经纪人:安排见面时间...");
Console.WriteLine("🤵 经纪人:收取见面费用...");
// 符合条件的才转给明星
_star.MeetFans();
// 后续处理
Console.WriteLine("🤵 经纪人:安排保安护送...");
}
public void SignContract()
{
// 经纪人先谈判
Console.WriteLine("🤵 经纪人:审查合同条款...");
Console.WriteLine("🤵 经纪人:谈判价格...");
Console.WriteLine("🤵 经纪人:确认合作细节...");
// 谈好了才让明星签
_star.SignContract();
Console.WriteLine("🤵 经纪人:后续跟进执行...");
}
}
使用场景
csharp
class Program
{
static void Main()
{
// 粉丝想联系明星
Console.WriteLine("🎯 粉丝想见明星:");
// 粉丝接触的是经纪人(代理),不是明星本人
IStar starContact = new AgentProxy();
// 见面会
starContact.MeetFans();
Console.WriteLine("\n🎯 公司想签约:");
// 签约
starContact.SignContract();
}
}
输出结果
🎯 粉丝想见明星:
🤵 经纪人:先检查粉丝资格...
🤵 经纪人:安排见面时间...
🤵 经纪人:收取见面费用...
🌟 明星亲自与粉丝见面!
🤵 经纪人:安排保安护送...
🎯 公司想签约:
🤵 经纪人:审查合同条款...
🤵 经纪人:谈判价格...
🤵 经纪人:确认合作细节...
🌟 明星亲自签署合同!
🤵 经纪人:后续跟进执行...
🎯 代理模式在生活中的其他例子
1. 快递代收点
csharp
// 你不在家时,快递员把包裹放在代收点(代理)
// 你有空时再去代收点取(访问真实对象)
2. 银行柜台
csharp
// 你想取钱,但不是直接进金库
// 通过银行职员(代理)帮你办理
3. 餐厅服务员
csharp
// 你点菜不是直接跟厨师说
// 通过服务员(代理)传达给厨房
💡 核心思想
代理就像是一个"中间人"或"门卫":
- ✅ 控制访问 - 决定谁可以见明星
- ✅ 添加功能 - 安排时间、收费、安保等
- ✅ 简化流程 - 粉丝不用操心细节
- ✅ 保护对象 - 明星不会被随意打扰
关键点:粉丝(客户端)以为自己在跟明星交流,实际上是通过经纪人来完成的,但体验上感觉像是在直接跟明星互动!
6. 代理模式的优点
| 优点 | 说明 | 例子 |
|---|---|---|
| 控制访问 | 可以在调用真实对象前后添加控制逻辑 | 权限检查、参数验证 |
| 延迟加载 | 只有在真正需要时才创建昂贵对象 | 大图片、大文件加载 |
| 缓存功能 | 缓存结果提高性能 | 网络请求、计算结果的缓存 |
| 日志记录 | 自动记录操作日志 | 方法调用日志 |
| 职责清晰 | 真实对象只关注核心业务 | 数据库只管查询,不管权限 |
7. 什么时候使用代理模式?
✅ 使用场景:
- 需要控制对某个对象的访问时
- 需要给对象添加额外功能但又不想修改原对象时
- 需要延迟创建开销大的对象时
- 需要缓存操作结果时
❌ 不适用场景:
- 如果功能很简单,直接使用真实对象即可
- 如果代理的逻辑太复杂,可能过度设计
8. 总结
记住这个核心思想:
代理就是真实对象的"秘书"或"保镖",客户端通过代理来访问真实对象,代理在中间负责各种杂事,让真实对象专注于核心工作。
关键特点:
- 🎭 代理和真实对象实现相同的接口
- 🔄 代理持有真实对象的引用
- 🛡️ 代理在调用真实对象前后可以添加各种功能
- 👥 客户端不知道(也不关心)用的是代理还是真实对象