深入理解Go设计模式之代理模式

什么是代理模式 代理模式是一种结构型设计模式。 其中代理控制着对于原对象的访问, 并允许在将请求提交给原对象的前后进行一些处理,从而增强原对象的逻辑处理。 上面

什么是代理模式

代理模式是一种结构型设计模式。 其中代理控制着对于原对象的访问, 并允许在将请求提交给原对象的前后进行一些处理,从而增强原对象的逻辑处理。

上面的代理者我们一般叫做代理对象或者直接叫做代理-- Proxy,进行逻辑处理的原对象通常被称作服务对象,代理要跟服务对象实现相同的接口,才能让客户端傻傻分不清自己使用的到底是代理还是真正的服务对象,这样一来代理就能在客户端察觉不到的情况下对服务对象的处理逻辑进行增强。

什么叫对处理逻辑进行增强?或者换一种说法,叫对核心功能添加增强功能?举个例子来说,处理客户端查询用户订单信息的 API Handler 就是核心处理逻辑,增强逻辑就是我们需要在查询订单信息之前,验证请求是否是有效用户、记录请求的参数和返回的响应数据等等。

看了上面代理模式的解释,你可能还是觉得有点宽泛,下面咱们写一个简单的代码示例,这个过程中你差不多就会发现:“诶,原来这就是代理模式啊,我之前写代码的时候早就用过了~!” 下面我们一起开下这个例子吧。

代理模式使用演示

假设有一个代表小汽车的 Car 类型

type Car struct{}

小汽车要的主要行为就是可以让人驾驶,所以 Car 需要实现一个代表驾驶行为的接口(interface)Vehicle,该接口只有一个方法Drive()

type Vehicle interface {
    Drive()
}
type Car struct{}
func (c *Car) Drive() {
    fmt.Println("Car is being driven")
}

Car 的结构体指针通过实现Drive()方法实现了Vehicle接口。

现在我们只要实例化一个Car的实例,在实例上面调用Drive()方法就能让车开起来,不过如果我们的驾驶员现在还是个未成年,那么在地球的大部分国家都是不允许开车的,如果在开车时要加一个驾驶员的年龄限制,我们该怎么办呢? 给Car结构体加一个Age字段显然是不合理的,因为我们要表示的驾驶员的年龄而不是车的车龄。同理驾驶员年龄的判断我们也不应该加在 Car 实现的 Drive() 方法里, 这样会导致每个实现 Vehicle 接口的类型都要在自己的 Drive() 方法里加上类似的判断。

这个时候通常的做法是,加一个表示驾驶员的类型 Driver

type Driver struct {
    Age int
}

然后再来一个包装 Driver 和 Vehicle 类型的包装类型。

type CarProxy struct {
    vehicle    Vehicle
    driver *Driver
}
func NewCarProxy(driver *Driver) *CarProxy {
    return &CarProxy{&Car{}, driver}
}

这样的话我们接可以通过,用包装类型代理vehicle属性的 Drive() 行为时,给它加上驾驶员的年龄限制。

func (c *CarProxy) Drive() {
    if c.driver.Age >= 16 {
        c.vehicle.Drive()
    } else {
        fmt.Println("Driver too young!")
    }
}

我相信这个编程技巧大家在平时开发中都用过,这个其实就是代理模式。

现在我们通过代理模式给 Car 类型的 Drive() 行为扩充了检查驾驶员的行为,下面我们执行一下程序试试效果。

func main() {
	car := NewCarProxy(&Driver{12})
	car.Drive() // 输出 Driver too young!
	car2 := NewCarProxy(&Driver{22})
	car2.Drive() // 输出 Car is being driven
}

正如执行后的结果所示,我们不必为服务对象 -- Car 类型添加任何属性和方法。相反,我们只是在其上面的代理层把客户端 Drive() 方法的调用委托(英文术语叫delegate)给了其 vehicle 属性的 Drive 方法,并在之前添加了年龄检查行为,从而达到我们想要的效果。

看完例子后,相信大家都理解了写代码时怎么使用代理模式,下面我们从代码走出来,再更清晰的描述下代理模式它的整体结构。

看清代理模式

根据上面一开始的描述和后面的代码例子,我们总结出来,参与代理模式的一共有四种角色:客户端、服务接口、服务类和代理类,他们之间的关系用 UML 类图表示如下:

上面 UML 类图一共有四个角色,这四个角色在代理模式中的职责分别是。

  • 服务接口 (Ser­vice Inter­face) 声明了服务类要实现的接口。 服务类的业务处理逻辑就是实现在这里定义的接口方法中,代理类也必须遵循该接口才能伪装成服务对象。
  • 服务 (Ser­vice) 类,就是上面说的,提供实际业务逻辑的原对象。
  • 代理 (Proxy) 类包含一个服务对象作为成员变量。 代理完成其任务 (例如延迟初始化、记录日志、 访问控制和缓存等)后面会将请求传递给服务对象。通常情况下, 代理会对其服务对象的整个生命周期进行管理,来增强服务对象,这样与核心业务逻辑不相关的增强逻辑就可以由代理来实现
  • 客户端 (Client) 通过统一接口与服务或代理进行交互, 所以可在一切需要服务对象的代码中使用服务对象的代理,客户端完全不会感知到。

代理模式延伸

在代理模式中,通过让代理类实现跟服务类相同的接口,从而把代理类伪装成了服务类,客户端请求代理时,代理再把请求委派给其持有的真实服务类,在委派的过程中我们就可以添加增强逻辑。 那么如果我们再给代理类加个代理,代理的代理再加代理,那么其实就变成了另外一种设计模式--装饰器模式啦,其实装饰器模式本身就是代理模式的一个特殊应用,关于装饰器的内容,我们放到后面进行学习。

以上就是深入理解Go设计模式之代理模式的详细内容,更多关于Go 代理模式的资料请关注好代码网其它相关文章!

您可能有感兴趣的文章
golang并发编程的如何实现

golang如何实现redis的延时消息队列功能示例

golang中range在slice和map遍历中的注意事项

Golang命令行进行debug调试操作

golang的空标识符理解