缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用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依次创建并调用了三种订单服务, 后两种以代理模式添加日志记录和权限检查功能.

  1. package structural_patterns_test
  2. import (
  3. "learning/gooop/structural_patterns/proxy"
  4. "strings"
  5. "testing"
  6. "time"
  7. )
  8. func Test_ProxyPattern(t *testing.T) {
  9. admin := proxy.NewMockUser(1, "管理员", strings.Split("order.load,order.save,order.delete", ","))
  10. guest := proxy.NewMockUser(2, "张三", strings.Split("order.load", ","))
  11. order := &proxy.Order{
  12. ID: 1,
  13. OrderNo: "mock-order-1",
  14. CustomerID: 1,
  15. OrderDate: time.Now().Format("2006-01-02"),
  16. ReceiveAddress: "mock address",
  17. }
  18. os1 := proxy.NewMockOrderService()
  19. fnCallAndLog := func(fn func() error) {
  20. e := fn()
  21. if e != nil {
  22. t.Log(e)
  23. }
  24. }
  25. fnCallAndLog(func() error {
  26. return os1.Save(order, admin)
  27. })
  28. fnCallAndLog(func() error {
  29. return os1.Delete(order.ID, admin)
  30. })
  31. os2 := proxy.NewLoggableOrderService(os1)
  32. fnCallAndLog(func() error {
  33. return os2.Save(order, admin)
  34. })
  35. fnCallAndLog(func() error {
  36. return os2.Delete(order.ID, admin)
  37. })
  38. os3 := proxy.NewLoggableOrderService(proxy.NewSecureOrderService(os1))
  39. fnCallAndLog(func() error {
  40. return os3.Save(order, admin)
  41. })
  42. fnCallAndLog(func() error {
  43. return os3.Delete(order.ID, admin)
  44. })
  45. fnCallAndLog(func() error {
  46. return os3.Save(order, guest)
  47. })
  48. fnCallAndLog(func() error {
  49. return os3.Delete(order.ID, guest)
  50. })
  51. }

测试输出

  1. $ go test -v proxy_pattern_test.go
  2. === RUN Test_ProxyPattern
  3. IOrderService.Save, user=管理员, order=&{1 mock-order-1 1 2021-02-01 mock address}, error=<nil>
  4. IOrderService.Delete, user=管理员, order.id=1, error=<nil>
  5. IOrderService.Save, user=管理员, order=&{1 mock-order-1 1 2021-02-01 mock address}, error=<nil>
  6. IOrderService.Delete, user=管理员, order.id=1, error=<nil>
  7. IOrderService.Save, user=张三, order=&{1 mock-order-1 1 2021-02-01 mock address}, error=permission denied
  8. proxy_pattern_test.go:26: permission denied
  9. IOrderService.Delete, user=张三, order.id=1, error=permission denied
  10. proxy_pattern_test.go:26: permission denied
  11. --- PASS: Test_ProxyPattern (0.00s)
  12. PASS
  13. ok command-line-arguments 0.002s

IUser.go

定义系统用户接口, 用于订单服务上下文

  1. package proxy
  2. type IUser interface {
  3. ID() int
  4. Name() string
  5. Allowed(perm string) bool
  6. }

tMockUser.go

tMockUser实现IUser接口, 封装运行时用户信息

  1. package proxy
  2. type tMockUser struct {
  3. iID int
  4. sName string
  5. mPermissions map[string]bool
  6. }
  7. func NewMockUser(id int, name string, perms []string) IUser {
  8. it := &tMockUser{
  9. id, name, make(map[string]bool, 16),
  10. }
  11. for _,k := range perms {
  12. it.mPermissions[k] = true
  13. }
  14. return it
  15. }
  16. func (me *tMockUser) ID() int {
  17. return me.iID
  18. }
  19. func (me *tMockUser) Name() string {
  20. return me.sName
  21. }
  22. func (me *tMockUser) Allowed(perm string) bool {
  23. if me.mPermissions == nil {
  24. return false
  25. }
  26. _,ok := me.mPermissions[perm]
  27. return ok
  28. }

Order.go

定义订单信息实体

  1. package proxy
  2. type Order struct {
  3. ID int
  4. OrderNo string
  5. CustomerID int
  6. OrderDate string
  7. ReceiveAddress string
  8. }

