夸克之书

  • 首页
  • 科普
  • 笔记
  • .NET/C#
  • 物联网
  • 算法
  • Linux
  • 树莓派
夸克之内,别有洞天
  1. 首页
  2. .NET/C#
  3. 正文

23种常见的设计模式(6):代理(委托)模式

2020-01-13 6664点热度 0人点赞 0条评论

正如其名,代理模式就是为其他对象提供一种代理以控制对这个对象的访问。理解起来很简单,就是提供一个代理对象,让代理对象代理执行实际对象中的方法。说穿了,代理类就相当于我们生活中的中介。而本篇将分为两个小节,分别是静态代理和动态代理。通过这两个小节,循序渐进的学习代理模式。

一、静态代理

举个简单的例子。在打官司的过程中,我们是原告。我们要请个律师来代理我们进行辩护,而这个律师就是我们的代理人。 (代理律师:根据当事人的委托代表当事人进行法律活动的代理人。)
PS:我个人也没打过官司,具体流程也不是很清楚,大概明白就行。

下面我们来实现以上的这个过程。首先是具体的角色类,也就是我们的原告(真正干活的人)。

定义一个接口,指定一个Say方法。表示原告是要说话的,不能当哑巴。

public interface IUser
{
    void Say();
}

继承IUser,实现Say方法。

/// <summary>
/// 原告
/// </summary>
public class PlaintiffUser : IUser
{
    private string _name;

    public PlaintiffUser(string name)
    {
        _name = name;
    }

    public virtual void Say()
    {
        Console.WriteLine($"{_name}在讲述事实。(其实是代理人帮{_name}说的)");
    }
}

然后是我们的代理律师,他将代理我们说话,毕竟人家是专业的。和原告一样的,律师也要说话,如果律师都不帮我们说话了,那岂不是凉凉了。所以律师也要继承IUser类,并实现Say方法。

/// <summary>
/// 代理律师
/// </summary>
public class ProxyLawyer : IUser
{
    private IUser _myUser = null;

    public ProxyLawyer(string name)
    {
        if (string.IsNullOrEmpty(name))
            throw new Exception("被代理人姓名不能为空。");
        _myUser = new PlaintiffUser(name);
    }

    public void Say()
    {
        Console.WriteLine($"在说话之前已经进行过了认真的思考。");
        _myUser.Say();
    }
}

有了原告,有了律师。再来编写场景类。

/// <summary>
/// 代理模式
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    string plaintiffUserName = "亚瑟";

    //静态代理
    IUser proxy = new ProxyLawyer(plaintiffUserName);
    proxy.Say();

    Console.ReadKey(false);
}
%title插图%num
静态代理运行结果

这就是一个最简单的代理模式的应用,我们通过律师(代理类)来限制了对原告(具体执行类)的访问。来看看代理模式的经典类图:

%title插图%num
代理模式通用类图

但是这样的代理还存在一个很大的问题,现实生活中,律师不可能是一对一专门为某个人服务(一般情况下),一个律师是可以为不同的对象服务的。在编写程序时,也不可能每写一个代理,都要去新建一个代理类。所以我们来修改一下代理类(律师类),让这个律师不仅仅可以代理我们进行辩护。

/// <summary>
/// 代理律师
/// </summary>
public class ProxyLawyer : IUser
{
    private IUser _myUser = null;

    public ProxyLawyer(IUser plaintiffUser)
    {
        if (plaintiffUser == null)
            throw new Exception("被代理人不能为空。");
        _myUser = plaintiffUser;
    }

    public void Say()
    {
        Console.WriteLine($"在说话之前已经进行过了认真的思考。");
        _myUser.Say();
    }
}

很简单,只需要将被代理人作为构造函数参数传入代理类即可。就相当于是告诉代理律师要代理某某人。

看到这里,很容易就能分析出代理模式的有优点。

· 职责清晰,真实对象负责真实的业务逻辑实现,代理人负责进行调用以及处理代理对象各种前期和后期工作。(注意:代理人并不是只能代理一个真实对象,只是上面的例子没有体现而已)
· 访问控制,可以对真实对象进行访问控制。

二、动态代理

其实我们不难发现,静态代理仍然不够灵活。在不同类型的的对象进行代理时,任然需要创建对应的代理类。既然有问题,那么就要解决问题,所以就有了动态代理。动态代理的核心就是不用去关心谁是代理,需要进行代理的时候,动态的创建一个即可。

