提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

// 远程服务接口。interface ThirdPartyTVLib ismethod listVideos()method getVideoInfo(id)method downloadVideo(id)// 服务连接器的具体实现。该类的方法可以向腾讯视频请求信息。请求速度取决于// 用户和腾讯视频的互联网连接情况。如果同时发送大量请求,即使所请求的信息// 一模一样,程序的速度依然会减慢。class ThirdPartyTVClass implements ThirdPartyTVLib ismethod listVideos() is// 向腾讯视频发送一个 API 请求。method getVideoInfo(id) is// 获取某个视频的元数据。method downloadVideo(id) is// 从腾讯视频下载一个视频文件。// 为了节省网络带宽,我们可以将请求结果缓存下来并保存一段时间。但你可能无// 法直接将这些代码放入服务类中。比如该类可能是第三方程序库的一部分或其签// 名是`final(最终)`。因此我们会在一个实现了服务类接口的新代理类中放入// 缓存代码。当代理类接收到真实请求后,才会将其委派给服务对象。class CachedTVClass implements ThirdPartyTVLib isprivate field service: ThirdPartyTVLibprivate field listCache, videoCachefield needResetconstructor CachedTVClass(service: ThirdPartyTVLib) isthis.service = servicemethod listVideos() isif (listCache == null || needReset)listCache = service.listVideos()return listCachemethod getVideoInfo(id) isif (videoCache == null || needReset)videoCache = service.getVideoInfo(id)return videoCachemethod downloadVideo(id) isif (!downloadExists(id) || needReset)service.downloadVideo(id)// 之前直接与服务对象交互的 GUI 类不需要改变,前提是它仅通过接口与服务对// 象交互。我们可以安全地传递一个代理对象来代替真实服务对象,因为它们都实// 现了相同的接口。class TVManager isprotected field service: ThirdPartyTVLibconstructor TVManager(service: ThirdPartyTVLib) isthis.service = servicemethod renderVideoPage(id) isinfo = service.getVideoInfo(id)// 渲染视频页面。method renderListPanel() islist = service.listVideos()// 渲染视频缩略图列表。method reactOnUserInput() isrenderVideoPage()renderListPanel()// 程序可在运行时对代理进行配置。class Application ismethod init() isaTVService = new ThirdPartyTVClass()aTVProxy = new CachedTVClass(aTVService)manager = new TVManager(aTVProxy)manager.reactOnUserInput()
/*** The Subject interface declares common operations for both RealSubject and the* Proxy. As long as the client works with RealSubject using this interface,* you'll be able to pass it a proxy instead of a real subject.*/interface Subject {request(): void;}/*** The RealSubject contains some core business logic. Usually, RealSubjects are* capable of doing some useful work which may also be very slow or sensitive -* e.g. correcting input data. A Proxy can solve these issues without any* changes to the RealSubject's code.*/class RealSubject implements Subject {public request(): void {console.log('RealSubject: Handling request.');}}/*** The Proxy has an interface identical to the RealSubject.*/class Proxy implements Subject {private realSubject: RealSubject;/*** The Proxy maintains a reference to an object of the RealSubject class. It* can be either lazy-loaded or passed to the Proxy by the client.*/constructor(realSubject: RealSubject) {this.realSubject = realSubject;}/*** The most common applications of the Proxy pattern are lazy loading,* caching, controlling the access, logging, etc. A Proxy can perform one of* these things and then, depending on the result, pass the execution to the* same method in a linked RealSubject object.*/public request(): void {if (this.checkAccess()) {this.realSubject.request();this.logAccess();}}private checkAccess(): boolean {// Some real checks should go here.console.log('Proxy: Checking access prior to firing a real request.');return true;}private logAccess(): void {console.log('Proxy: Logging the time of request.');}}/*** The client code is supposed to work with all objects (both subjects and* proxies) via the Subject interface in order to support both real subjects and* proxies. In real life, however, clients mostly work with their real subjects* directly. In this case, to implement the pattern more easily, you can extend* your proxy from the real subject's class.*/function clientCode(subject: Subject) {// ...subject.request();// ...}console.log('Client: Executing the client code with a real subject:');const realSubject = new RealSubject();clientCode(realSubject);console.log('');console.log('Client: Executing the same client code with a proxy:');const proxy = new Proxy(realSubject);clientCode(proxy);