IOrderService.go

订单服务接口

  1. package proxy
  2. type IOrderService interface {
  3. Load(id int) (error, *Order)
  4. Save(order *Order, user IUser) error
  5. Delete(id int, user IUser) error
  6. }

tMockOrderService.go

虚拟订单服务, 实现IOrderService接口, 提供订单的基本增删改查功能

  1. package proxy
  2. import (
  3. "errors"
  4. )
  5. type tMockOrderService struct {
  6. mItems map[int]*Order
  7. }
  8. func NewMockOrderService() IOrderService {
  9. return &tMockOrderService{
  10. mItems: make(map[int]*Order, 16),
  11. }
  12. }
  13. func (me *tMockOrderService) Load(id int) (error, *Order) {
  14. //fmt.Printf("tMockOrderService.Load, id=%v\n", id)
  15. it, ok := me.mItems[id]
  16. if ok {
  17. return nil, it
  18. } else {
  19. return errors.New("no such order"), nil
  20. }
  21. }
  22. func (me *tMockOrderService) Save(it *Order, user IUser) error {
  23. me.mItems[it.ID] = it
  24. return nil
  25. }
  26. func (me *tMockOrderService) Delete(id int, user IUser) error {
  27. _,ok := me.mItems[id]
  28. if ok {
  29. delete(me.mItems, id)
  30. } else {
  31. return errors.New("no such order")
  32. }
  33. return nil
  34. }

tLoggableOrderService.go

订单服务代理, 以代理模式增加订单的Save和Delete日志

  1. package proxy
  2. import "fmt"
  3. type tLoggableOrderService struct {
  4. mOrderService IOrderService
  5. }
  6. func NewLoggableOrderService(service IOrderService) IOrderService {
  7. return &tLoggableOrderService{
  8. mOrderService: service,
  9. }
  10. }
  11. func (me *tLoggableOrderService) Load(id int) (error, *Order) {
  12. return me.mOrderService.Load(id)
  13. }
  14. func (me *tLoggableOrderService) Save(it *Order, user IUser) error {
  15. e := me.mOrderService.Save(it, user)
  16. fmt.Printf("IOrderService.Save, user=%v, order=%v, error=%v\n", user.Name(), it, e)
  17. return e
  18. }
  19. func (me *tLoggableOrderService) Delete(id int, user IUser) error {
  20. e := me.mOrderService.Delete(id, user)
  21. fmt.Printf("IOrderService.Delete, user=%v, order.id=%v, error=%v\n", user.Name(), id, e)
  22. return e
  23. }

tSecureOrderService.go

订单服务代理, 以代理模式增加订单操作的权限检查功能

  1. package proxy
  2. import (
  3. "errors"
  4. )
  5. type tSecureOrderService struct {
  6. mOrderService IOrderService
  7. }
  8. var gErrorPermissionDenied = errors.New("permission denied")
  9. func NewSecureOrderService(service IOrderService) IOrderService {
  10. return &tSecureOrderService{
  11. mOrderService: service,
  12. }
  13. }
  14. func (me *tSecureOrderService) Load(id int) (error, *Order) {
  15. return me.mOrderService.Load(id)
  16. }
  17. func (me *tSecureOrderService) Save(it *Order, user IUser) error {
  18. if !user.Allowed("order.save") {
  19. return gErrorPermissionDenied
  20. }
  21. return me.mOrderService.Save(it, user)
  22. }
  23. func (me *tSecureOrderService) Delete(id int, user IUser) error {
  24. if !user.Allowed("order.delete") {
  25. return gErrorPermissionDenied
  26. }
  27. return me.mOrderService.Delete(id, user)
  28. }

代理模式小结

静态代理vs动态代理
(1) 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。
(2)动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
代理模式的优点
(1)代理模式能将代理对象与真实被调用目标对象分离。
(2)在一定程度上降低了系统的耦合性,扩展性好。
(3)可以起到保护目标对象的作用。
(4)可以增强目标对象的功能。
代理模式的缺点
(1)代理模式会造成系统设计中类的数量增加。
(2)在客户端和目标对象中增加一个代理对象,会导致处理请求的速度变慢。

(end)