万隆的笔记 万隆的笔记
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
  • 数据结构与算法

  • 设计模式

    • 23种设计模式

      • 设计模式概述
      • 监听模式
      • 状态模式
      • 中介模式
      • 装饰模式
      • 单例模式
      • 原型模式
      • 职责模式
      • 代理模式
      • 外观/门面模式
        • 什么是外观模式
        • 外观模式的设计思想
        • 从生活中领悟外观模式
        • 外观模式的框架模型
        • 实战应用
        • 应用场景
      • 迭代模式
      • 组合模式
      • 构建模式
      • 适配器模式
      • 策略模式
      • 工厂模式
      • 命令模式
      • 备忘模式
      • 享元模式
      • 访问模式
      • 模板模式
      • 桥接模式
      • 解释模式
    • 设计模式拓展

    • 设计原则

  • 编程方法论

  • 分布式设计与微服务理论

  • Leetcode

  • 程序员内功
  • 设计模式
  • 23种设计模式
2021-07-01
目录

外观/门面模式

# 外观/门面模式

# 什么是外观模式

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

为子系统中的一组接口提供一个一致的界面称为外观模式,外观模式定义了一个高层接口,这个接口使得子系统更容易使用。外观模式又被称为门面模式,通常体现层次结构,是比较简单的设计模式,但却是非常常用的设计模式。

# 外观模式的设计思想

外观模式的核心思想是:用一个简单的接口封装一个复杂的系统,使这个系统更容易使用。客户端只需要跟接口交互,而不需要关系内部复杂的运行机制。

外观模式的应用可以体现软件的分层结构思想。

# 从生活中领悟外观模式

在生活中,迎宾接待的礼仪、新手指南说明书、迎新志愿者等都是外观模式的例子。

这里用迎新志愿者做示例,在新学期开始的时候,校方通常都会组织迎新志愿者,让他们陪同并帮助入学新生完成报到登记、缴纳学费、领日用品、入住宿舍等一系列的报到流程。新生不用知道具体的报到流程,不用去寻找各个场地;只要跟着志愿者走,到指定的地点,根据志愿者的指导,完成指定的任务即可。志愿者虽然不直接提供这些报到服务,但也相当于间接提供了报到登记、缴纳学费、领日用品、入住宿舍等一条龙的服务,帮新生减轻了不少麻烦和负担。

# 外观/门面模式 - 新生报到
class Register:
    """报到登记"""

    def register(self, name):
        print("活动中心:%s 同学报到成功!" % name)


class Payment:
    """缴费中心"""

    def pay(self, name, money):
        print("缴费中心:收到 %s 同学 %s 元付款,缴费成功!" % (name, money))


class DormitoryManagementCenter:
    """宿舍管理中心"""

    def provideLivingGoods(self, name):
        print("生活中心:%s 同学的生活用品已发放。" % name)


class Dormitory:
    """宿舍"""

    def meetRoommate(self, name):
        print("宿舍 :大家好!这是刚来的%s 同学,是你们未来需要共度四年的室友!相互认识一下...balala" % name)


class Volunteer:
    """迎新志愿者"""

    def __init__(self, name):
        self.__name = name
        self.__register = Register()
        self.payment = Payment()
        self.lifeCenter = DormitoryManagementCenter()
        self.__dormitory = Dormitory()

    def welcomeFreshmen(self, name):
        print("你好,%s 同学! 我是新生报到的志愿者%s ,我将带你完成整个报到流程。" % (name, self.__name))
        self.__register.register(name)
        self.payment.pay(name, 10000)
        self.lifeCenter.provideLivingGoods(name)
        self.__dormitory.meetRoommate(name)


def testRegister():
    volunteer = Volunteer("Frank")
    volunteer.welcomeFreshmen("Tony")


if __name__ == '__main__':
    testRegister()

"""
你好,Tony 同学! 我是新生报到的志愿者Frank ,我将带你完成整个报到流程。
活动中心:Tony 同学报到成功!
缴费中心:收到 Tony 同学 10000 元付款,缴费成功!
生活中心:Tony 同学的生活用品已发放。
宿舍 :大家好!这是刚来的Tony 同学,是你们未来需要共度四年的室友!相互认识一下...balala
"""

