介绍
代理模式是一种结构型设计模式,它允许通过代理对象控制对另一个对象的访问。代理对象充当了客户端和实际对象之间的中介,客户端通过代理对象来访问实际对象,从而可以在不直接访问实际对象的情况下,控制对实际对象的访问和操作。
代理模式的主要目的是为了提供额外的功能或控制访问,而不需要改变原始对象的结构。代理模式可以用于许多不同的场景,例如远程代理(用于访问远程对象)、虚拟代理(用于延迟加载或创建昂贵的对象)、安全代理(用于控制对对象的访问权限)等。
涉及的角色有:
- Subject(主题):定义了真实对象和代理对象共同实现的接口,这样代理对象可以替代真实对象进行操作。
- RealSubject(真实主题):代表真实的对象,是代理对象所代理的具体实现。
- Proxy(代理):包含对真实主题的引用,并实现了主题接口,可以控制对真实主题的访问,并在必要时执行额外的操作。
优点
- 实现了客户端和真实对象的解耦:代理模式通过引入代理对象作为中介,将客户端与真实对象解耦。客户端只需要与代理对象进行交互,无需直接访问真实对象,从而降低了客户端与真实对象之间的依赖关系。
- 增强了安全性:代理模式可以在代理对象中实施安全控制,以限制对真实对象的访问。代理可以验证客户端的权限、验证输入数据的有效性等,从而提供更高层次的安全性。
- 实现了延迟加载:代理模式可以延迟加载真实对象,只有在需要的时候才去创建和初始化真实对象。这对于一些资源密集型的对象或者需要耗费较长时间初始化的对象来说,可以减少启动时间和内存占用。
- 提升了性能:代理模式可以通过在代理对象中缓存结果或者对代理请求进行优化,从而提升系统的性能。例如,代理对象可以缓存真实对象的计算结果,避免重复计算。
缺点
- 增加了复杂性:引入代理对象会增加系统的复杂性,因为需要维护额外的代理类。这可能会增加代码量、调试和维护的难度。
- 可能引入性能损耗:由于代理模式涉及到额外的间接层,可能会导致一定程度的性能损耗。每次通过代理访问真实对象都需要经过额外的处理和转发。
- 可能造成资源占用:如果代理对象没有正确管理对真实对象的引用,可能会造成资源的占用。如果代理对象过多或者生命周期管理不当,可能会导致资源泄漏或者资源浪费。
使用场景
- 数据库连接池代理:在高并发的系统中,数据库连接是有限资源。使用代理模式可以创建数据库连接池代理,用于管理和复用数据库连接。代理对象可以控制对连接的访问,限制连接数或实现连接的延迟加载。
- 图像/视频加载代理:在Web应用中,当需要加载大型图像或视频时,可以使用代理模式。代理对象可以在图像/视频加载之前显示占位符或加载动画,从而提供更好的用户体验。同时,代理对象可以负责缓存已加载的图像/视频,以避免重复的网络请求。
- 安全代理:代理模式可以用于实现安全控制。例如,在网络应用中,可以使用代理对象对用户请求进行身份验证和授权,从而确保只有经过验证的用户可以访问敏感资源。
- 远程服务代理:在分布式系统中,可以使用代理模式调用远程服务。代理对象充当客户端和远程服务之间的中介,处理网络通信细节,隐藏底层的网络协议和调用细节。
- 延迟加载代理:当需要加载大量对象或者初始化耗时的对象时,可以使用代理模式进行延迟加载。代理对象可以在真正需要对象时才进行加载,从而减少启动时间和资源消耗。
- 缓存代理:代理模式可以用于实现缓存机制。代理对象可以缓存真实对象的结果,以避免重复计算或频繁访问。这对于一些计算密集型的操作或者需要频繁访问的数据可以提供性能优化。
- 日志记录代理:代理模式可以用于记录操作日志。代理对象可以在调用真实对象的方法前后记录日志信息,用于调试、监控或审计等目的。
代码示例
package main
import "fmt"
// 远程服务接口
type RemoteService interface {
Request()
}
// 真实的远程服务
type RealRemoteService struct{}
func (r *RealRemoteService) Request() {
fmt.Println("执行远程请求")
}
// 代理对象,它持有一个真实服务对象的引用。
type RemoteServiceProxy struct {
realService *RealRemoteService
}
func (p *RemoteServiceProxy) Request() {
if p.realService == nil {
p.realService = &RealRemoteService{}
}
fmt.Println("调用代理")
// 调用真实对象的方法
p.realService.Request()
}
func main() {
// 创建远程服务代理
proxy := &RemoteServiceProxy{}
// 通过代理对象调用真实对象的方法,实现代理
proxy.Request()
// 调用代理
// 执行远程请求
}
通过代理对象可以在执行真实请求之前或之后进行一些额外的操作,同时实现了客户端与真实对象之间的解耦。
代理模式和装饰器模式的区别
-
目的不同:代理模式的目的是控制对原始对象的访问,提供额外的功能或者控制访问权限;装饰器模式的目的是动态地为对象添加额外的行为,而不需要修改原始对象的结构。
-
角色不同:代理模式涉及到三个角色:代理对象、原始对象和客户端;装饰器模式涉及到两个角色:装饰器对象和被装饰对象。
-
对原始对象的修改方式不同:代理模式通过创建一个代理对象来包裹原始对象,控制对原始对象的访问;装饰器模式通过创建一组装饰器对象来包裹原始对象,动态地添加额外的行为。
-
使用方式不同:代理模式很直接,客户端调用Proxy,Proxy调用原始类。装饰器模式则可以嵌套使用,A装饰B,B装饰C,C装饰D。