当前位置: 首页 > article >正文

设计模式Python版 外观模式

文章目录

  • 前言
  • 一、外观模式
  • 二、外观模式示例
  • 三、抽象外观类
  • 四、抽象外观类示例


前言

GOF设计模式分三大类:

  • 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。
  • 结构型模式:关注类和对象之间的组合,包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式。
  • 行为型模式:关注对象之间的交互,包括职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。

一、外观模式

外观模式(Facade Pattern)

  • 定义:外部与一个子系统的通信通过一个统一的外观角色进行,为子系统中的一组接口提供一个一致的入口。外观模式定义了一个高层接口,这个接口使得子系统更加容易使用。外观模式又称为门面模式。

  • 解决问题:如何为复杂子系统提供一个统一的入口?

  • 使用场景:

    • 外观模式的主要目的在于降低系统的复杂程度,给客户端的使用带来极大方便。
    • 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
  • 组成:

    • Facade(外观角色):在客户端可以调用这个角色的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任。在正常情况下,它将所有从客户端发来的请求委派到相应的子系统中去,传递给相应的子系统对象处理。
    • SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色。每个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能。每个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求。子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
  • 补充说明:

    • 外观模式通过引入一个新的外观类来实现该功能。外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。
    • 在客户端代码和业务类之间增加一个外观类,由外观类来封装与业务类之间的交互,而客户端只需与外观类交互即可。
    • 引入一个外观(Facade)角色,它为子系统(在外观模式中所指的子系统是一个广义的概念,它可以是一个类、一个功能模块、系统的一个组成部分或者一个完整的系统)的访问提供了一个简单而单一的入口。
    • 外观模式也是迪米特法则的体现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。
    • 在外观角色中维持了对子系统对象的引用,客户端可以通过外观角色来间接调用子系统对象的业务方法,而无须与子系统对象直接交互。
    • 外观类可以是一个单例类。
    • 在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向客户端提供相应的业务功能。
    • 试图通过外观类为子系统增加新行为的做法是错误的。
    • 实现步骤:定义子系统,创建外观类,客户端调用。
    • 在GOF设计模式的7种结构型模式中,外观模式的使用频率是最高的。
  • 优点:

    • 通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,使子系统与客户端的耦合度降低,且客户端调用非常方便。
    • 实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
    • 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
  • 缺点:

    • 增加新的子系统或者修改原有子系统,可能需要修改外观类的源代码,这违背了开闭原则。

在这里插入图片描述

二、外观模式示例

使用外观模式设计文件加密模块

  • EncryptFacade充当外观类,FileReader、CipherMachine和FileWriter充当子系统类。
"""子系统类"""


class FileReader:
    def read(self, file_name_src):
        # 读取文件,获取明文。(略)
        return f"{file_name_src}文件的明文"


class CipherMachine:
    def encrypt(self, plain_text):
        # 数据加密,将明文转换为密文。(略)
        return f"{plain_text}对应的密文"


class FileWriter:
    def write(self, encrypt_str, file_name_des):
        # 保存密文,写入文件
        print(f"{encrypt_str},写入文件{file_name_des}")


"""外观类"""


class EncryptFacade:
    def __init__(self):
        self.reader = FileReader()
        self.cipher = CipherMachine()
        self.writer = FileWriter()

    def file_encrypt(self, file_name_src, file_name_des):
        plain_str = self.reader.read(file_name_src)
        encrypt_str = self.cipher.encrypt(plain_str)
        self.writer.write(encrypt_str, file_name_des)

  • 客户端代码
ef = EncryptFacade()
ef.file_encrypt("《三国演义》.pdf", "《A》.pdf")
  • 输出结果
《三国演义》.pdf文件的明文对应的密文,写入文件《A》.pdf

三、抽象外观类