在这里,我们可以进一步把学校想象成一个庞大且复杂的系统,而登记中心、缴费中心、生活中心都是系统的一小部分,这些部分的登记、缴费、发放生活用品的功能可以组成完成新生注册报到的流程,志愿者是学校系统的门脸,将新生报到的流程需要用到的功能进行组合,提供了“新生报到”的功能,新来的学生可以在志愿者的帮助下快速完成入学报到。

整个示例,就有了以下的分层结构:

学校系统(System)---迎新流程(SubSystem)---迎新志愿者(Facade) --- 新生(Client)

# 外观模式的框架模型

对示例代码进一步思考,抽象代理模式框架

# 类图

外观模式

Facade封装了子系统的复杂实现,给外部提供一个统一的接口,用户只需要通过Facade来访问子系统,而不用关心内部ClassA、ClassB、ClassC、ClassD的具体实现。

# 设计要点

外观模式需要区分以下两个角色:

  • 外观角色(Façade):为子系统封装统一的对外接口,如同子系统的门面。这个类一般不负责具体的业务逻辑,只是一个委托类,具体的业务逻辑由子系统完成。
  • 子系统(SubSystem):由多个类组成的具有某一特定功能的子系统。可以是第三方库,也可以是自己的基础库,还可以是一个子服务,为整个系统提供特定的功能或服务。

# 外观模式优缺点

优点:

  1. 契合软件的层次化结构设计。可以用外观模式来定义每一层系统的调用接口,层与层之间不直接产生联系,而是通过外观类建立联系,降低底层之间的耦合度。
  2. 实现子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响调用它的客户端。
  3. 门脸能提供简单的可用的调用接口,简化了客户端对子系统的使用难度,客户端(用户)无须关心子系统复杂的具体实现方式,而只需要和外观进行交互即可。
  4. 为不同的用户提供了统一的调用接口,方便了系统的管理和维护。

缺点:

  1. 因为统一了调用的接口,降低了系统功能的灵活性。

# 实战应用

在互联网世界中,文件的压缩与解压缩是一项非常重要的功能,它不仅能减小文件的存储空间,还能减少网络带宽,现在最常用的压缩文件格式有ZIP、RAR、7Z。从压缩率看:ZIP<RAR<7Z(即7Z的压缩比最高),从压缩时间看:ZIP<RAR<7Z(即ZIP的压缩速度最快)。从普及率上看,ZIP应该是应用最广泛的,因为出现的时间最早,格式开放且免费;而7Z因为其极高的压缩比和开放性,大有赶超之势。

假设我们有一个压缩与解压缩系统专门处理文件的压缩和解压缩,这个系统有三个模块:ZIPModel、RARModel、ZModel,分别处理ZIP、RAR、7Z三种文件格式的压缩与解压缩。现在这一系统要提供给上层应用程序使用。

为了让这一系统更方便使用,就可以用外观模式进行封装,定义一套统一的调用接口,如下:

# 外观模式 应用实战-压缩、解压缩系统
# 引入path,进行路径相关的处理
from os import path
# 引入logging,进行错误日志的记录
import logging


class ZIPModel:
    """ZIP模块,负责ZIP文件的压缩与解压缩,简单模拟"""

    def compress(self, srcFilePath, dstFilePath):
        print("ZIP模块正在进行 %s 文件的压缩......" % srcFilePath)
        print("文件压缩成功,已保存至 %s " % dstFilePath)

    def decompress(self, srcFilePath, dstFilePath):
        print("ZIP模块正在进行 %s 文件的解压缩....." % srcFilePath)
        print("文件解压缩成功,已保存至 %s " % dstFilePath)


class RARModel:
    """RAR模块,负责RAR文件的压缩与解压缩,简单模拟"""

    def compress(self, srcFilePath, dstFilePath):
        print("RAR模块正在进行 %s 文件的压缩......" % srcFilePath)
        print("文件压缩成功,已保存至 %s " % dstFilePath)

    def decompress(self, srcFilePath, dstFilePath):
        print("RAR模块正在进行 %s 文件的解压缩....." % srcFilePath)
        print("文件解压缩成功,已保存至 %s " % dstFilePath)


class ZModel:
    """7Z模块,负责7Z文件的压缩与解压缩,简单模拟"""

    def compress(self, srcFilePath, dstFilePath):
        print("7Z模块正在进行 %s 文件的压缩......" % srcFilePath)
        print("文件压缩成功,已保存至 %s " % dstFilePath)

    def decompress(self, srcFilePath, dstFilePath):
        print("7Z模块正在进行 %s 文件的解压缩....." % srcFilePath)
        print("文件解压缩成功,已保存至 %s " % dstFilePath)


