2022/03/01 Proxy代理模式
简介
代理模式为其他对象提供一种代理以控制对这个对象的访问。 在需要用比较通用和复杂的对象指针代替简单的的指针 的时候,使用代理模式。有四种常用的情况:
1、远程代理,也就是为一个对象在不同的地址空间提供局部代表 。这样可以隐藏一个对象存在于不同地址空间的事实。
2、虚拟代理 ,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
3、安全代理 ,用来控制真实对象访问的权限。
4、智能指针 ,取代了简单的指针,它在访问对象时执行一些附加操作。(????)
参与者:
— 保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,proxy会引用Subject。
— 提供一个与Subject的接口相同的接口,这样代理就可以用来代替实体。
— 控制对实体的存取,并可能负责创建和删除它。
— 其它功能依赖于代理的类型。
— 定义RealSubject和Proxy的共用接口,这样就可以在任何使用RealSubject的地方都可以使用Proxy 。
— 定义Proxy所代表的实体。
思想:作为C工程师,免不了要管理内存,内存管理也是C 中的难点,而智能指针采用引用计数的办法很方便的帮我们管理了内存的使用,极大方便了我们的工作效率。而智能指针的这种用法其实就是代理模式的一种,他帮我们控制了该对象的内存使用。代理模式就是为其他对象提供一种代理来控制对这个对象的访问。
场景:需要注意体会他和Decorator的需别。
Proxy是继承需要修饰的类,而Decorator用的是包含的方式。Proxy模式,或者准确地说DynamicProxy模式,是现代AOP框架实现中的一种常用方式。典型的实现如Spring,JBoss以及CastleProject中的Aspect。
实现:继承,并在过载方法中添加需要的修饰功能。
java实现代码
1 2 3 4 public interface Image { void display () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class RealImage implements Image { private String fileName; public RealImage (String fileName) { this .fileName = fileName; loadFromDisk(fileName); } @Override public void display () { System.out.println("Displaying " + fileName); } private void loadFromDisk (String fileName) { System.out.println("Loading " + fileName); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class ProxyImage implements Image { private RealImage realImage; private String fileName; public ProxyImage (String fileName) { this .fileName = fileName; } @Override public void display () { if (realImage == null ){ realImage = new RealImage (fileName); } realImage.display(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ProxyPatternDemo { public static void main (String[] args) { Image image = new ProxyImage ("test_10mb.jpg" ); image.display(); System.out.println("" ); image.display(); } }
C++实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <iostream> class Subject {public : virtual void Request () const = 0 ; }; class RealSubject : public Subject {public : void Request () const override { std::cout << "RealSubject: Handling request.\n" ; } }; class Proxy : public Subject { private : RealSubject* real_subject_; bool CheckAccess () const { std::cout << "Proxy: Checking access prior to firing a real request.\n" ; return true ; } void LogAccess () const { std::cout << "Proxy: Logging the time of request.\n" ; } public : Proxy (RealSubject* real_subject) : real_subject_ (new RealSubject (*real_subject)) { } ~Proxy () { delete real_subject_; } void Request () const override { if (this ->CheckAccess ()) { this ->real_subject_->Request (); this ->LogAccess (); } } }; void ClientCode (const Subject& subject) { subject.Request (); } int main () { std::cout << "Client: Executing the client code with a real subject:\n" ; RealSubject* real_subject = new RealSubject; ClientCode (*real_subject); std::cout << "\n" ; std::cout << "Client: Executing the same client code with a proxy:\n" ; Proxy* proxy = new Proxy (real_subject); ClientCode (*proxy); delete real_subject; delete proxy; return 0 ; }
代码分析与总结
代理对象里有内置引用,能够通过proxy对象加载成功后,像直接用realsubject一样相关操作调用RealSubject对象
代理对象能够实现对真正实现对象的延后初始化,用的时候再进行加载,并且能够通过代理决定什么时候把这个对象缓存释放
实际应用场景
延迟初始化 (虚拟代理)。如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源时,可使用代理模式。
你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
(难道我真正需要的时候再创建初始化对象不行吗?为什么还要裹一层皮)
访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序),此时可使用代理模式。
代理可仅在客户端凭据满足要求时将请求传递给服务对象。
本地执行远程服务 (远程代理)。 适用于服务 对象位于远程服务器上的情形。
在这种情形中, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。
记录日志请求 (日志记录代理)。 **适用于当你需要保存对于服务对象的请求历史记录时。 **代理可以在向服务传递请求前进行记录。
缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。
智能引用。可在没有客户端使用某个重量级对象时立即销毁该对象。
应用实例和伪代码
1、Windows 里面的快捷方式。
2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。
3、买火车票不一定在火车站买,也可以去代售点。
4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
5、spring aop。
怎么说 还是有点看不懂人家写的伪代码,不知道配置是怎么实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 interface ThirdPartyTVLib is method listVideos () method getVideoInfo (id) method downloadVideo (id) class ThirdPartyTVClass implements ThirdPartyTVLib is method listVideos () is method getVideoInfo (id) is method downloadVideo (id) is class CachedTVClass implements ThirdPartyTVLib is private field service: ThirdPartyTVLib private field listCache, videoCache field needReset constructor CachedTVClass (service: ThirdPartyTVLib) is this .service = service method listVideos () is if (listCache == null || needReset) listCache = service.listVideos() return listCache method getVideoInfo (id) is if (videoCache == null || needReset) videoCache = service.getVideoInfo(id) return videoCache method downloadVideo (id) is if (!downloadExists(id) || needReset) service.downloadVideo(id) class TVManager is protected field service: ThirdPartyTVLib constructor TVManager (service: ThirdPartyTVLib) is this .service = service method renderVideoPage (id) is info = service.getVideoInfo(id) method renderListPanel () is list = service.listVideos() method reactOnUserInput () is renderVideoPage () renderListPanel() class Application is method init () is aTVService = new ThirdPartyTVClass () aTVProxy = new CachedTVClass (aTVService) manager = new TVManager (aTVProxy) manager.reactOnUserInput()
优缺点
与其他模式的关系
装饰 和代理 有着相似的结构, 但是其意图却非常不同。 这两个模式的构建都基于组合原则, 也就是说一个对象应该将部分工作委派给另一个对象。 两者之间的不同之处在于代理通常自行管理其服务对象的生命周期 , 而装饰的生成则总是由客户端进行控制。