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 类图表示如下:

 

  1. 服务接口:Vehicle接口是一个服务接口,定义了Drive()方法,用于描述所有交通工具(车辆)的驾驶行为。

  2. 服务(被代理对象):Car结构体是服务接口Vehicle的一个具体实现,它实现了Drive()方法,表示汽车的驾驶行为。

  3. 代理:CarProxy结构体是Vehicle接口的代理,它实现了Vehicle接口,并持有一个实际的服务对象(vehicle)以及一个Driver对象(driver)。CarProxy在实现Drive()方法时,首先根据Driver的年龄判断是否满足驾驶条件,若满足则调用实际服务对象的Drive()方法,否则输出"Driver too young!"。

  4. 客户端:在main()函数中,通过调用NewCarProxy()函数创建了两个CarProxy对象,每个CarProxy对象都对应一个Driver对象。然后通过调用Drive()方法来驾驶车辆。其中,第一个CarProxy对象的Driver对象年龄为12,不满足驾驶条件,输出"Driver too young!";第二个CarProxy对象的Driver对象年龄为22,满足驾驶条件,调用Car服务的Drive()方法,输出"Car is being driven"。

综上所述,服务接口是Vehicle,服务是Car,代理是CarProxy,客户端是main()函数中的代码。代理(CarProxy)在服务接口(Vehicle)的基础上增加了额外的控制逻辑,通过代理对象(CarProxy)来实现对实际服务对象(Car)的访问控制。

代理模式延伸

在代理模式中,通过让代理类实现跟服务类相同的接口,从而把代理类伪装成了服务类,客户端请求代理时,代理再把请求委派给其持有的真实服务类,在委派的过程中我们就可以添加增强逻辑。

如果我们把代理类当成服务对象再给代理类加个代理,代理的代理再加代理,那么就变成了另外一种设计模式--装饰器模式啦,其实装饰器模式本身就是代理模式的一个特殊应用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值