class CompressionFacade:
    """压缩系统的外观类"""

    def __init__(self):
        self.__zipModel = ZIPModel()
        self.__rarModel = RARModel()
        self.__zModel = ZModel()

    def compress(self, srcFilePath, dstFilePath, type):
        """根据不同的压缩类型,压缩成不同的格式"""
        # 获取新的文件名
        extName = "." + type
        fullName = dstFilePath + extName
        if(type.lower() == "zip"):
            self.__zipModel.compress(srcFilePath, fullName)
        elif(type.lower() == "rar"):
            self.__rarModel.compress(srcFilePath, fullName)
        elif(type.lower() == "7z"):
            self.__zModel.compress(srcFilePath, fullName)
        else:
            logging.error("Not support this format:" + str(type))
            return False
        return True

    def decompress(self, srcFilePath, dstFilePath):
        """从srcFilePath中获取后缀,根据不同后缀名,进行不同格式的解压缩"""
        baseName = path.basename(srcFilePath)
        extName = baseName.split(".")[1]
        if (extName.lower() == "zip"):
            self.__zipModel.decompress(srcFilePath, dstFilePath)
        elif (extName.lower() == "rar"):
            self.__rarModel.decompress(srcFilePath, dstFilePath)
        elif (extName.lower() == "7z"):
            self.__zModel.decompress(srcFilePath, dstFilePath)
        else:
            logging.error("Not support this format:" + str(extName))
            return False
        return True


def testCompression():
    facade = CompressionFacade()
    facade.compress("C:/app/facade/zip.md", "C:/app/facade/zip压缩文件", "zip")
    facade.decompress("C:/app/facade/zip压缩文件.zip", "C:/app/facade/file")
    print()
    facade = CompressionFacade()
    facade.compress("C:/app/facade/rar.md", "C:/app/facade/rar压缩文件", "rar")
    facade.decompress("C:/app/facade/rar压缩文件.rar", "C:/app/facade/file")
    print()
    facade = CompressionFacade()
    facade.compress("C:/app/facade/7z.md", "C:/app/facade/7z压缩文件", "7z")
    facade.decompress("C:/app/facade/7z压缩文件.7z", "C:/app/facade/file")


if __name__ == '__main__':
    testCompression()

'''
ZIP模块正在进行 C:/app/facade/zip.md 文件的压缩......
文件压缩成功,已保存至 C:/app/facade/zip压缩文件.zip 
ZIP模块正在进行 C:/app/facade/zip压缩文件.zip 文件的解压缩.....
文件解压缩成功,已保存至 C:/app/facade/file 

RAR模块正在进行 C:/app/facade/rar.md 文件的压缩......
文件压缩成功,已保存至 C:/app/facade/rar压缩文件.rar 
RAR模块正在进行 C:/app/facade/rar压缩文件.rar 文件的解压缩.....
文件解压缩成功,已保存至 C:/app/facade/file 

7Z模块正在进行 C:/app/facade/7z.md 文件的压缩......
文件压缩成功,已保存至 C:/app/facade/7z压缩文件.7z 
7Z模块正在进行 C:/app/facade/7z压缩文件.7z 文件的解压缩.....
文件解压缩成功,已保存至 C:/app/facade/file 
'''

ps: 在实际的项目开发中,不应该通过文件后缀名来区分文件格式,因为用户可能将一个RAR格式的文件改成.zip后缀,这会造成解压缩的错误;应该通过文件的魔数来判断,每一种格式的文件,在二进制文件的开头都会有一个魔数(MagicNumber)来说明该文件的类型(可通过二进制文件工具查看,如WinHex),如ZIP的魔数是PK(50 4B 03 04),RAR的魔数是Rar(52 61 72),7z的魔数是7z(37 7A)。

# 应用场景

  1. 要为一个复杂子系统提供一个简单接口时。
  2. 客户程序与多个子系统之间存在很大的依赖性时。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植植性。
  3. 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

摘自: 罗伟富. 《人人都懂设计模式:从生活中领悟设计模式:Python实现》. 电子工业出版社

#23种设计模式
上次更新: 5/21/2023, 11:34:33 PM
迭代模式

迭代模式→

最近更新
01
2025
01-15
02
Elasticsearch面试题
07-17
03
Elasticsearch进阶
07-16
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式