[设计模式] 设计模式课程(十三)-- 代理模式
Posted cxc1357
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[设计模式] 设计模式课程(十三)-- 代理模式相关的知识,希望对你有一定的参考价值。
概述
- 属于结构型模式
- 提供代理作为对象的替代品或其占位符,代理控制对原对象的访问,并可对请求在提交给对象前后进行一些处理
- 由于某种原因(如对象创建开销很大,某种操作需要安全控制,或者需要进程外访问等)直接访问会给使用者或者系统结构带来很多麻烦
- 如何在不失去透明操作对象的同时来管理 / 控制这些对象特有的复杂性?增加一层间接层
- 为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问
- “增加一层间接层”是软件系统中对很多复杂问题的一种常见解决方式,在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy是解决这一问题的常用手段
- 具体实现方法、实现粒度都相差很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组件模块提供抽象代理层,在架构层次对对象做proxy
- Proxy并不一定要求保持接口完整的一致性,只要能够实现间接控制,有时损及一些透明性是可以接受的
场景
- 数据库对象非常巨大,查询操作缓慢,但并非总是用到。代理将自己伪装成数据库对象,可在客户端或实际数据库对象不知情的情况下处理延迟初始化和缓存查询结果的工作
- 信用卡是银行账户的代理
结构
- 服务接口:代理必须遵循该接口才能伪装成服务对象
- 服务类:提供业务逻辑
- 代理类:包含指向服务对象的引用成员变量,代理其完成任务(延迟初始化、日志、访问控制、缓存等)后将请求传递给服务对象
- 客户端:通过同一接口与服务或代理进行交互
示例1
client.cpp
1 class ISubject{ 2 public: 3 virtual void process(); 4 }; 5 6 class RealSubject: public ISubject{ 7 public: 8 virtual void process(){ 9 //.... 10 } 11 }; 12 13 class ClientApp{ 14 15 ISubject* subject; 16 17 public: 18 19 ClientApp(){ 20 subject=new RealSubject(); 21 } 22 23 void DoTask(){ 24 //... 25 subject->process(); 26 27 //.... 28 } 29 };
proxy.cpp
1 class ISubject{ 2 public: 3 virtual void process(); 4 }; 5 6 //Proxy的设计 7 class SubjectProxy: public ISubject{ 8 9 10 public: 11 virtual void process(){ 12 //对RealSubject的一种间接访问 13 //.... 14 } 15 }; 16 17 class ClientApp{ 18 19 ISubject* subject; 20 21 public: 22 23 ClientApp(){ 24 subject=new SubjectProxy(); 25 } 26 27 void DoTask(){ 28 //... 29 subject->process(); 30 31 //.... 32 } 33 };
示例2
1 #include <string> 2 #include <iostream> 3 using namespace std; 4 5 // 定义接口 6 class Interface{ 7 public: 8 virtual void Request() = 0 ; 9 }; 10 // 真实类 11 class RealClass:public Interface{ 12 public: 13 virtual void Request(){ 14 cout<<"真实的请求"<<endl; 15 } 16 }; 17 // 代理类 18 class ProxyClass:public Interface{ 19 private: 20 RealClass* m_realClass; 21 public: 22 virtual void Request(){ 23 m_realClass = new RealClass(); 24 m_realClass->Request(); 25 delete m_realClass; 26 } 27 }; 28 // 客户端 29 int main(){ 30 ProxyClass* test = new ProxyClass(); 31 test->Request(); 32 return 0; 33 }
>>真实的请求
示例3
- 使用代理模式在腾讯视频(TencentVideo,记为TV)程序库中添加延迟初始化和缓存
- 程序库提供了下载类,但效率非常低,如果客户端多次请求下载同一视频,程序会反复下载,而不会将首次下载的文件缓存复用
- 代理类实现和原下载器相同的接口,并将工作委派给原下载器,但代理类会保存所有文件下载记录,如果程序请求同一文件,则返回缓存文件
1 // 远程服务接口。 2 interface ThirdPartyTVLib is 3 method listVideos() 4 method getVideoInfo(id) 5 method downloadVideo(id) 6 7 // 服务连接器的具体实现。该类的方法可以向腾讯视频请求信息。请求速度取决于 8 // 用户和腾讯视频的互联网连接情况。如果同时发送大量请求,即使所请求的信息 9 // 一模一样,程序的速度依然会减慢。 10 class ThirdPartyTVClass implements ThirdPartyTVLib is 11 method listVideos() is 12 // 向腾讯视频发送一个 API 请求。 13 14 method getVideoInfo(id) is 15 // 获取某个视频的元数据。 16 17 method downloadVideo(id) is 18 // 从腾讯视频下载一个视频文件。 19 20 // 为了节省网络带宽,我们可以将请求结果缓存下来并保存一段时间。但你可能无 21 // 法直接将这些代码放入服务类中。比如该类可能是第三方程序库的一部分或其签 22 // 名是`final(最终)`。因此我们会在一个实现了服务类接口的新代理类中放入 23 // 缓存代码。当代理类接收到真实请求后,才会将其委派给服务对象。 24 class CachedTVClass implements ThirdPartyTVLib is 25 private field service: ThirdPartyTVLib 26 private field listCache, videoCache 27 field needReset 28 29 constructor CachedTVClass(service: ThirdPartyTVLib) is 30 this.service = service 31 32 method listVideos() is 33 if (listCache == null || needReset) 34 listCache = service.listVideos() 35 return listCache 36 37 method getVideoInfo(id) is 38 if (videoCache == null || needReset) 39 videoCache = service.getVideoInfo(id) 40 return videoCache 41 42 method downloadVideo(id) is 43 if (!downloadExists(id) || needReset) 44 service.downloadVideo(id) 45 46 // 之前直接与服务对象交互的 GUI 类不需要改变,前提是它仅通过接口与服务对 47 // 象交互。我们可以安全地传递一个代理对象来代替真实服务对象,因为它们都实 48 // 现了相同的接口。 49 class TVManager is 50 protected field service: ThirdPartyTVLib 51 52 constructor TVManager(service: ThirdPartyTVLib) is 53 this.service = service 54 55 method renderVideoPage(id) is 56 info = service.getVideoInfo(id) 57 // 渲染视频页面。 58 59 method renderListPanel() is 60 list = service.listVideos() 61 // 渲染视频缩略图列表。 62 63 method reactOnUserInput() is 64 renderVideoPage() 65 renderListPanel() 66 67 // 程序可在运行时对代理进行配置。 68 class Application is 69 method init() is 70 aTVService = new ThirdPartyTVClass() 71 aTVProxy = new CachedTVClass(aTVService) 72 manager = new TVManager(aTVProxy) 73 manager.reactOnUserInput()
示例4
1 #include <iostream> 2 /** 3 * The Subject interface declares common operations for both RealSubject and the 4 * Proxy. As long as the client works with RealSubject using this interface, 5 * you‘ll be able to pass it a proxy instead of a real subject. 6 */ 7 class Subject { 8 public: 9 virtual void Request() const = 0; 10 }; 11 /** 12 * The RealSubject contains some core business logic. Usually, RealSubjects are 13 * capable of doing some useful work which may also be very slow or sensitive - 14 * e.g. correcting input data. A Proxy can solve these issues without any 15 * changes to the RealSubject‘s code. 16 */ 17 class RealSubject : public Subject { 18 public: 19 void Request() const override { 20 std::cout << "RealSubject: Handling request. "; 21 } 22 }; 23 /** 24 * The Proxy has an interface identical to the RealSubject. 25 */ 26 class Proxy : public Subject { 27 /** 28 * @var RealSubject 29 */ 30 private: 31 RealSubject *real_subject_; 32 33 bool CheckAccess() const { 34 // Some real checks should go here. 35 std::cout << "Proxy: Checking access prior to firing a real request. "; 36 return true; 37 } 38 void LogAccess() const { 39 std::cout << "Proxy: Logging the time of request. "; 40 } 41 42 /** 43 * The Proxy maintains a reference to an object of the RealSubject class. It 44 * can be either lazy-loaded or passed to the Proxy by the client. 45 */ 46 public: 47 Proxy(RealSubject *real_subject) : real_subject_(new RealSubject(*real_subject)) { 48 } 49 50 ~Proxy() { 51 delete real_subject_; 52 } 53 /** 54 * The most common applications of the Proxy pattern are lazy loading, 55 * caching, controlling the access, logging, etc. A Proxy can perform one of 56 * these things and then, depending on the result, pass the execution to the 57 * same method in a linked RealSubject object. 58 */ 59 void Request() const override { 60 if (this->CheckAccess()) { 61 this->real_subject_->Request(); 62 this->LogAccess(); 63 } 64 } 65 }; 66 /** 67 * The client code is supposed to work with all objects (both subjects and 68 * proxies) via the Subject interface in order to support both real subjects and 69 * proxies. In real life, however, clients mostly work with their real subjects 70 * directly. In this case, to implement the pattern more easily, you can extend 71 * your proxy from the real subject‘s class. 72 */ 73 void ClientCode(const Subject &subject) { 74 // ... 75 subject.Request(); 76 // ... 77 } 78 79 int main() { 80 std::cout << "Client: Executing the client code with a real subject: "; 81 RealSubject *real_subject = new RealSubject; 82 ClientCode(*real_subject); 83 std::cout << " "; 84 std::cout << "Client: Executing the same client code with a proxy: "; 85 Proxy *proxy = new Proxy(real_subject); 86 ClientCode(*proxy); 87 88 delete real_subject; 89 delete proxy; 90 return 0; 91 }
参考
https://refactoringguru.cn/design-patterns/proxy
以上是关于[设计模式] 设计模式课程(十三)-- 代理模式的主要内容,如果未能解决你的问题,请参考以下文章