代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

使用场景

  1. RPC(远程过程调用):在分布式系统中,使用代理模式可以实现远程过程调用(RPC)。代理对象可以隐藏底层的网络通信细节,并提供一个本地调用的接口,客户端通过代理对象调用远程服务。
  2. 数据库访问:在应用程序中使用代理模式来管理数据库连接和查询。代理对象可以实现连接池、懒加载数据等功能,以提高数据库访问的效率和性能。
  3. 缓存代理:在应用程序中使用代理模式来实现缓存机制。代理对象可以在访问对象时缓存数据,并在下次访问相同对象时直接返回缓存数据,以提高访问速度和减少资源消耗。
  4. 权限控制:在Web应用程序中,可以使用代理模式来实现权限控制。代理对象可以根据用户的权限来控制对敏感资源的访问,只有经过授权的用户才能访问这些资源。
  5. 日志记录:在应用程序中使用代理模式来记录日志。代理对象可以在访问对象时记录日志,以便后续调试、性能监控等目的。
  6. 服务代理:在微服务架构中,可以使用代理模式来实现服务代理。代理对象可以处理服务之间的通信、负载均衡、服务发现等功能,从而简化服务间的交互。

代码实现

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
65
66
67
68
69
package main

import (
"fmt"
"sync"
)

// Database 是模拟的数据库结构
type Database struct {
data map[string]string
}

// NewDatabase 创建一个新的数据库对象
func NewDatabase() *Database {
return &Database{
data: make(map[string]string),
}
}

// GetData 根据键获取数据
func (d *Database) GetData(key string) string {
return d.data[key]
}

// ProxyDatabase 是代理数据库,它在访问数据之前可以缓存数据
type ProxyDatabase struct {
database *Database
cache map[string]string
mu sync.Mutex
}

// NewProxyDatabase 创建一个新的代理数据库对象
func NewProxyDatabase() *ProxyDatabase {
return &ProxyDatabase{
database: NewDatabase(),
cache: make(map[string]string),
}
}

// GetData 根据键获取数据,先从缓存中查找,如果不存在则从数据库中获取,并更新缓存
func (p *ProxyDatabase) GetData(key string) string {
// 先检查缓存
p.mu.Lock()
value, ok := p.cache[key]
p.mu.Unlock()

if !ok {
// 如果缓存中不存在,则从数据库中获取数据
value = p.database.GetData(key)

// 更新缓存
p.mu.Lock()
p.cache[key] = value
p.mu.Unlock()
}

return value
}

func main() {
// 创建代理数据库
proxyDB := NewProxyDatabase()

// 第一次获取数据,会从数据库中获取并更新缓存
fmt.Println(proxyDB.GetData("key1"))

// 第二次获取数据,会直接从缓存中获取
fmt.Println(proxyDB.GetData("key1"))
}

结语

装饰模式和代理模式很相似,对装饰模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。

两者的区别是:

  • 代理模式用于控制对对象的访问,代理类隐藏了对象的具体信息,客户端通过代理类来访问对象。通常在代理类内部创建被代理对象的实例。

  • 装饰模式用于动态地添加对象的功能,通常将原始对象作为参数传递进装饰器的内部,然后在装饰器中包装原始对象,并添加额外的功能。

代理模式在Go中的应用场景多种多样,包括但不限于远程过程调用、缓存代理、权限控制、日志记录等。通过使用代理模式,我们可以在不改变原有代码结构的情况下,为系统添加额外的功能和控制,提高系统的灵活性和可维护性。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。