代理模式(Proxy)

发布时间 2023-08-09 14:34:09作者: huihui_teresa

定义

为其他对象提供一种代理以控制对这个对象的访问。

结构和说明

  • Proxy:代理对象,通常具有如下功能。实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象。保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象。可以控制对具体目标对象的访问,并可以负责创建和删除它。
  • Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
  • RealSubject:具体的目标对象,真正实现目标接口要求的功能。

示例代码

/// <summary>
/// 抽象的目标接口,定义具体的目标对象和代理公用的接口
/// </summary>
public interface Subject
{
    /// <summary>
    /// 示意方法,一个抽象的请求方法
    /// </summary>
    void Request();
}

/// <summary>
/// 具体的目标对象,是真正被代理的对象
/// </summary>
public class RealSubject : Subject
{
    public void Request()
    {
        //执行具体的功能处理
    }
}

/// <summary>
/// 代理对象
/// </summary>
public class Proxy : Subject
{
    //持有被代理的具体的目标对象
    private RealSubject _realSubject = null;

    /// <summary>
    /// 构造方法,传入被代理的具体的目标对象
    /// </summary>
    /// <param name="realSubject"></param>
    public Proxy(RealSubject realSubject)
    {
        _realSubject = realSubject;
    }

    public void Request()
    {
        //在转调具体的目标对象前,可以执行一些功能处理

        //转调具体的目标对象的方法
        _realSubject.Request();

        //转掉具体的目标对象后,可以执行一些功能处理
    }

}

例子示例代码

/// <summary>
/// 定义用户数据对象的接口
/// </summary>
public interface UserModelApi
{
    void Request();
}

/// <summary>
/// 真正对象
/// </summary>
public class UserModel : UserModelApi
{
    public string UserId { get; set; }
    public string UserName { get; set; }

    //这里去数据库重新获取数据
    public void Request()
    {
        //这里写逻辑,数据库冲洗取值
        UserId = "1";
        UserName = "2";
    }
}

/// <summary>
/// 代理对象,代理用户数据对象
/// </summary>
public class Proxy : UserModelApi
{
    private UserModel _realSubject = null;

    /// <summary>
    /// 构造方法,传入被代理的具体目标对象
    /// </summary>
    /// <param name="realSubject"></param>
    public Proxy(UserModel realSubject)
    {
        _realSubject = realSubject;
    }

    //标记是否已经重新装载过数据了
    private bool _loaded = false;

    public string UserId { get; set; }
    public string DepId { get; set; }
    public void Request()
    {
        _loaded = true;
        _realSubject.Request();
    }
}

例子:如果只是访问用户编号和用户姓名的数据,不需要重新查询数据库。只有当访问这两个数据以外数据时,才需要重新查询数据库已获得完整的数据。

认识代理模式

代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象后,对客户端并没有什么影响,就跟得到了真实对象一样来使用。

当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。

正是因为有代理对象夹在客户端和被代理的真实对象中间,相当于一个中转,那么在中转的时候就有很多花招可以玩,比如,判断一下权限,如果没有足够的权限那就不给你中转了,等等。

保护代理

保护代理是一种控制对原始对象访问的代理,多用于对象应该有不同得访问权限的时候。保护代理会检查调用者是否具有请求所必须的访问权限,如果没有相应的权限,那么就不会调用目标对象,从而实现对目标对象的保护。

示例需求

现在有一个订单系统,要求是:一旦订单被创建,只有订单的创建人才可以修改订单中的数据,其他人则不能修改。

代码示例

/// <summary>
/// 订单对象的接口定义
/// </summary>
public interface OrderApi
{
    /// <summary>
    /// 获取产品名称
    /// </summary>
    /// <returns></returns>
    string GetProductName();

    /// <summary>
    /// 设置产品名称
    /// </summary>
    /// <param name="productName"></param>
    /// <param name="user"></param>
    void SetProductName(string productName, string user);

    /// <summary>
    /// 获取订单订购的数量
    /// </summary>
    /// <returns></returns>
    int GetOrderNum();

    /// <summary>
    /// 设置订单订购的数量
    /// </summary>
    /// <param name="orderNum"></param>
    /// <param name="user"></param>
    void SetOrderNum(int orderNum, string user);

    /// <summary>
    /// 获取创建订单的人员
    /// </summary>
    /// <returns></returns>
    string GetOrderUser();

    /// <summary>
    /// 设置创建订单的人员
    /// </summary>
    /// <param name="orderUser"></param>
    /// <param name="user"></param>
    void SetOrderUser(string orderUser, string user);
}

/// <summary>
/// 订单对象
/// </summary>
public class Order : OrderApi
{
    //产品名称
    private string _productName;
    //订单采购数量
    private int _orderNum;
    //创建人
    private string _orderUser;

    public Order(string productName,int orderNum,string orderUser)
    {
        _productName = productName;
        _orderNum = orderNum;
        _orderUser = orderUser;
    }

    public string GetProductName()
    {
        return _productName;
    }

    public void SetProductName(string productName, string user)
    {
        _productName = productName;
    }

    public int GetOrderNum()
    {
        return _orderNum;
    }

    public void SetOrderNum(int orderNum, string user)
    {
        _orderNum = orderNum;
    }

    public string GetOrderUser()
    {
        return _orderUser;
    }

    public void SetOrderUser(string orderUser, string user)
    {
        _orderUser = orderUser;
    }
}

/// <summary>
/// 订单的代理对象
/// </summary>
public class OrderProxy : OrderApi
{
    //被代理的具体的目标对象
    private Order order = null;
    public OrderProxy(Order realSubject)
    {
        order = realSubject;
    }

    public string GetProductName()
    {
        throw new NotImplementedException();
    }

    public void SetProductName(string productName, string user)
    {
        //控制访问权限,只有订单创建人才能修改(其他类似)
        if(user==GetOrderUser())
            order.SetProductName(productName,user);
        else
        {
            Console.WriteLine($"对不起{user},您无权限修改订单名称!");
        }
    }

    public int GetOrderNum()
    {
        throw new NotImplementedException();
    }

    public void SetOrderNum(int orderNum, string user)
    {
        throw new NotImplementedException();
    }

    public string GetOrderUser()
    {
        throw new NotImplementedException();
    }

    public void SetOrderUser(string orderUser, string user)
    {
        throw new NotImplementedException();
    }
}

思考代理模式

代理模式的本质:控制对象的访问。

何时选用代理模式

  • 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理
  • 需要按照需要创建开销很大的对象的时候,可以使用虚代理。
  • 需要控制对原始对象的访问的时候,可以使用保护代理。
  • 需要在访问对象执行一些附加操作的时候,可以使用智能指引代理。