引言
代理模式(Proxy Pattern)是一种结构型设计模式,通过代理对象控制对真实对象的访问,广泛用于权限控制、延迟加载和功能增强。在 Go 语言中,凭借其简洁的接口和结构体组合,代理模式的实现既优雅又高效。本文将通过一个原创的在线学习平台场景,详细讲解代理模式的概念、实现和应用,带您从零到一掌握这一模式。
本文适合 Go 语言初学者、设计模式爱好者以及希望分享技术内容的开发者。让我们开始吧!
1. 什么是代理模式?
代理模式通过引入一个代理对象,充当客户端与真实对象之间的中介,控制访问并可添加额外逻辑。代理就像一个“门卫”,在客户端访问资源前进行验证或处理。例如,在在线学习平台中,代理可以检查用户权限,记录访问日志,甚至缓存结果,而无需修改核心课程服务。
代理模式的优点
- 访问控制:限制或验证对真实对象的访问。
- 功能增强:添加日志、缓存等逻辑。
- 延迟加载:推迟昂贵资源初始化。
- 解耦:客户端与真实对象解耦。
代理模式的缺点
- 复杂性:引入代理增加系统复杂性。
- 性能:代理层可能带来轻微开销。
2. 代理模式的核心角色
代理模式包含以下角色:
- 抽象主题(Subject):定义真实对象和代理的公共接口。
- 真实主题(Real Subject):实现核心功能。
- 代理(Proxy):控制访问,添加额外逻辑。
- 客户端(Client):通过抽象主题与代理交互。
在 Go 中,我们用接口定义主题,用结构体实现代理和真实主题。
3. 在线学习平台场景
我们设计一个在线学习平台的课程访问控制场景:
背景:你开发了一个学习平台,学生可访问课程视频和资料。要求:
- 付费用户可访问高级课程。
- 免费用户只能访问基础课程。
- 每次访问需记录日志(用户ID和时间)。
直接访问课程可能导致未授权访问或日志缺失,因此需要代理控制访问。
目标:通过代理模式,实现权限控制和日志记录。
4. Go 语言实现
下面,我们分步骤实现代理模式。
4.1 定义抽象主题
定义课程访问接口 CourseService
。
1
2
3
4
5
6
|
package main
// CourseService 是抽象主题接口,定义课程访问操作
type CourseService interface {
AccessCourse(userID string, courseID string) string
}
|
说明:
CourseService
定义了 AccessCourse
方法,接受用户ID和课程ID。
- 客户端和代理都通过此接口操作。
4.2 定义真实主题
实现真实课程服务 RealCourseService
。
1
2
3
4
5
6
7
8
9
|
package main
// RealCourseService 是真实主题,提供实际的课程访问功能
type RealCourseService struct{}
// AccessCourse 实现 CourseService 接口,返回课程访问结果
func (r *RealCourseService) AccessCourse(userID string, courseID string) string {
return "User " + userID + " is accessing course: " + courseID
}
|
说明:
RealCourseService
提供核心访问功能。
- 实际系统中,这可能涉及加载资源。
4.3 定义代理
创建代理 CourseAccessProxy
,添加权限检查和日志。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package main
import (
"fmt"
"time"
)
// User 模拟用户信息
type User struct {
ID string
IsPremium bool // 是否为付费用户
}
// CourseAccessProxy 是代理,控制课程访问并记录日志
type CourseAccessProxy struct {
realService *RealCourseService
users map[string]User // 模拟用户数据库
}
// NewCourseAccessProxy 创建代理实例
func NewCourseAccessProxy() *CourseAccessProxy {
// 初始化用户数据(模拟数据库)
users := map[string]User{
"user1": {ID: "user1", IsPremium: true},
"user2": {ID: "user2", IsPremium: false},
}
return &CourseAccessProxy{
realService: &RealCourseService{},
users: users,
}
}
// AccessCourse 实现 CourseService 接口,添加权限检查和日志
func (p *CourseAccessProxy) AccessCourse(userID string, courseID string) string {
// 记录访问日志
p.logAccess(userID, courseID)
// 检查用户是否存在
user, exists := p.users[userID]
if !exists {
return "Access denied: User " + userID + " not found"
}
// 检查课程权限
if courseID == "premium_course" && !user.IsPremium {
return "Access denied: User " + userID + " is not a premium user"
}
// 授权通过,调用真实服务
return p.realService.AccessCourse(userID, courseID)
}
// logAccess 记录访问日志
func (p *CourseAccessProxy) logAccess(userID string, courseID string) {
timestamp := time.Now().Format("2006-01-02 15:04:05")
fmt.Printf("Log: User %s accessed course %s at %s\n", userID, courseID, timestamp)
}
|
说明:
- 代理持有
RealCourseService
引用,控制访问。
- 实现权限检查(用户存在性和付费状态)和日志记录。
NewCourseAccessProxy
初始化用户数据。
4.4 客户端代码
模拟学习平台通过代理访问课程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main
import "fmt"
// LearningPlatform 模拟在线学习平台
type LearningPlatform struct{}
func (lp *LearningPlatform) AccessCourse(service CourseService, userID string, courseID string) {
fmt.Printf("User %s requesting course %s:\n", userID, courseID)
result := service.AccessCourse(userID, courseID)
fmt.Println(result)
fmt.Println()
}
func main() {
// 创建学习平台
platform := &LearningPlatform{}
// 创建代理
proxy := NewCourseAccessProxy()
// 测试场景1:付费用户访问高级课程
platform.AccessCourse(proxy, "user1", "premium_course")
// 测试场景2:免费用户访问高级课程
platform.AccessCourse(proxy, "user2", "premium_course")
// 测试场景3:免费用户访问基础课程
platform.AccessCourse(proxy, "user2", "basic_course")
// 测试场景4:不存在的用户
platform.AccessCourse(proxy, "user3", "basic_course")
}
|
说明:
LearningPlatform
依赖 CourseService
接口。
- 测试不同用户和课程的访问场景。
4.5 运行结果
运行代码,输出(时间根据实际运行):
User user1 requesting course premium_course:
Log: User user1 accessed course premium_course at 2025-05-14 01:14:05
User user1 is accessing course: premium_course
User user2 requesting course premium_course:
Log: User user2 accessed course premium_course at 2025-05-14 01:14:05
Access denied: User user2 is not a premium user
User user2 requesting course basic_course:
Log: User user2 accessed course basic_course at 2025-05-14 01:14:05
User user2 is accessing course: basic_course
User user3 requesting course basic_course:
Log: User user3 accessed course basic_course at 2025-05-14 01:14:05
Access denied: User user3 not found
5. 扩展:添加缓存功能
为代理添加缓存功能,减少重复调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package main
import (
"fmt"
"time"
)
// CourseAccessProxyWithCache 是带缓存的代理
type CourseAccessProxyWithCache struct {
realService *RealCourseService
users map[string]User
cache map[string]string // 缓存:key=userID:courseID
}
// NewCourseAccessProxyWithCache 创建带缓存的代理
func NewCourseAccessProxyWithCache() *CourseAccessProxyWithCache {
users := map[string]User{
"user1": {ID: "user1", IsPremium: true},
"user2": {ID: "user2", IsPremium: false},
}
return &CourseAccessProxyWithCache{
realService: &RealCourseService{},
users: users,
cache: make(map[string]string),
}
}
// AccessCourse 实现 CourseService 接口,添加缓存、权限检查和日志
func (p *CourseAccessProxyWithCache) AccessCourse(userID string, courseID string) string {
// 记录日志
p.logAccess(userID, courseID)
// 检查缓存
cacheKey := userID + ":" + courseID
if result, found := p.cache[cacheKey]; found {
return "Cached: " + result
}
// 检查用户
user, exists := p.users[userID]
if !exists {
result := "Access denied: User " + userID + " not found"
p.cache[cacheKey] = result
return result
}
// 检查权限
if courseID == "premium_course" && !user.IsPremium {
result := "Access denied: User " + userID + " is not a premium user"
p.cache[cacheKey] = result
return result
}
// 调用真实服务
result := p.realService.AccessCourse(userID, courseID)
p.cache[cacheKey] = result
return result
}
// logAccess 记录访问日志
func (p *CourseAccessProxyWithCache) logAccess(userID string, courseID string) {
timestamp := time.Now().Format("2006-01-02 15:04:05")
fmt.Printf("Log: User %s accessed course %s at %s\n", userID, courseID, timestamp)
}
|
更新 main.go
测试缓存:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main
import "fmt"
// LearningPlatform 模拟在线学习平台
type LearningPlatform struct{}
func (lp *LearningPlatform) AccessCourse(service CourseService, userID string, courseID string) {
fmt.Printf("User %s requesting course %s:\n", userID, courseID)
result := service.AccessCourse(userID, courseID)
fmt.Println(result)
fmt.Println()
}
func main() {
// 创建学习平台
platform := &LearningPlatform{}
// 创建带缓存的代理
proxy := NewCourseAccessProxyWithCache()
// 测试场景1:付费用户访问高级课程(首次)
platform.AccessCourse(proxy, "user1", "premium_course")
// 测试场景2:付费用户再次访问高级课程(从缓存)
platform.AccessCourse(proxy, "user1", "premium_course")
// 测试场景3:免费用户访问基础课程(首次)
platform.AccessCourse(proxy, "user2", "basic_course")
// 测试场景4:免费用户再次访问基础课程(从缓存)
platform.AccessCourse(proxy, "user2", "basic_course")
}
|
新增输出:
User user1 requesting course premium_course:
Log: User user1 accessed course premium_course at 2025-05-14 01:14:05
User user1 is accessing course: premium_course
User user1 requesting course premium_course:
Log: User user1 accessed course premium_course at 2025-05-14 01:14:05
Cached: User user1 is accessing course: premium_course
说明:
- 缓存减少了对真实服务的调用,提升性能。
- 扩展功能无需修改核心服务。
6. Go 语言中的代理特性
Go 的代理模式特点:
- 隐式接口:简化接口实现。
- 组合:通过引用实现扩展。
- 轻量:避免复杂继承。
- 静态检查:编译时验证接口。
与 Java 相比,Go 的代理更直观,但需手动实现逻辑。
7. 应用场景
代理模式适用于:
- 访问控制:权限验证。
- 延迟加载:延迟初始化资源。
- 日志记录:记录操作。
- 缓存:缓存数据。
- 远程代理:隐藏远程调用。
- 保护代理:限制敏感资源。
8. 最佳实践与注意事项
最佳实践
- 精简接口:只定义必要方法。
- 清晰职责:代理专注控制和增强。
- 工厂函数:简化实例创建。
- 日志和错误:妥善处理。
- 测试:确保代理逻辑正确。
注意事项
- 避免代理逻辑过复杂。
- 缓存需考虑失效策略。
- 评估性能开销。
9. 总结
代理模式是控制访问的理想选择,在 Go 中通过接口和结构体实现简洁高效。本文通过在线学习平台场景,展示了权限控制、日志和缓存的实现,体现了代理模式的实用性。
评论 0