[特殊字符] 代理模式超详细讲解 ——.NET

🌟 代理模式超详细讲解

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. 总结

记住这个核心思想

代理就是真实对象的"秘书"或"保镖",客户端通过代理来访问真实对象,代理在中间负责各种杂事,让真实对象专注于核心工作。

关键特点

  • 🎭 代理和真实对象实现相同的接口
  • 🔄 代理持有真实对象的引用
  • 🛡️ 代理在调用真实对象前后可以添加各种功能
  • 👥 客户端不知道(也不关心)用的是代理还是真实对象
相关推荐
TDengine (老段)1 小时前
TDengine 转换函数 TO_JSON 用户手册
android·大数据·数据库·json·时序数据库·tdengine·涛思数据
2301_800256111 小时前
第七章 空间存储与索引 知识点梳理3(空间填充曲线)
数据库·笔记·sql·postgresql
冰封剑心1 小时前
MiniCPM-V-2_6 (4-bit 量化)使用
java·前端·数据库
用户8356290780511 小时前
C# 高效生成 Word 表格:复杂表格创建实战指南
后端·c#
屠夫1 小时前
C# LINQ
c#
小满、2 小时前
MySQL :存储引擎原理、索引结构与执行计划
数据库·mysql·索引·mysql 存储引擎
x***13392 小时前
SQL Server 创建用户并授权
数据库·oracle
JIngJaneIL2 小时前
智慧物业|物业管理|基于SprinBoot+vue的智慧物业管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·论文·智慧物业管理系统
枫叶梨花2 小时前
一次 Kettle 中文乱码写入失败的完整排查实录
数据库·后端