一 类别

​ 门面模式又叫外观模式,是一种常用的封装模式

二 概述

​ 给一系列具有关联性的子系统的集合提供对外访问的一组接口,调用者不用明确内部具体的业务逻辑,只需要调用这组接口达到目的即可。也就是要求子系统外部与内部不能直接进行通讯,必须通过一个统一的对象进行,而这个统一的对象就是门面。门面模式通过只提供高层次的接口,从而降低了外部与子系统调用的复杂性。

三 关系

​ 门面角色: 了解子系统内部的全部业务逻辑与实现。

​ 子系统角色: 被门面角色的实现对象所调用,等价于外界的客户端。

​ Client角色: 只需要传入相对应的签名,调用相关方法,就能得到目标结果。

​ 子系统内部: 在实现Clinet调用方业务需求的同时,可以对具体业务进行功能拓展,对外不变,对内多变,增加额外的功能,比如说检验等。

四 门面模式的优点

​ A. 减少了系统的相互依赖性;在我看来通过门面的代理,直接切断了调用方与各个子系统内部的调用关系,降低了双方的耦合关系,从而降低了系统混乱、依赖。直接将两者的依赖关系转移到门面对象之间的依赖,减少了直接与子系统间的相互依赖关系,符合设计原则。

​ B. 功能拓展性更灵活,只要不影响门面对象,子系统内部可以多样性。

​ C. 通过门面对外只提供必要的服务与直接对外暴露子系统服务而言,系统的安全性更高。

五 门面模式的缺点

​ 门面模式不满足开闭原则,门面的修改风险性很大。

六 适用场景

​ A. 为一组子系统或一系列复杂的模块提供一个统一的外部访问的接口。

​ B. 调用方与子系统依赖性很强,通过门面将client与子系统强依赖转化门面对象间的依赖。

七 代码实现

​ 实现一个邮寄信件的功能,用户只需要输入信的内容与地址,即可实现写信、填写地址、封装、邮寄的功能。此外在拓展一个公安机关对文件内容检查功能。

  1. 代码清单

    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()
    }

    // 由此可见,在不改变用户调用的接口的情况下,已经完成了逻辑的修改,增加的新的功能,这就是门面模式带来的好处

  2. 代码测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package 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
  3. 结果输出

    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

  4. 说明

    ​ 将用户写信的WriteLetters、AddAddress、PutInToEnvelope、SendLetters等四个操作,全部交给了SampleFacade 简单门面实现,该门面实现了Facade的接口,只提供一个写信的方法。SampleFacade结构体组合了写信的SampleLetters对象和安全检查的PoliceInspection对象,调用者将之前对子系统SampleLetters的四个操作直接转化成对门面对象的操作,并且只有一个方法可以调用,就是调用实例对象的sendLetter方法,就能达到调用者的目的。此外调用者并不知道子系统内部对信件内容进行安全检测,即对子系统功能的拓展非常方便。