在标准的外观模式结构图中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则。

  • 在引入抽象外观类之后,客户端可以针对抽象外观类进行编程,对于新的业务需求,不需要修改原有外观类,而对应增加一个新的具体外观类
  • 如何在不修改客户端代码的前提下使用新的外观类呢?解决方法是:引入一个抽象外观类,客户端针对抽象外观类编程,而在运行时再确定具体外观类。
  • 由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改任何源代码并更换外观类的目的。

四、抽象外观类示例

文件加密模块中需要更换一个加密类

  • 将加密类CipherMachine,更换为新加密类NewCipherMachine
  • 加入抽象外观类AbstractEncryptFacade,和新的具体外观类
# 模块facades.py
"""新加密类"""


class NewCipherMachine:
    def encrypt(self, plain_text):
        # 数据加密,将明文转换为密文。(略)
        return f"{plain_text}对应的密文(新加密类)"


"""抽象外观类"""


class AbstractEncryptFacade:
    def file_encrypt(self, file_name_src, file_name_des):
        raise NotImplementedError


"""具体外观类(新)"""


class NewEncryptFacade(AbstractEncryptFacade):
    def __init__(self):
        self.reader = FileReader()
        self.cipher = NewCipherMachine()
        self.writer = FileWriter()

    def file_encrypt(self, file_name_src, file_name_des):
        plain_str = self.reader.read(file_name_src)
        encrypt_str = self.cipher.encrypt(plain_str)
        self.writer.write(encrypt_str, file_name_des)

  • 配置文件config.json
{
    "class_name": "NewEncryptFacade"
}
  • 工具类utils.py
from pathlib import Path
import json


class JsonUtil:
    @staticmethod
    def get_class_name():
        """读取配置文件,返回配置文件中的配置"""
        path = Path("config.json")
        contents = path.read_text(encoding="utf-8")
        conf = json.loads(contents)
        return conf.get("class_name", None)
  • 客户端代码
import facades
from utils import JsonUtil

class_name = JsonUtil.get_class_name()
klass = getattr(facades, class_name, None)
if klass is None:
    raise ValueError
ef: facades.AbstractEncryptFacade = klass()
ef.file_encrypt("《三国演义》.pdf", "《A》.pdf")
  • 输出结果
《三国演义》.pdf文件的明文对应的密文(新加密类),写入文件《A》.pdf

您正在阅读的是《设计模式Python版》专栏!关注不迷路~


http://www.kler.cn/a/533509.html

相关文章:

  • 100.6 AI量化面试题:如何评估AI量化模型的过拟合风险?
  • TCP | RFC793
  • 一文速览DeepSeek-R1的本地部署——可联网、可实现本地知识库问答:包括671B满血版和各个蒸馏版的部署
  • 洛谷 P1387 最大正方形 C语言
  • 深入浅出:频谱掩码 Spectral Masking —— 噪音消除利器
  • LeetCode --- 434周赛
  • 国产化创新 守护开放边界网络安全
  • cocos spine执行动画报错Cannot read properties of null (reading ‘data‘)
  • Android原生开发入门
  • Unity 2D实战小游戏开发跳跳鸟 - 记录显示最高分
  • leetcode——子集(java)
  • Python实现CAN FD 通信(基于PCAN开发CAN FD测试工具)
  • vue3新建组件库项目并上传到私库
  • 实时波形与频谱分析———傅立叶变换
  • RabbitMQ深度探索:消息幂等性问题
  • MongoDB 查询文档
  • 哈夫曼树原理及其C语言实现
  • 时间对象管理相关
  • gesp(C++六级)(13)洛谷:P11375:[GESP202412 六级] 树上游走
  • 因果推断与机器学习—可解释性、公平性和因果机器学习
  • go运算符
  • Redis缓存穿透、击穿、雪崩介绍以及解决方案
  • vscode 设置在编辑器的标签页超出可视范围时自动换行(workbench.editor.wrapTabs)
  • SpringBoot 基于个性化定制的智慧校园管理系统设计与开发 - 论文、开题报告
  • 搭建Python环境:为量化交易做准备
  • Linux之安装MySQL