JavaScript设计模式-结构型-代理模式
概要
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
问题
为什么要控制对于某个对象的访问呢? 举个例子: 有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。
你可以实现延迟初始化: 在实际有需要时再创建该对象。 对象的所有客户端都要执行延迟初始代码。 不幸的是, 这很可能会带来很多重复代码。
在理想情况下, 我们希望将代码直接放入对象的类中, 但这并非总是能实现: 比如类可能是第三方封闭库的一部分。
解决方案
代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。
代理将自己伪装成数据库对象, 可在客户端或实际数据库对象不知情的情况下处理延迟初始化和缓存查询结果的工作。
这有什么好处呢? 如果需要在类的主要业务逻辑前后执行一些工作, 你无需修改类就能完成这项工作。 由于代理实现的接口与原类相同, 因此你可将其传递给任何一个使用实际服务对象的客户端。
适配器模式结构图
Subject: 抽象主题角色 Proxy: 代理主题角色 RealSubject: 真实主题角色
举例
程序提供接口,调用接口可以得到数据库的一些信息。
但是有时候产品要求客户端要获取这些信息需要做一些验证,如果用户没有登录或者没有权限访问的话需要做一些逻辑。
还有就是查询这些信息的时候,做一些额外的处理,比如统计这个接口的访问次数等等需求
下面我们来模拟这些场景。
class subject {
request() {
throw new Error('Request Method not implemented')
}
}
class RealSubject extends subject {
constructor() {
super()
}
request() {
console.log(`RealSubject: Handling request.`)
}
}
class Proxy extends subject {
constructor(realSubject) {
super()
this.realSubject = realSubject;
}
request() {
if (this.checkAccess()) {
this.realSubject.request()
this.logAccess()
}
console.log(`RealSubject: Handling request.`)
}
checkAccess() {
// Some real checks should go here.
console.log('Proxy: Checking access prior to firing a real request.')
return true;
}
logAccess() {
console.log('Proxy: Logging the time of request.')
}
}
function clientCode(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)
output
"Client: Executing the client code with a real subject:"
"RealSubject: Handling request."
""
"Client: Executing the same client code with a proxy:"
"Proxy: Checking access prior to firing a real request."
"RealSubject: Handling request."
"Proxy: Logging the time of request."
"RealSubject: Handling request."
适合应用场景
使用代理模式的方式多种多样, 我们来看看最常见的几种。
-
延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。
你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
-
访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。
代理可仅在客户端凭据满足要求时将请求传递给服务对象。
-
本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。
在这种情形中, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。
-
记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录。
缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。
- 代理可对重复请求所需的相同结果进行缓存, 还可使用请求参数作为索引缓存的键值。
-
智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。
代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。
代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。
总结
优缺点
-
优点
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
- 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
- 保护代理可以控制对真实对象的使用权限。
-
缺点
- 由于在客户端和真实主题之间增加了代理对象,因此 有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现 非常复杂。