Go实现23种设计模式—门面模式(外观模式)
一 类别
门面模式又叫外观模式,是一种常用的封装模式
二 概述
给一系列具有关联性的子系统的集合提供对外访问的一组接口,调用者不用明确内部具体的业务逻辑,只需要调用这组接口达到目的即可。也就是要求子系统外部与内部不能直接进行通讯,必须通过一个统一的对象进行,而这个统一的对象就是门面。门面模式通过只提供高层次的接口,从而降低了外部与子系统调用的复杂性。
三 关系
门面角色: 了解子系统内部的全部业务逻辑与实现。
子系统角色: 被门面角色的实现对象所调用,等价于外界的客户端。
Client角色: 只需要传入相对应的签名,调用相关方法,就能得到目标结果。
子系统内部: 在实现Clinet调用方业务需求的同时,可以对具体业务进行功能拓展,对外不变,对内多变,增加额外的功能,比如说检验等。
四 门面模式的优点
A. 减少了系统的相互依赖性;在我看来通过门面的代理,直接切断了调用方与各个子系统内部的调用关系,降低了双方的耦合关系,从而降低了系统混乱、依赖。直接将两者的依赖关系转移到门面对象之间的依赖,减少了直接与子系统间的相互依赖关系,符合设计原则。
B. 功能拓展性更灵活,只要不影响门面对象,子系统内部可以多样性。
C. 通过门面对外只提供必要的服务与直接对外暴露子系统服务而言,系统的安全性更高。
五 门面模式的缺点
门面模式不满足开闭原则,门面的修改风险性很大。
六 适用场景
A. 为一组子系统或一系列复杂的模块提供一个统一的外部访问的接口。
B. 调用方与子系统依赖性很强,通过门面将client与子系统强依赖转化门面对象间的依赖。
七 代码实现
实现一个邮寄信件的功能,用户只需要输入信的内容与地址,即可实现写信、填写地址、封装、邮寄的功能。此外在拓展一个公安机关对文件内容检查功能。
代码清单
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
70
71// 提供一个邮寄信件的功能: 写信、填写地址、检查、装信封、投递
type SampleLetters struct {
letter string
}
func (l *SampleLetters) WriteLetters(content string) {
l.letter = fmt.Sprintf("写信内容为:%s", content)
}
func (l *SampleLetters) AddAddress(address string) {
l.letter = l.letter + "\n" + fmt.Sprintf("邮寄至: %s", address)
}
func (l *SampleLetters) PutInToEnvelope() {
fmt.Println("信件存放到信封中...")
}
func (l *SampleLetters) SendLetters() {
// fmt.Println("信件内容:", l.letter)
fmt.Println("信件已发送...")
}
type Facade interface {
sendLetter(context, address string)
}
func NewSampleFacade() *SampleFacade {
return &SampleFacade{}
}
// 如果此时需要新拓展功能,对信的内容进行检查
// 则继续添加SampleFacade的组合类
type PoliceInspection struct {
}
func (p *PoliceInspection) CheckLetterSecurity(content string) bool {
// 如果检查内通通过,则返回true
fmt.Printf("被检查内容: %s\n", content)
fmt.Println("内容检查通过...")
return true
}
// 将相关联性的子系统的集合进行综合,完成一系列的业务逻辑
type SampleFacade struct {
SampleLetters
PoliceInspection
}
func (f *SampleFacade) sendLetter(context, address string) {
// 此处进行业务逻辑的实现
// 1. 写信
f.WriteLetters(context)
// 2. 写地址
f.AddAddress(address)
// 此处新增检查信件安全性
ok := f.CheckLetterSecurity(f.letter)
if ok {
fmt.Println("this is safe!")
} else {
fmt.Println("this is danger!")
return
}
// 3. 封装
f.PutInToEnvelope()
// 4.发送
f.SendLetters()
}
// 由此可见,在不改变用户调用的接口的情况下,已经完成了逻辑的修改,增加的新的功能,这就是门面模式带来的好处代码测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package Facade
import "testing"
/*
@Time : 2021/1/12 12:57
@Author : austsxk
@Email : austsxk@163.com
@File : facade_test.go
@Software: GoLand
*/
func TestNewSampleFacade(t *testing.T) {
facade := NewSampleFacade()
facade.sendLetter(
"hello Song, this is facade pattern testing...",
"austsxk@vip.qq.com")
}
// go test -v facade_test.go facade.go结果输出
1
2
3
4
5
6
7
8
9
10
11=== RUN TestNewSampleFacade
被检查内容: 写信内容为:hello Song, this is facade pattern testing...
邮寄至: austsxk@vip.qq.com
内容检查通过...
this is safe!
信件存放到信封中...
信件已发送...
--- PASS: TestNewSampleFacade (0.00s)
PASS
ok command-line-arguments 0.897s说明
将用户写信的WriteLetters、AddAddress、PutInToEnvelope、SendLetters等四个操作,全部交给了SampleFacade 简单门面实现,该门面实现了Facade的接口,只提供一个写信的方法。SampleFacade结构体组合了写信的SampleLetters对象和安全检查的PoliceInspection对象,调用者将之前对子系统SampleLetters的四个操作直接转化成对门面对象的操作,并且只有一个方法可以调用,就是调用实例对象的sendLetter方法,就能达到调用者的目的。此外调用者并不知道子系统内部对信件内容进行安全检测,即对子系统功能的拓展非常方便。