%title插图%num
动态代理类图

代理模式的核心是代理人并没有对代理的对象做任何修改,只是“拦截”了代理对象的方法,在执行代理方法的前后多干了一些事而已。既然如此,我们就可以根据这种思路来实现动态代理,通过一个定义和实现一个拦截类,在执行真实类中方法时,先执行拦截对象中的方法。

还是上面那个例子,不过这次不是我们去找代理律师了。现在法院提供了法律援助服务,我们只需要将被告告到法院,由法院去为我们提供代理律师。

继承拦截器接口,重写Before或者After方法。

/// <summary>
/// 拦截器
/// </summary>
public class PlaintiffInterceptor : IInterceptor
{
    public override void Before(object @object, string MethodName, object[] Parameters)
    {
        Console.WriteLine("在说话之前已经进行过了认真的思考。");
    }

    public override void After(object @object, string MethodName, object Result, DateTime Start, DateTime End)
    {

    }
}

在情景方法中使用动态代理。

/// <summary>
/// 代理模式
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    string plaintiffUserName = "亚瑟";

    //动态代理
    IUser proxy = new DynamictProxyLoader().CreateProxyObject<PlaintiffUser>(new PlaintiffInterceptor(), plaintiffUserName);
    proxy.Say();

    Console.ReadKey(false);
}

运行一下。

%title插图%num
动态代理运行截图

通过运行结果可以看到,在执行真实类中Say方法之前执行了拦截器中的Before方法(其实也执行了After方法,不过After方法中没有代码)。动态代理类是通过Emit放射实现的,原理是继承真实类并重新构建Say,在执行基类(真实类)中Say方法前后执行拦截对象中的方法。有兴趣可以参考文末的参考链接和下载代码查看,本篇暂不讨论如何去实现动态代理。

目前非常流行的一种编程方式叫做面向切片编程(AOP),其核心就是动态代理。AOP其实是对面向对象编程的一种补充,它更关心的一个系统中横切面,比如权限控制,系统日志等。如果写过ASP.NET或ASP.NET Core的拦截器的话,就很容易明白AOP编程的思想了。

代码下载:点击下载

参考

1、Emit学习-基础篇-为动态类添加属性、构造函数、方法
2、Emit实现动态代理
3、FastIOC动态代理框架
4、什么是AOP
5、《设计模式之禅》 -秦小波

本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2020-12-13

afirefish

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论
放松一下
https://www.quarkbook.com/wp-content/uploads/2021/05/凤凰传奇-海底(Live).flac
分类
  • .NET/C#
  • Linux
  • 树莓派
  • 物联网
  • 科普
  • 笔记
  • 算法
  • 默认
最新 热点 随机
最新 热点 随机
在代码中判断龙芯新旧世界平台 Windows获取固定后缀的IPv6地址 目前为止,你可能找不到第二台支持志强的1L小主机(P350 Tiny+W-1350+ECC+双NVME+PCIE扩展)!!! iKuai(爱快)实现成都移动IPTV IPoE拨号 Linux EXT4分区误删除后数据恢复 C#连接到巴法云
在代码中判断龙芯新旧世界平台
Hyper-v 开启嵌套虚拟化方法 在Windows右键菜单中添加命令提示符 23种常见的设计模式(7):原型模式 简单开始异步编程(1) 智能语音控制中心 - 树莓派、Nanopi、Orangepi语音识别控制 夸克之内,别有洞天
最近评论
Eagle 发布于 7 个月前(10月21日) 参考博主教程成功搞定了成都移动IPTV组播转单播,电脑、手机都可以播放了。但目前有个问题,原IPTV...
rundoze 发布于 9 个月前(08月31日) 牛逼
cc21216695 发布于 2 年前(09月27日) 试了一下,加入启动项也无效,压根没有用
afirefish 发布于 3 年前(11月28日) 非常感谢,非常棒!
》随缘《 发布于 3 年前(11月20日) 最新【一键处理】方法: https://github.com/MrXhh/VSTools/rele...
书签
  • 打赏
  • 毒鸡汤
  • 米店
  • 金鱼直播间

COPYRIGHT © 2023 quarkbook.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

蜀ICP备15036129号-9

登录
注册|忘记密码?