代理模式

一、概念

当无法直接访问某个对象或者访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象要实现相同的接口。代理模式根据其目的不同,划分为保护代理、虚拟代理、缓存代理等

代理模式引入一个代理对象,在客户对象和目标对象中之间起到一个中介的作用,去掉了客户不能看到的内容和服务或者添加客户需要的额外的新服务。

定义:给某一个对象提供一个代理或占位符,并由代理对象来控制原对象的访问。

二、模式结构

  • Subject(抽象主题类):声明了真实主题和代理主题的共同接口,使所有使用真实主题的都可以使用代理主题。

  • Real Subject(真实主题):实现了抽象主题中的具体业务,是代理对象所代表的真实对象,实现了真实的业务操作。

  • Proxy(代理主题):提供了与真实主题相同的接口,其中内部含有对真实主题类的引用,它可以访问、控制或者扩展真实主题。

    image-20240707223052226

public abstract class Subject
{
    public abstract void request();
}

public class RealSubject: Subject
{
    public override void request()
    {
        // 业务方法的具体实现
    }
}

public class Proxy : Subject
{
    private RealSubject _realSubject = new RealSubject();
    public void preRequest()
    {

    }
    public override void request()
    {
        preRequest();
        _realSubject.request();
        postRequest();
    }

    public void postRequest()
    {

    }
}

三、代理模式分类

代理模式根据其目的和实现方式不同可分为多种种类。

  1. 远程代理:为一个位于不同地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一个主机中,也可以在另一台主机中(如网络通信)。
  2. 虚拟代理:对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理,在真实对象创建成功之前虚拟代理扮演真实对象的替身,当真实对象创建之后虚拟对象再将用户的请求转发给真实对象(如网站加载资源,或者图片等)。
  3. 保护代理:控制对一个对象的访问,并可以给不同用户不同的访问权限。
  4. 缓存代理:为了某个目标操作的结果提供一个临时的存储空间,以便更多个客户端可以共享这些结果。
  5. 智能引用代理:当一个对象被引用时提供一些额外的操作,如将对象被调用的次数记录下路。

四、具体实现

某软件公司需实现一个收费商务信息查询系统,用户在查询之前需要通过身份验证,只有合法的用户才能进行查询业务,并在进行商务查询时记录查询日志,以便根据查询次数进行查询收费。使用代理模式来实现身份认真和日志记录等功能,而查询功能由真实对象来实现。

AccessValidator、Logger:业务类

public class AccessValidator
{
    private Dictionary<int, string> _users;

    public bool Validate(int uid, string password)
    {
        if (_users == null) _users = new Dictionary<int, string>();
        if (_users.ContainsKey(uid) && _users[uid] == password)
        {
            Console.WriteLine("验证成功");
            return true;
        }
        else
        {
            Console.WriteLine("登录失败");
            return false;
        }
    }
}

public class Logger
{
    private Dictionary<int, int> _userSearch;
    public void Log(int uid)
    {
        if (_userSearch == null) _userSearch = new Dictionary<int, int>();
        if (_userSearch.TryGetValue(uid, out int count))
        {
            _userSearch[uid] = count + 1;
            Console.WriteLine("更新数据库, 用户查询次数 + 1");
        }
        else
        {
            _userSearch.Add(uid, 1);
        }
    }
}

public struct UserInfo
{
    public int uid;
    public string name;
}

Search(抽象主题类):查询接口

public interface Searcher
{
    public abstract UserInfo DoSearch(int uid, string password);
}

RealSearch(真实主题类):真实查询类

 public class RealSearcher : Searcher
 {
     private Dictionary<int, UserInfo> _userInfo;

     public UserInfo DoSearch(int uid, string password)
     {
         UserInfo info = new UserInfo();
         if (_userInfo == null) _userInfo = new Dictionary<int, UserInfo>();
         if (_userInfo.TryGetValue(uid, out var UserInfo))
         {
             info = UserInfo;
         }
         return info;
     }
 }

Proxy(代理类):代理查询类

  public class Proxy : Searcher
  {
      private RealSearcher _realSearcher = new RealSearcher();
      private Logger _logger;
      private AccessValidator _accessValidator;
      public UserInfo DoSearch(int uid, string password)
      {
          UserInfo userInfo = new UserInfo();
          if (Vaildate(uid, password))
          {   
              userInfo = _realSearcher.DoSearch(uid, password);
              Log(uid);
          }
          return userInfo;
      }

      public bool Vaildate(int uid, string password)
      {
          if (_accessValidator == null) _accessValidator = new AccessValidator();
          return _accessValidator.Validate(uid, password);
      }

      public void Log(int  uid)
      {
          if (_logger == null) _logger = new Logger();
          _logger.Log(uid);
      }
  }

五、优缺点

  • 优点:
    1. 分离目标对象:代理模式将代理对象与真实对象分离,降低了系统的耦合度,使系统更具有扩展性。
    2. 保护目标对象:客户端只与代理类交互,不直接接触目标对象,从而保护了目标对象的业务逻辑。
    3. 增加目标对象:代理类可以在目标对象的基础上添加新的功能。
  • 缺点:
    1. 类的个数增加:引入代理模式会增加系统的类数量,增加类的复杂性。
    2. 性能降低:在客户端和目标对象之间添加一个代理对象,可能导致请求处理速度变慢。

六、适用场景

  1. 保护目标对象: 代理模式用于限制客户端直接访问目标对象的细节。类似于租客通过中介找房东租房子,客户端只与代理类交互,不清楚目标对象的具体细节。
  2. 增强目标对象: 代理类可以在目标对象的基础上添加新的功能。例如,代理模式可以用于在访问对象前后添加额外的逻辑。
  3. 远程代理: 在网络编程中,可以使用代理服务器来缓存请求和响应,从而提高网络访问速度。
  4. 虚拟代理: 虚拟代理用于延迟加载资源,例如大型图像或视频文件,只有在需要时才真正加载资源。
  5. 智能引用: 智能引用代理用于管理资源和内存,例如引用计数、垃圾回收等。