设计模式——代理模式
需求
现实生活中,我们难免会需要对一些不熟悉的行业处理事情,常常就找专业的中介机构来代办这些事情:例如法律事务聘请律师代理办案,买二手房找专业的房产中介代办,银行基金理财找基金理财顾问,这些专业的中介都是代理模式(Proxy Pattern)的实际应用。
在程序开发中,也有类似的事情。例如设计好了业务规则类,又想添加权限控制。如果直接修改业务规则类代码加进去,则违反了单一职责的原理。那么可以再设计一个代理类,在这里代理类里调用业务规则功能,但是添加需要的权限控制就可以了。
还有一种情况,如果开发的系统会依赖于其它系统的某些功能,但是那些被依赖的系统可能因为这样那样的原因还不稳定,可能发生变化。如果直接使用,以后被依赖者发生了变化后,本系统所有涉及到的地方都有必要需要检修。怎么办?可以采用代理模式,在要开发的系统与所依赖的系统之间增加一个中间层,在中间层调用依赖目标,即通过代理类来代理所依赖的目标。这样以后所依赖系统如果发生了变化,也只需要修改这个中间层就可以了,而不用再到所开发系统的各个角落乱找那里需要修改。
定义
代理模式就是给一个对象(代理目标,subject)提供一个代理对象,由这个代理对象控制对原对象的使用,客户端通过代理对象而使用原对象的功能。
代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接使用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。
代理模式由3部分组成:(1)需要代理的抽象主题类(Abstract Subject),声明了代理主题和真实主题的公共接口,使任何需要真实主题的地方都能用代理主题代替;(2)具体代理主题类(Subject),需要代理的对象;(3)继承/实现代理主题的代理类(Proxy)。
实现方法:代理对象内部含有对真实主题对象的引用,从而可以操作真实主题对象,同时代理对象实现与真实主题对象相同的接口以便在任何时刻都能代替真实主题对象;同时,代理对象可以在执行真实主题对象操作时,附加其他的操作,相当于对真实主题对象进行包装。
一个简单案例
一个游戏场景:坦克能够移动和攻击,已经构造好了坦克接口与类,但是在这个场景里还需要给坦克移动与攻击都增加一些额外的控制操作,所以又设计了代理类。
class Program
{
static voidMain(string[] args)
{
// 客户程序
Tank aTank = new Tank();
TankProxy aTankProxy = new TankProxy(aTank);
aTankProxy.Move();
aTankProxy.Attack();
}
}
// 抽象目标
public interface ITank
{
void Move();
void Attack();
}
// 具体目标
public class Tank : ITank
{
public void Move() { Console.WriteLine("坦克移动..."); }
public void Attack() { Console.WriteLine("坦克攻击..."); }
}
// 代理类
public class TankProxy : ITank // 这里实现抽象目标来得到代理类,也有人直接继承具体目标来实现代理类
{
public TankProxy(ITank moveable) { this.moveable = moveable; }
private ITank moveable;
public void Move() // 在这个方法里可以添加一些额外的控制
{
Console.WriteLine("开始移动:");
this.moveable.Move();
Console.WriteLine("结束移动!");
}
public void Attack() // 在这个方法里可以添加一些额外的控制
{
Console.WriteLine("开始攻击:");
this.moveable.Move();
Console.WriteLine("结束攻击!");
}
}
静态代理与动态代理
象上面代码,在代码编译期间,代理类代理的抽象主题与具体主题已经确定,有些书上就称为静态代理模式。在上面案例代码中可以看出,一个代理类只能对一个接口或者类提供代理服务。就像上面的情况,每个需要代理的方法增加的额外控制都相同,如果需要代理的方法或者类很多,那么需要增加很多非常相似的代理方法或者类。这就是静态代理的缺点。
为了规避静态代理模式的这个缺点,可以通过反射机制来实现代理模式,通过运行时动态反射需要代理的类型及其方法,增加相似甚至相同的额外控制功能,这种实现方法就是所谓的动态代理模式了。动态代理模式请查阅相关资料。
优缺点
优点:使用代理模式,能够对某对象(代理目标)进行额外的控制;向客户端隐藏了某个需要依赖对象的细节及复杂性,隔离了其可能的变动给客户端带来的影响;可以动态地调用一个对象中的方法,且无需实现固定的接口。
缺点:对于静态代理来说,一个代理类只能对一个接口或者类提供代理服务。在需要增加额外控制相似或者相同的情况下,如果需要代理的类或者方法很多,则要为每个类或者方法实现相似的代理类。
适用场景
(1)客户程序需要对调用的某个对象增加额外的操作,或者那个对象以后还可能修改,那么搞个代理包装它;
(2)直接访问一个对象很困难,或者说不能访问,此时只能是找个代理去访问,然后把结果反馈给自己;
(3)假如有一个外部组件包,不允许实现其接口,则就只能使用其动态代理了。
补充:代理模式与装饰模式比较
(1)它们都提供间接访问对象层,都保存被调用对象的引用;(2)代理模式通过代理类对某个类的某个方法进行控制,而不在原类上修改;(3)装饰模式通过装饰类对某个类的某个方法增加额外的功能,也不在原类上修改。
补充:代理模式的实际应用
以下内容来自:http://tech.ccidnet.com/art/1110/20050714/869417_1.html
(1)远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
(2)虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载;代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
(3)Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
(4)保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
(5)Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
(6)防火墙(Firewall)代理:保护目标,不让恶意用户接近。
(7)同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
(8)智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述