代理模式是什么?如何在 C# 中实现代理模式
代理模式是什么?如何在 C# 中实现代理模式
代理模式 并不是日常开发工作中常常用到的一种设计模式,也是一种不易被理解的一种设计模式。但是它会广泛的应用在系统框架、业务框架中。
定义
它的 定义 就如其它同大部分 设计模式 的定义类似,即不通俗也不易懂,而且随便百度一下就能找到 : 为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用。
每个字都认识,连在一起就看不懂了 by. 某个攻城狮
我们一个词一个词看就明白了。
其他对象
所谓的 其它,其实就是你系统中 任意 一个类型,可以是 UserService、OrderRepository、DataDeletedEventListener、等等。
控制对这个对象的访问
访问 其实就是调用这个对象上的方法、访问它的属性、设置它的属性等等,比如
User user = UserService.GetUserById(1); // 访问了 GetUserById 方法
int id = user.Id; // 访问了 Id 属性
Order order = OrderRepository.SelectByUserId(id); // 访问了 SelectByUserId 方法
控制访问 ,控制 的本质是包装,外部不再直接使用 其他对象 ,而是使用 代理 ,再由代理来访问 其它对象。我们可以使用一个已有的 List 实现一个 IList ,并在使用 Add 方法时,打印一行日志。
public class LogList : IList
{
// other code here..
private IList raw; // 这个就 "其它对象"
public EventList(IList raw)
{
this.raw = raw; // 通过构造函数,这可以让 EventList 控制对 IList<T> 的访问。
}
public void Add(T value)
{
this.raw.Add(value); Console.WriteLine(value);
}
}
上面就是一个简单的代理模式的例子:
为 IList 提供一种 LogList ,以控制对 IList 的访问。
实现
简单实现
上面 LogList 就是一种简单的实现。
但是你无法对这个类做外部扩展,所有的逻辑都在类型的内部被固定了。
于是我们可以使用下面的方法创建一个可以灵活扩展的 ListProxy
public interface IListInterruption
{
// other codes // 执行 IList.Add 时会进入的方法 void OnAdding(IList<T> list, T addingValue); // 执行完 IList.Add 时会进入的方法 void OnAdded(IList<T> list, T addedValue); // other codes
}
// 列表代理类
// 允许外部提供 IListInterruption 来丰富 ListProxy 的逻辑。
public class ListProxy : IList
{
private readonly IList<T> raw; private readonly List<IListInterruption> interruptions; public ListProxy(IList<T> raw) { this.interruptions = new List<IListInterruption>(); this.raw = raw; } public void AddInterruption(IListInterruption interruption) { this.interruptions.Add(interruption); } public void Add(T value) { foreach(var item in this.interruptions) item.OnAdding(this.raw, value); this.raw.Add(value); foreach(var item in this.interruptions) item.OnAdded(this.raw, value); }
}
上面的代码实现一个较为灵活的 ProxyList 。
首先看看 IListInterruption。通过实现 IListInterruption 接口,可以向 ProxyList 提供各种各样的功能。
我们可以看一个简单的功能
public class LogListInterruption : IListInterruption
{
// other codes public void OnAdding(IList<T> list, T addingValue) { Console.WriteLie("Adding : {0}", addingValue); } // other codes
}
向 ProxyList 添加上述组件,就可以实现在 Add 前打印待添加的值的功能。
List myList = new List();
ProxyList proxy = new ProxyList(myList);
proxy.AddInterruption(new LogListInterruption());
proxy.Add(1);
// >> Adding : 1
这种实现方式可以创建出针对某个类型的代理类,并通过外部给予的 IListInterruption 来丰富代理类功能。
但缺点是,当你无法为所有的类型都创建 Proxy 和 Interruption 。
动态代理类
之前的方法中,我们在编写阶段就已经建立了代理类,被称为静态代理类。
这种方法无法将代理类运用在系统中任何一个我们可能需要的类型上。
于是,动态代理类 就来了。
动态代理类 依靠编程语言本身的特征,让程序在 运行时 创建类型,实现接口,添加功能等等。
在 C# 中可以通过两种方式实现运行时创建类型的功能
CodeDom + 运行时编译
Emit
CodeDom 可以用来生成 C# 代码,再利运行时编译,会将C#代码编译为内存中的程序集,最后通过反射访问程序集中的成员。
这种方法的特点就是慢。。。。因为要生成语句,还要编译,生成程序集,最后还要反射,都是大开销,所以慢是可想而知的。
Emit 提供了利用 IL 命令在运行时创建类型、方法,并填充方法内的功能。
毕竟 C# 最终就是编译成 IL 的,所以直接使用 IL 性能当然快无敌了。
这个方式的缺点只有一个 : 学习 IL 。这玩意可不是每个人都愿意去学的。。。
于是,选择一些已经利用 Emit 做好了动态代理类功能的第三方功能库,成为了一个很好的选择。
C# 大环境下,可以用来生成动态代理类的库一般有两个选择 :
PostSharp
Caslte.DynamicProxy
其中 PostSharp 使用了更复杂的技术,不是使用 Emit,而且在编译时,就将代理类所附加的功能放进了目标类型中,你可以通过 Attribute 向任意的方法添加额外的功能。
PostSharp 会在程序编译时,把这些 Attribute 的功能直接编译到方法内部。
这种在编译时就把一切准备好的方法,让 PostSharp 有着最高的运行性能。
但是又傻瓜、又好用的 PostSharp 只有一个缺点 ———— 收费。
Castle.DynamicProxy 是免费的,他是利用 Emit 在程序运行时创建代理类的。
使用 Castle.DynamicProxy 有两个步骤:
编写 Interceptor
将 Interceptor 附加到某个类型或接口上,并得到一个会调用你的 Interceptor 的代理类实例
开发流程很像之前的 LogList 的例子。
相比较 PostSharp 那种一个 Attribute 就搞定一切的模式, Caslte.DynamicProxy 就没有那么方便了。
那么一个显而易见的问题就来了 :
能不能利用 Caslte.DynamicProxy 实现像 PostSharp 那样利用 Attribute 创建代理类的功能呢?
Reface.AppStarter.Proxy
这是基于 Reface.AppStarter 开发的一个功能模块,
使用它,可以利用 Attribute 的方式轻松的创建代理类,并实现 AOP 的功能。
你所要做的,就是创建一个继承于 ProxyAttribute 的特征。
ProxyAttribute 中有三个方法需要重写
OnExecuting ,被标记的方法执行时
OnExecuted ,被标记的方法执行后
OnExecuteError , 被标记的方法执行出现异常后
你可以编写你的逻辑在这三个方法内,并将你的 Attribute 挂载到你需要的类型的方法上即可。
剩下的事情只有两件
向你的 AppModule 添加 ProxyAppModule
为你需要创建代理的类型加上 [Component] 特征
你已经完成了所有工作,
当你利用 Reface.AppStarter 的框架的 IOC / DI 容器创建你的类型时,实际得到的就是代理类,这些代理类会调试你给予的 ProxyAttribute 。
关于 Reface.AppStarter.Proxy 的细节,会在以后的文章中进一步介绍。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2020年,数字化转型的十大趋势
在当今相互连接的世界中,消费者正在以新的方式使用和采用新兴的数字技术。 他们的需求,行为习惯正在发生变化。 他们比以往任何时候都掌握更多的信息,他们期望企业及时提供更高质量,顺畅的客户体验和更低成本的服务。因此,在各种规模和跨领域的企业中,公司必须以数字方式参与以创新和保持竞争力,数字化转型成为传统企业实现业务增长的必经之路。 什么是数字化转型? 数字化转型(DX)就是利用数字化技术来推动企业组织转变业务模式,组织架构,企业文化等的变革措施。它是一种使业务创新得以实现的策略,该创新基于将数字技术整合到您的运营流程,产品,解决方案和客户互动中。该战略的重点是通过数字资产的创造和货币化来利用新技术(如移动、Web、社交、大数据、机器学习、人工智能、物联网、云计算、区块链)的机遇及其对业务的影响。 数字化转型涉及数字化生态系统的构建,在该生态系统中,客户,合作伙伴,员工,供应商和外部实体之间保持一致和无缝集成,从而为整体提供更大的整体价值。 2020年十大数字化转型趋势 在过去的几年中,有关2020年数字化转型趋势的大多数讨论都围绕着:云,边缘计算,物联网IoT,增强现实AR……这些关键词反...
- 下一篇
java对象头信息和三种锁的性能对比
java对象头信息和三种锁的性能对比 java头的信息分析首先为什么我要去研究java的对象头呢? 这里截取一张hotspot的源码当中的注释 这张图换成可读的表格如下 Object Header (128 bits) Mark Word (64 bits) Klass Word (64 bits) unused:25 identity_hashcode:31 unused:1 age:4 biased_lock:1 lock:2 OOP to metadata object 无锁 thread:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 OOP to metadata object 偏向锁 ptr_to_lock_record:62 lock:2 OOP to metadata object 轻量锁 ptr_to_heavyweight_monitor:62 lock:2 OOP to metadata object 重量锁 lock:2 OOP to metadata object GC 意思是java的对象头在对象的不同状态下会有...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境