缘起
最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之
代理模式
代理模式(Proxy Pattern)指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型设计模式。
使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
_
场景
- 某订单管理系统, 允许用户对订单进行增删改查
- 后增加日志需求, 要求对订单的Save和Delete操作, 记录操作日志
- 后再增加权限需求, 要求对订单的Save操作, 检查order.save权限; Delete操作, 检查order.delete权限
设计
- IUser: 定义用户信息及权限信息
- Order: 订单实体类
- IOrderService: 订单服务, 提供订单的增删改查方法
- tMockOrderService: 虚拟订单服务, 提供对订单的增删改查的基本功能
- tLoggableOrderService: 根据日志需求, 新增的订单服务代理, 提供日志记录功能
- tSecureOrderService: 根据权限需求, 新增的订单服务代理, 提供权限检查功能
单元测试 - proxy_pattern_test.go
proxy_pattern_test.go依次创建并调用了三种订单服务, 后两种以代理模式添加日志记录和权限检查功能.
package structural_patterns_testimport ("learning/gooop/structural_patterns/proxy""strings""testing""time")func Test_ProxyPattern(t *testing.T) {admin := proxy.NewMockUser(1, "管理员", strings.Split("order.load,order.save,order.delete", ","))guest := proxy.NewMockUser(2, "张三", strings.Split("order.load", ","))order := &proxy.Order{ID: 1,OrderNo: "mock-order-1",CustomerID: 1,OrderDate: time.Now().Format("2006-01-02"),ReceiveAddress: "mock address",}os1 := proxy.NewMockOrderService()fnCallAndLog := func(fn func() error) {e := fn()if e != nil {t.Log(e)}}fnCallAndLog(func() error {return os1.Save(order, admin)})fnCallAndLog(func() error {return os1.Delete(order.ID, admin)})os2 := proxy.NewLoggableOrderService(os1)fnCallAndLog(func() error {return os2.Save(order, admin)})fnCallAndLog(func() error {return os2.Delete(order.ID, admin)})os3 := proxy.NewLoggableOrderService(proxy.NewSecureOrderService(os1))fnCallAndLog(func() error {return os3.Save(order, admin)})fnCallAndLog(func() error {return os3.Delete(order.ID, admin)})fnCallAndLog(func() error {return os3.Save(order, guest)})fnCallAndLog(func() error {return os3.Delete(order.ID, guest)})}
测试输出
$ go test -v proxy_pattern_test.go=== RUN Test_ProxyPatternIOrderService.Save, user=管理员, order=&{1 mock-order-1 1 2021-02-01 mock address}, error=<nil>IOrderService.Delete, user=管理员, order.id=1, error=<nil>IOrderService.Save, user=管理员, order=&{1 mock-order-1 1 2021-02-01 mock address}, error=<nil>IOrderService.Delete, user=管理员, order.id=1, error=<nil>IOrderService.Save, user=张三, order=&{1 mock-order-1 1 2021-02-01 mock address}, error=permission deniedproxy_pattern_test.go:26: permission deniedIOrderService.Delete, user=张三, order.id=1, error=permission deniedproxy_pattern_test.go:26: permission denied--- PASS: Test_ProxyPattern (0.00s)PASSok command-line-arguments 0.002s
IUser.go
定义系统用户接口, 用于订单服务上下文
package proxytype IUser interface {ID() intName() stringAllowed(perm string) bool}
tMockUser.go
tMockUser实现IUser接口, 封装运行时用户信息
package proxytype tMockUser struct {iID intsName stringmPermissions map[string]bool}func NewMockUser(id int, name string, perms []string) IUser {it := &tMockUser{id, name, make(map[string]bool, 16),}for _,k := range perms {it.mPermissions[k] = true}return it}func (me *tMockUser) ID() int {return me.iID}func (me *tMockUser) Name() string {return me.sName}func (me *tMockUser) Allowed(perm string) bool {if me.mPermissions == nil {return false}_,ok := me.mPermissions[perm]return ok}
Order.go
定义订单信息实体
package proxytype Order struct {ID intOrderNo stringCustomerID intOrderDate stringReceiveAddress string}
IOrderService.go
订单服务接口
package proxytype IOrderService interface {Load(id int) (error, *Order)Save(order *Order, user IUser) errorDelete(id int, user IUser) error}
tMockOrderService.go
虚拟订单服务, 实现IOrderService接口, 提供订单的基本增删改查功能
package proxyimport ("errors")type tMockOrderService struct {mItems map[int]*Order}func NewMockOrderService() IOrderService {return &tMockOrderService{mItems: make(map[int]*Order, 16),}}func (me *tMockOrderService) Load(id int) (error, *Order) {//fmt.Printf("tMockOrderService.Load, id=%v\n", id)it, ok := me.mItems[id]if ok {return nil, it} else {return errors.New("no such order"), nil}}func (me *tMockOrderService) Save(it *Order, user IUser) error {me.mItems[it.ID] = itreturn nil}func (me *tMockOrderService) Delete(id int, user IUser) error {_,ok := me.mItems[id]if ok {delete(me.mItems, id)} else {return errors.New("no such order")}return nil}
tLoggableOrderService.go
订单服务代理, 以代理模式增加订单的Save和Delete日志
package proxyimport "fmt"type tLoggableOrderService struct {mOrderService IOrderService}func NewLoggableOrderService(service IOrderService) IOrderService {return &tLoggableOrderService{mOrderService: service,}}func (me *tLoggableOrderService) Load(id int) (error, *Order) {return me.mOrderService.Load(id)}func (me *tLoggableOrderService) Save(it *Order, user IUser) error {e := me.mOrderService.Save(it, user)fmt.Printf("IOrderService.Save, user=%v, order=%v, error=%v\n", user.Name(), it, e)return e}func (me *tLoggableOrderService) Delete(id int, user IUser) error {e := me.mOrderService.Delete(id, user)fmt.Printf("IOrderService.Delete, user=%v, order.id=%v, error=%v\n", user.Name(), id, e)return e}
tSecureOrderService.go
订单服务代理, 以代理模式增加订单操作的权限检查功能
package proxyimport ("errors")type tSecureOrderService struct {mOrderService IOrderService}var gErrorPermissionDenied = errors.New("permission denied")func NewSecureOrderService(service IOrderService) IOrderService {return &tSecureOrderService{mOrderService: service,}}func (me *tSecureOrderService) Load(id int) (error, *Order) {return me.mOrderService.Load(id)}func (me *tSecureOrderService) Save(it *Order, user IUser) error {if !user.Allowed("order.save") {return gErrorPermissionDenied}return me.mOrderService.Save(it, user)}func (me *tSecureOrderService) Delete(id int, user IUser) error {if !user.Allowed("order.delete") {return gErrorPermissionDenied}return me.mOrderService.Delete(id, user)}
代理模式小结
静态代理vs动态代理
(1) 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。
(2)动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
代理模式的优点
(1)代理模式能将代理对象与真实被调用目标对象分离。
(2)在一定程度上降低了系统的耦合性,扩展性好。
(3)可以起到保护目标对象的作用。
(4)可以增强目标对象的功能。
代理模式的缺点
(1)代理模式会造成系统设计中类的数量增加。
(2)在客户端和目标对象中增加一个代理对象,会导致处理请求的速度变慢。
(end)
