亦称:门面模式, Facade [fəˈsɑːd]
门面模式为一组复杂的子系统对外提供统一的接口,定义一组高层接口使子系统更方便的被使用。
门面模式的思想更常用在架构设计上,在编写代码层面大家很少提门面模式,但却一直在默默的使用。
背景
假设必须在代码中使用某个复杂的库或框架中的众多对象。正常情况下,需要负责所有对象的初始化工作、管理其依赖关系并按正确的顺序执行方法等。
最终,程序中类的业务逻辑将与第三方类的实现细节紧密耦合,使得理解和维护代码的工作很难进行。
解决方案
外观类为包含许多活动部件的复杂子系统提供一个简单的接口。与直接调用子系统相比,外观提供的功能可能比较有限,但它却包含了客户端真正关心的功能。
如果程序需要与包含几十种功能的复杂库整合, 但只需使用其中非常少的功能, 那么使用外观模式会非常方便。
外观模式思想比较简单。系统中包含多个子系统,完成一项任务需要多个子系统通力合作。
可以选择将子系统所有接口暴露给Client,让Client自行调用。 但这会导致一些问题
- 后期沟通成本会很高。假如完成一个功能需要调用多个接口,Client联调时出问题率会飙升,系统提供者需要不断答疑。
- 如果有多个Client,相同代码Client需要重复开发,而且后期代码有变更,各方都会很烦费力。
- 影响响应时间和性能,多个接口往返,白白增加了很多通信时间和请求量。
使用外观模式,对于指定功能,系统端做好封装,只提供一个接口。
沟通成本低、Client不需要重复开发、功能更改影响范围小、提高响应时间和性能。一般这些接口会有对应的OpenAPI,实现了功能对外开放的效果。
适用场景
- 如果需要一个指向复杂子系统的直接接口,且该接口的功能有限,则可以使用外观模式。
- 子系统通常会随着时间的推进变得越来越复杂。即便是应用了设计模式,通常也会创建更多的类。尽管在多种情形中子系统可能是更灵活或易于复用的,但其所需的配置和样板代码数量将会增长得更快。
- 为了解决这个问题,外观将会提供指向子系统中最常用功能的快捷方式,能够满足客户端的大部分需求。
- 如果需要将子系统组织为多层结构, 可以使用外观
- 创建外观来定义子系统中各层次的入口。可以要求子系统仅使用外观来进行交互,以减少子系统之间的耦合。
实现步骤
- 考虑能否在现有子系统的基础上提供一个更简单的接口。如果该接口能让客户端代码独立于众多子系统类, 那么你的方向就是正确的。
- 在一个新的外观类中声明并实现该接口。
- 外观应将客户端代码的调用重定向到子系统中的相应对象处。
- 如果客户端代码没有对子系统进行初始化,也没有对其后续生命周期进行管理,那么外观必须完成此类工作。
- 如果要充分发挥这一模式的优势,必须确保所有客户端代码仅通过外观来与子系统进行交互。
- 此后客户端代码将不会受到任何由子系统代码修改而造成的影响。比如子系统升级后,只需修改外观中的代码即可。
- 如果外观变得过于臃肿,可以考虑将其部分行为抽取为一个新的专用外观类。
优缺点
优点
- 可以让自己的代码独立于复杂子系统
- 体现系统的分层设计
缺点
在面向对象编程领域,一个上帝对象(God object)是一个了解过多或者负责过多的对象。
与其它模式的关系
外观模式
为现有对象定义了一个新接口,适配器模式
则会试图运用已有的接口。
- 适配器通常只封装一个对象,外观通常会作用于整个对象子系统上。
- 当只需对客户端代码隐藏子系统创建对象的方式时,你可以使用
抽象工厂模式
来代替外观。
享元模式
展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。
- 外观和
中介者
模式的职责类似:它们都尝试在大量紧密耦合的类中组织起合作。
- 外观为子系统中的所有对象定义了一个简单接口,但是它不提供任何新功能。子系统本身不会意识到外观的存在。子系统中的对象可以直接进行交流。
- 中介者将系统中组件的沟通行为中心化。各组件只知道中介者对象,无法直接相互交流。
- 外观类通常可以转换为
单例模式类
, 因为在大部分情况下一个外观对象就足够了。
- 外观与
代理模式
的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。
- 代理与其服务对象遵循同一接口,使得自己和服务对象可以互换,在这一点上它与外观不同。
示例
钱包子系统
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
|
// Wallet 钱包子系统
type Wallet struct {
balance int
}
func newWallet() *Wallet {
return &Wallet{
balance: 0,
}
}
func (w *Wallet) creditBalance(amount int) {
w.balance += amount
fmt.Println("Wallet balance added successfully")
return
}
func (w *Wallet) debitBalance(amount int) error {
if w.balance < amount {
return fmt.Errorf("Balance is not sufficient")
}
fmt.Println("Wallet balance is Sufficient")
w.balance = w.balance - amount
return nil
}
|
验证码子系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import "fmt"
// SecurityCode 验证码子系统
type SecurityCode struct {
code int
}
func newSecurityCode(code int) *SecurityCode {
return &SecurityCode{
code: code,
}
}
func (s *SecurityCode) checkCode(incomingCode int) error {
if s.code != incomingCode {
return fmt.Errorf("Security Code is incorrect")
}
fmt.Println("SecurityCode Verified")
return nil
}
|
帐户子系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import "fmt"
// Account 账户子系统
type Account struct {
name string
}
func newAccount(accountName string) *Account {
return &Account{
name: accountName,
}
}
func (a *Account) checkAccount(accountName string) error {
if a.name != accountName {
return fmt.Errorf("Account Name is incorrect")
}
fmt.Println("Account Verified")
return nil
}
|
簿记子系统
1
2
3
4
5
6
7
8
9
10
11
|
import "fmt"
// Ledger 簿记系统
type Ledger struct {
}
func (s *Ledger) makeEntry(accountID, txnType string, amount int) {
fmt.Printf("Make ledger entry for accountId %s with txnType %s for amount %d\n", accountID, txnType, amount)
return
}
|
通知子系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import "fmt"
// Notification 通知系统
type Notification struct {
}
func (n *Notification) sendWalletCreditNotification() {
fmt.Println("Sending wallet credit notification")
}
func (n *Notification) sendWalletDebitNotification() {
fmt.Println("Sending wallet debit notification")
}
|
钱包门面
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
|
import "fmt"
// WalletFacade 钱包外观类
type WalletFacade struct {
account *Account // 账户子系统
wallet *Wallet // 钱包子系统
securityCode *SecurityCode // 验证码子系统
notification *Notification // 通知子系统
ledger *Ledger // 簿记子系统
}
func NewWalletFacade(accountID string, code int) *WalletFacade {
fmt.Println("Starting create account")
walletFacacde := &WalletFacade{
account: newAccount(accountID),
securityCode: newSecurityCode(code),
wallet: newWallet(),
notification: &Notification{},
ledger: &Ledger{},
}
fmt.Println("Account created")
return walletFacacde
}
// AddMoneyToWallet 外观模式对外提供的存款功能,内部调用其它子系统
func (w *WalletFacade) AddMoneyToWallet(accountID string, securityCode int, amount int) error {
fmt.Println("Starting add money to wallet")
_ = w.account.checkAccount(accountID)
_ = w.securityCode.checkCode(securityCode)
w.wallet.creditBalance(amount)
w.notification.sendWalletCreditNotification()
w.ledger.makeEntry(accountID, "credit", amount)
return nil
}
// deductMoneyFromWallet 外观模式对外提供的取款功能,内部调用其它子系统
func (w *WalletFacade) DeductMoneyFromWallet(accountID string, securityCode int, amount int) error {
fmt.Println("Starting debit money from wallet")
_ = w.account.checkAccount(accountID)
_ = w.securityCode.checkCode(securityCode)
_ = w.wallet.debitBalance(amount)
w.notification.sendWalletDebitNotification()
w.ledger.makeEntry(accountID, "credit", amount)
return nil
}
|
客户端
1
2
3
4
5
6
7
8
9
|
package main
func main() {
walletFacade := NewWalletFacade("abc", 1234)
_ = walletFacade.AddMoneyToWallet("abc", 1234, 10)
_ = walletFacade.DeductMoneyFromWallet("abc", 1234, 5)
}
|
References
guru