结构型
结构型模式主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题
。结构型模式包括:代理模式、桥接模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式。
代理模式
代理模式在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。
由于Golang
和java
的差异性,我们无法比较方便的利用反射实现动态代理,所以这里主要介绍静态代理
静态代理
例一
package proxy
import (
"log"
"time"
)
// IUser IUser
type IUser interface {
Login(username, password string) error
}
// User 用户
type User struct {
}
// Login 用户登录
func (u *User) Login(username, password string) error {
// 不实现细节
return nil
}
// UserProxy 代理类,包含了原始类,并且和原始类实现了相同接口IUser
type UserProxy struct {
user *User
}
// NewUserProxy NewUserProxy
func NewUserProxy(user *User) *UserProxy {
return &UserProxy{
user: user,
}
}
// Login 登录,和 user 实现相同的接口
func (p *UserProxy) Login(username, password string) error {
// before 这里可能会有一些统计的逻辑
start := time.Now()
// 这里是原有的业务逻辑
if err := p.user.Login(username, password); err != nil {
return err
}
// after 这里可能也有一些监控统计的逻辑
log.Printf("user login cost time: %s", time.Now().Sub(start))
return nil
}
例二
// 代理模式:代理模式在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同。
package main
// 场景举例:为 UserController 的 register 和 login 方法增加 metrics 打点
// 用户类,业务中用到的实体类
type UserVo struct{}
// 抽象接口,在业务代码中使用接口
type IUserController interface {
login(telephone, password string) UserVo
register(telephone, password string) UserVo
}
// UserController 类,被代理类,处理实际业务
type UserController struct{}
func (uc UserController) register(telephone, password string) UserVo {
// ... register 逻辑 ...
return UserVo{}
}
func (uc UserController) login(telephone, password string) UserVo {
// ... login 逻辑 ...
return UserVo{}
}
// metrics 类
type MetricsCollector struct{}
func (ms MetricsCollector) recordRequest() {}
func NewMetricsCollector() MetricsCollector {
return MetricsCollector{}
}
// UserController的代理类,组合其他类对UserController类相关功能增强
type UserControllerProxy struct {
userController UserController
metricsCollector MetricsCollector
}
func NewUserControllerProxy(userController UserController) UserControllerProxy {
return UserControllerProxy{
userController: userController,
metricsCollector: NewMetricsCollector(),
}
}
// 需要和被代理类实现相同接口,这样才可以把代理类传给接口,然后使用接口,感觉上就像和在使用被代理类一样,实际已经对功能进行了增强
func (ucp UserControllerProxy) register(telephone, password string) UserVo {
userVo := ucp.userController.register(telephone, password)
ucp.metricsCollector.recordRequest()
return userVo
}
func (ucp UserControllerProxy) login(telephone, password string) UserVo {
userVo := ucp.userController.login(telephone, password)
ucp.metricsCollector.recordRequest()
return userVo
}
应用场景
代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。除此之外,代理模式还可以用在 RPC
、缓存等应用场景中。