Golang学习笔记_35——代理模式

发布于:2025-02-24 ⋅ 阅读:(10) ⋅ 点赞:(0)

Golang学习笔记_32——适配器模式
Golang学习笔记_33——桥接模式
Golang学习笔记_34——组合模式



一、核心概念

1. 定义

代理模式(Proxy Pattern)是一种结构型设计模式,通过引入代理对象间接访问真实对象,从而在不修改原对象代码的前提下,控制访问增强功能(如日志、缓存、权限校验)。

2. 解决的问题

  • 访问控制:限制对真实对象的直接访问(如权限校验)
  • 功能扩展:在调用真实对象前后添加额外逻辑(如日志、缓存)
  • 资源优化:延迟加载高开销对象(如大文件、远程服务)

3. 核心角色

  1. Subject(抽象主题)
    定义真实对象和代理的公共接口(如UserService接口)
  2. RealSubject(真实主题)
    实现具体业务逻辑(如RealUserService处理数据库查询)
  3. Proxy(代理)
    持有真实对象的引用,并添加增强逻辑(如缓存代理UserServiceProxy

4. 类图

基本类图

下载文件示例类图

二、特点分析

优点

  1. 职责分离:代理处理非核心逻辑(如日志),真实对象专注业务
  2. 扩展性强:新增代理不影响原有代码,符合开闭原则
  3. 资源保护:通过代理控制敏感操作(如权限校验)

缺点

  1. 性能损耗:动态代理(如反射)可能降低执行效率
  2. 复杂度增加:需维护代理与真实对象的关系

三、适用场景

  1. 权限控制:接口调用前的身份验证
  2. 延迟加载:按需加载大资源(如图片、文件)
  3. 日志监控:记录方法调用参数和结果
  4. 远程调用:RPC框架通过代理隐藏网络细节

四、代码示例(Go语言)

1. 文件下载代理(含权限校验)

package proxydemo

import "fmt"

// 抽象主题 文件接口
type File interface {
   Download()
}

// 真实主题 真实文件
type RealFile struct {
   Name string
}

func (r *RealFile) Download() {
   fmt.Println("Downloading " + r.Name)
}

// 代理:文件代理
type FileProxy struct {
   realFile *RealFile
   userRole string
}

func NewFileProxy(filename, role string) *FileProxy {
   return &FileProxy{
      realFile: &RealFile{Name: filename},
      userRole: role,
   }
}

func (f *FileProxy) Download() {
   if f.userRole == "admin" {
      f.realFile.Download()
   } else {
      fmt.Println("You don't have permission to download " + f.realFile.Name)
   }
}

func test_downloadFile(role string) {
   adminFile := NewFileProxy("adminFile.txt", role)
   adminFile.Download()
}

测试方法

package proxydemo

import "testing"

func Test_test_downloadFile(t *testing.T) {
	type args struct {
		role string
	}
	tests := []struct {
		name string
		args args
	}{
		{
			name: "admin",
			args: args{
				role: "admin",
			},
		},
		{
			name: "user",
			args: args{
				role: "user",
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			test_downloadFile(tt.args.role)
		})
	}
}


输出结果

=== RUN   Test_test_downloadFile
=== RUN   Test_test_downloadFile/admin
Downloading adminFile.txt
=== RUN   Test_test_downloadFile/user
You don't have permission to download adminFile.txt
--- PASS: Test_test_downloadFile (0.00s)
    --- PASS: Test_test_downloadFile/admin (0.00s)
    --- PASS: Test_test_downloadFile/user (0.00s)
PASS

2. 缓存代理(用户信息查询)

package proxydemo

import "fmt"

type UserService interface {
   GetUser(id int) (string, error)
}

type RealUserService struct{}

func (r *RealUserService) GetUser(id int) (string, error) {
   // 模拟从数据库中获取用户信息
   return fmt.Sprintf("User%d", id), nil
}

type CachedUserService struct {
   realService UserService
   cache       map[int]string
}

func NewCachedUserService() *CachedUserService {
   return &CachedUserService{
      realService: &RealUserService{},
      cache:       make(map[int]string),
   }
}

func (c *CachedUserService) GetUser(id int) (string, error) {
   if user, ok := c.cache[id]; ok {
      fmt.Println("Returning user from cache")
      return user, nil
   }
   fmt.Println("Fetching user from real service")
   user, err := c.realService.GetUser(id)
   if err != nil {
      return "", err
   }
   c.cache[id] = user
   return user, nil
}

func test_cache() {
   cachedService := NewCachedUserService()
   user, _ := cachedService.GetUser(1)
   fmt.Println("User:", user)
   fmt.Println("-----------------")
   user, _ = cachedService.GetUser(1)
   fmt.Println("User:", user)
}

输出结果

=== RUN   Test_test_cache
Fetching user from real service
User: User1
-----------------
Returning user from cache
User: User1
--- PASS: Test_test_cache (0.00s)
PASS

五、高级应用

  1. 动态代理
    Go可通过反射实现动态代理(需处理性能损耗):

    func DynamicProxy(target interface{}, preHandler func()) interface{} {
        val := reflect.ValueOf(target)
        return reflect.New(val.Type()).Interface()
    }
    
  2. 组合代理链
    多个代理嵌套使用(如先鉴权再缓存):

    authProxy := NewAuthProxy(userRole)
    cachedProxy := NewCachedProxy(authProxy)
    cachedProxy.Download()
    

六、与其他模式对比

模式 核心目标 关键区别
装饰器 动态添加功能 关注功能叠加
适配器 接口转换 解决兼容性问题
代理 控制访问/增强前置后置逻辑 侧重访问限制和流程控制

七、总结

代理模式通过间接访问逻辑增强,解决了以下问题:

  1. 安全控制:通过代理拦截非法请求
  2. 功能扩展:无侵入式添加日志、缓存等逻辑
  3. 资源优化:延迟加载降低系统开销

在Go中实现时需注意:

  • 优先使用接口定义抽象主题
  • 代理与真实对象需实现相同接口
  • 避免过度使用动态代理(反射有性能损耗)