亦称:门面模式, Facade [fəˈsɑːd]

门面模式为一组复杂的子系统对外提供统一的接口,定义一组高层接口使子系统更方便的被使用。

门面模式的思想更常用在架构设计上,在编写代码层面大家很少提门面模式,但却一直在默默的使用。

背景

假设必须在代码中使用某个复杂的库或框架中的众多对象。正常情况下,需要负责所有对象的初始化工作、管理其依赖关系并按正确的顺序执行方法等。

最终,程序中类的业务逻辑将与第三方类的实现细节紧密耦合,使得理解和维护代码的工作很难进行。

解决方案

外观类为包含许多活动部件的复杂子系统提供一个简单的接口。与直接调用子系统相比,外观提供的功能可能比较有限,但它却包含了客户端真正关心的功能。

如果程序需要与包含几十种功能的复杂库整合, 但只需使用其中非常少的功能, 那么使用外观模式会非常方便。

外观模式思想比较简单。系统中包含多个子系统,完成一项任务需要多个子系统通力合作。

可以选择将子系统所有接口暴露给Client,让Client自行调用。 但这会导致一些问题

  • 后期沟通成本会很高。假如完成一个功能需要调用多个接口,Client联调时出问题率会飙升,系统提供者需要不断答疑。
  • 如果有多个Client,相同代码Client需要重复开发,而且后期代码有变更,各方都会很烦费力。
  • 影响响应时间和性能,多个接口往返,白白增加了很多通信时间和请求量。

使用外观模式,对于指定功能,系统端做好封装,只提供一个接口。 沟通成本低、Client不需要重复开发、功能更改影响范围小、提高响应时间和性能。一般这些接口会有对应的OpenAPI,实现了功能对外开放的效果。

门面模式

适用场景

  • 如果需要一个指向复杂子系统的直接接口,且该接口的功能有限,则可以使用外观模式。
    • 子系统通常会随着时间的推进变得越来越复杂。即便是应用了设计模式,通常也会创建更多的类。尽管在多种情形中子系统可能是更灵活或易于复用的,但其所需的配置和样板代码数量将会增长得更快。
    • 为了解决这个问题,外观将会提供指向子系统中最常用功能的快捷方式,能够满足客户端的大部分需求。
  • 如果需要将子系统组织为多层结构, 可以使用外观
    • 创建外观来定义子系统中各层次的入口。可以要求子系统仅使用外观来进行交互,以减少子系统之间的耦合。

实现步骤

  1. 考虑能否在现有子系统的基础上提供一个更简单的接口。如果该接口能让客户端代码独立于众多子系统类, 那么你的方向就是正确的。
  2. 在一个新的外观类中声明并实现该接口。
    • 外观应将客户端代码的调用重定向到子系统中的相应对象处。
    • 如果客户端代码没有对子系统进行初始化,也没有对其后续生命周期进行管理,那么外观必须完成此类工作。
  3. 如果要充分发挥这一模式的优势,必须确保所有客户端代码仅通过外观来与子系统进行交互。
    • 此后客户端代码将不会受到任何由子系统代码修改而造成的影响。比如子系统升级后,只需修改外观中的代码即可。
  4. 如果外观变得过于臃肿,可以考虑将其部分行为抽取为一个新的专用外观类。

优缺点

优点

  • 可以让自己的代码独立于复杂子系统
  • 体现系统的分层设计

缺点

  • 外观可能成为与程序中所有类都耦合的上帝对象。

在面向对象编程领域,一个上帝对象(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