外观模式(结构型)

前言


在使用Visual Studio开发应用程序时候,在完成代码后,大家都习惯的点击Build按钮完成代码的编译链接过程,其实在IDE内部进行了一系列的操作:先Scanner扫描,后Parser解析,而后MidcodeGenerator产生中间代码,最后MachcodeGenerator产生最终的机器码。IDE的好处就是界面对客户来说非常简洁,内部的各个子系统对客户来说是隐藏的,客户也没必要知道,只需一个按钮即可。这个按钮与本文要介绍的外观模式很类似。

外观模式


外观模式,通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便

在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互即可
外观类(Facade):充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互
子系统(Subsystem):在外观模式中,那些需要交互的业务类称为子系统

意图

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

参与者

  • Facade
    知道哪些子系统类负责处理请求
    将客户的请求代理给适当的子系统对象

  • Subsystem
    实现子系统的功能
    处理由Facade对象指派的任务
    没有Facade的任何相关信息;即没有指向facade的指针

模式结构

facade

代码实现

  1. 首先定义两个子系统类,及操作接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class SubsystemA
    {
    public:
    void operateA(void)
    {
    cout << "Subsytem A operate" << endl;
    }
    };

    class SubsystemB
    {
    public:
    void operateB(void)
    {
    cout << "Subsytem B operate" << endl;
    }
    };

  2. 再定义Facade类,该类封装了子系统的内部的操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //外观类
    class Facade
    {
    public:
    void facadeOperate(void)
    {
    SubsystemA sa;
    SubsystemB sb;
    sa.operateA();
    sb.operateB();
    }
    };
  3. 测试外观模式:

    1
    2
    3
    4
    5
    void FacadeTest()
    {
    Facade facade;
    facade.facadeOperate();
    }

运行结果:

Subsytem A operate
Subsytem B operate

对外观模式的改进

在标准的外观模式结构图中,如果要修改与外观类交互的子系统类,必须修改外观类或客户端代码,违背了开闭原则,可以通过引入“抽象外观”来对系统进行改进,客户端可以针对抽象外观类进行编程,对于新的业务需求,不需要修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,如:

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
// 抽象外观类
class Facade
{
virtual void totalOperate(void) = 0;
}
// 具体外观类
class ConcreteFacadeA : Facade
{
virtual void totalOperate(void)
{
SubSystemA subSystemA;
SubSystemB subSystemB;
...

subSystemA.op();
subSystemB.op();
...
}
}
// 具体外观类
class ConcreteFacadeNewA : Facade
{
virtual void totalOperate(void)
{
SubSysteNewA subSystemNewA;
SubSystemB subSystemB;
...

subSystemNewA.op();
subSystemB.op();
...
}

}

//客户端
Facade *facade = new ConcreteFacadeA();
facade->totalOperate();

Facade *facadenew = new ConcreteFacadeNewA();
facadenew->totalOperate();

使用场景

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

通常来讲,仅需要一个Facade对象,因此Facade对象通常属于Singleton模式

优缺点

  • 优点
    • 它对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目,并使得子系统使用起来更加方便
    • 它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的
    • 外观模式可以消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要
  • 缺点
    • 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性

外观模式具体实例


编译过程

实现前言说描述的编译外观模型

代码实现

  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
    class Scanner
    {
    public:
    void Scan()
    {
    cout << "I am Scan SubSystem" << endl;
    }
    };

    class Parser
    {
    public:
    void Parse()
    {
    cout << "I am Parser SubSystem" << endl;
    }
    };

    class GenMidCode
    {
    public:
    void GenCode()
    {
    cout << "I am Generate Middle Code SubSytem" << endl;
    }
    };

    class GenMachineCode
    {
    public:
    void GenCode()
    {
    cout << "I am Generate Machine Code SubSystem" << endl;
    }
    };
  2. 高层接口,CompileFacade类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 高层接口
    class CompileFacade
    {
    public:
    void Compile()
    {
    Scanner scanner;
    Parser pareser;
    GenMidCode genmidcode;
    GenMachineCode genmachinecode;

    scanner.Scan();
    pareser.Parse();
    genmidcode.GenCode();
    genmachinecode.GenCode();
    }
    };
  3. 测试Compile Facade模式:

    1
    2
    3
    4
    5
    void CompileFacadeTest()
    {
    CompileFacade compilefacade;
    compilefacade.Compile();
    }
  4. 运行结果:

    I am Scan SubSystem
    I am Parser SubSystem
    I am Generate Middle Code SubSytem
    I am Generate Machine Code SubSystem