• C++设计模式之外观模式(结构型模式)


    学习软件设计,向OO高手迈进!
    设计模式(Design pattern)是软件开发人员在软件开发过程中面临的一般问题的解决方案。
    这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
    是前辈大神们留下的软件设计的"招式"或是"套路"。

    什么是外观模式

    一个系统都是由各种大大小小不同功能的类组成的,每一个类都会提供被外部调用的接口,我们可以通过这些接口去使用这些类,但是客户端要一个个的去调用这些类非常的麻烦,而且也会造成客户类与子系统类耦合性高的问题,就如下图:

    在这里插入图片描述

    所以为了解决这种问题,我们需要定义一个高层接口,让这个高层接口去完成各个子系统类接口的调用,客户类就只需要对这个高层接口进行调用即可,这样降低了客户类与子系统类的耦合,也能让客户端更加方便的使用这个系统,这就是外观模式,也叫门面模式。如下图:

    在这里插入图片描述

    定义

    外观模式 (Facade Pattern) 隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。也就是说为子系统中的一组接口提供一个一致的入口,它定义了一个高层接口,这个接口使得相关子系统更加容易使用;这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

    UML类图

    在这里插入图片描述

    外观模式中的角色:

    1. 外观 (Facade) 角色:为多个子系统对外提供一个共同的接口。此角色知晓子系统的所有功能和责任,将客户端的请求代理给适当的子系统对象。
    2. 子系统 (Sub System) 角色:实现系统的部分功能,客户可以通过外观角色访问它。可以同时拥有一个或多个子系统,每一个子系统都不是一个单独的类,而是一个类的集合,子系统并不知道门面的存在。
    3. 客户(Client) 角色:通过一个外观角色访问各个子系统的功能。

    Version 1.0

    下面我们以IDE编译运行来讲解该模式:

    平常我们使用IDE进行编译运行代码的时候,不需要我们去编写Makefile等文件。直接点击编译按钮或者F5就能进行编译并生产可执行文件。这就是一个比较典型的外观的例子。
    对于客户端(用户)来讲,只要给文件路径,就能产生最终的结果。编译器内部所做的事情:扫描源文件、语法分析、代码优化、生成生产可执行程序等,对客户端来说是透明的。

    一、定义各个子系统

    //扫描源文件路径
    class Scanner {
    public:
        void scan() {
            cout << "scan source file" << endl;
        }
    };
    
    //语法语义分析
    class Parser
    {
    public:
        void parse() {
            cout << "parse syntax" << endl;
        }
    };
    
    //代码优化
    class Optimizer {
    public:
        void optimizer() {
            cout << "optimize code" << endl;
        }
    };
    
    //生成中间代码
    class GenMiddleCode {
    public:
        void genMiddleCode() {
            cout << "gen middle code" << endl;
        }
    };
    
    //生产可执行程序
    class GenMachineCode {
    public:
        void genMachineCode() {
            cout << "gen executable code" << endl;
        }
    };
    
    • 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

    二、定义外观类

    //提供一个外观类(编译器)
    class Compiler
    {
    public:
        void Compile() {
            m_Scanner.scan();
            m_Parser.parse();
            m_Optimizer.optimizer();
            m_GenMiddleCode.genMiddleCode();
            m_GenMachineCode.genMachineCode();
        }
    private:
        Scanner m_Scanner;
        Parser m_Parser;
        Optimizer m_Optimizer;
        GenMiddleCode m_GenMiddleCode;
        GenMachineCode m_GenMachineCode;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三、客户端

    int main(int argc, char** argv) {
        Compiler *compile = new Compiler();
        compile->Compile();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行结果

    scan source file
    parse syntax
    optimize code
    gen middle code
    gen executable code
    
    • 1
    • 2
    • 3
    • 4
    • 5

    优点

    1. 松散耦合:外观模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。即提高子系统的独立性。
    2. 简单易用:外观模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观交互就可以了,相当于外观类为外部客户端使用子系统提供了一站式服务。
    3. 更好的划分访问层次:通过合理使用Facade,可以帮助我们更好的划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好的隐藏了内部的细节。

    缺点

    1. 过多的或者是不太合理的Facade也容易让人迷惑,到底是调用Facade好呢,还是直接调用模块好?
    2. 当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果要改东西很麻烦,继承重写都不太合适。

    适用场合

    1. 外观模式并非一个集装箱,可以任意地放进任何多个对象。Facade模式中组件的内部应该是“相互耦合关系比较大的一系列组件”,而不是一个简单的功能集合。
    2. 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。
    3. 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
    4. 当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过Facade进行通讯,从而简化了它们之间的依赖关系。
  • 相关阅读:
    自注意力机制(self-attention)
    【C++】动态内存管理
    Vue技术 props配置
    太赞了!别再说 不能用Python开发美观的GUI程序了!
    【QT基础入门】QT中的容器类:QList
    RocketMQ 消息失败重试 解析——图解、源码级解析
    threejs实现3d全景看房
    Gmail邮箱注册情况及最新动态
    数组模拟堆
    lambda的使用案例(1)
  • 原文地址:https://blog.csdn.net/cfl927096306/article/details/126677017