Facade模式

    又称外观模式。这个模式是现实中很常见到的一种模式,比如汽车的设计,汽车里面是很复杂的,但是开汽车是很简单的,为什么?因为汽车设计人员把汽车的复杂部分“封装”在汽车里面,只提供很简单易用的方向盘、刹车和加油给你控制汽车。这就是Facade模式,我们需要一个更简单的方式使用一个复杂的东西,并可满足我们的需要。
  通常我们的一个应用系统会划分成几个子系统,实际的一个应用中可能对应不同的子系统。外部接口与内部子系统之间的交互使用Facade来实现,让两者没有耦合关系。如果客户程序与组件中各种子系统有很过多的耦合,随着外部客户程序和各子系统的演化,这种耦合就会面临很多变化的挑战。一个汽车引擎的更改就要修改所有的引用它的部分。简化外部程序和系统之间的交互接口,使用Facade将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦。

  
名称 Facade
结构
意图 为子系统中的一组接口提供一个一致的界面,F a c a d e 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性
  • 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。F a c a d e 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过f a c a d e 层。
  • 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入f a c a d e 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
  • 当你需要构建一个层次结构的子系统时,使用f a c a d e 模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过f a c a d e 进行通讯,从而简化了它们之间的依赖关系。
Code Example
 1// Facade
 2
 3// Intent: "Provide a unified interface to a set of interfaces in a subsystem.
 4// Facade defines a higher-level interface that makes the subsystem easier
 5// to use". 
 6
 7// For further information, read "Design Patterns", p185, Gamma et al.,
 8// Addison-Wesley, ISBN:0-201-63361-2
 9
10/* Notes:
11 * Many subsystems are complex to manage - and this is a dis-incentive for 
12 * client code to use them. A Facasde design pattern provides a simlified 
13 * high-level API to the client, thus shielding it from direct contact 
14 * with the (more complex) subsystem classes.  
15 * 
16 * Often the code that is inside a facade would have to be inside client 
17 * code without the facade. The subsystem code beneath the facade can 
18 * change, without affecting the client code. 
19 */

20 
21namespace Facade_DesignPattern
22{
23    using System;
24
25    class SubSystem_class1 
26    {
27        public void OperationX() 
28        {
29            Console.WriteLine("SubSystem_class1.OperationX called");
30        }

31    }

32
33    class SubSystem_class2
34    {
35        public void OperationY()
36        {
37            Console.WriteLine("SubSystem_class2.OperationY called");
38        }

39    }

40
41    class SubSystem_class3 
42    {
43        public void OperationZ()
44        {            
45            Console.WriteLine("SubSystem_class3.OperationZ called");
46        }
    
47    }

48
49    class Facade 
50    {
51        private SubSystem_class1 c1 = new SubSystem_class1();
52        private SubSystem_class2 c2 = new SubSystem_class2();
53        private SubSystem_class3 c3 = new SubSystem_class3();
54
55        public void OperationWrapper()
56        {
57            Console.WriteLine("The Facade OperationWrapper carries out complex decision-making");
58            Console.WriteLine("which in turn results in calls to the subsystem classes");
59            c1.OperationX();
60            if (1==1 /*some really complex decision*/)
61            {
62                c2.OperationY();
63            }

64            // lots of complex code here . . .
65            c3.OperationZ();
66        }

67        
68    }

69
70    /// <summary>
71    ///    Summary description for Client.
72    /// </summary>

73    public class Client
74    {
75          public static int Main(string[] args)
76        {
77            Facade facade = new Facade();
78            Console.WriteLine("Client calls the Facade OperationWrapper");
79            facade.OperationWrapper();      
80            return 0;
81        }

82    }

83}

84
85



 
设计

那么如何能简单的使用一个复杂的东西呢?只需要这个复杂的东西提供我所需的功能就行了。比如,汽车向前行驶对汽车本身是一个很复杂的系统,要抽汽油燃烧,通过引擎形成动力,再通过承轴传到轮胎上,使轮胎滚动,从而使汽车前进。如果我们开车需要去控制汽车内部的这些部件的运转的话,估计没人能学会开车。那么怎么办?很简单,汽车提供一个功能——加油,我们只要脚踏油门,汽车就会前进。这样,大家都能很容易学会开车了。

 


3.3     
实现

UML图:

示例代码为:

 1using System;
 2
 3namespace Example
 4{
 5    /// <summary>
 6    /// 示例
 7    /// </summary>

 8    class Example
 9    {
10        /// <summary>
11        /// 应用程序的主入口点。
12        /// </summary>

13        [STAThread]
14        static void Main(string[] args)
15        {
16            UseCar useCar = new UseCar() ;
17            useCar.Drive() ;//开车
18        }

19        /// <summary>
20        /// 复杂的汽车
21        /// </summary>

22        public class Car
23        {
24            public Car() {}
25
26            public void Oil()
27            {
28                Console.WriteLine( "加油到引擎中……" ) ;
29            }

30            public void Motor()
31            {
32                Console.WriteLine( "发动引擎……" ) ;
33            }

34            public void Transmission()
35            {
36                Console.WriteLine( "动力传动中……" ) ;
37            }

38            public void Tire()
39            {
40                Console.WriteLine( "轮胎转动中……" ) ;
41            }

42            //其他方法……
43        }

44        /// <summary>
45        /// 使用汽车
46        /// </summary>
47        /// <remarks>
48        /// 使用Facade模式,用一个简单的功能(Drive)封装了汽车内部的复杂动作(Oil、Motor……)
49        /// 使用者只使用这个简单功能(Drive)就可满足控制汽车前进的需求
50        /// </remarks>

51        public class UseCar
52        {
53            public UseCar() {}
54
55            public void Drive()
56            {
57                Console.WriteLine( "准备开车!" ) ;
58                Car car = new Car() ;
59                car.Oil() ;
60                car.Motor() ;
61                car.Transmission() ;
62                car.Tire() ;
63                Console.WriteLine( "汽车行驶中!" ) ;
64            }

65        }

66    }

67}

68
====================================================================================

一、 门面(Facade)模式

外部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行,这就是门面模式。

医院的例子

用一个例子进行说明,如果把医院作为一个子系统,按照部门职能,这个系统可以划分为挂号、门诊、划价、化验、收费、取药等。看病的病人要与这些部门打交道,就如同一个子系统的客户端与一个子系统的各个类打交道一样,不是一件容易的事情。

首先病人必须先挂号,然后门诊。如果医生要求化验,病人必须首先划价,然后缴款,才能到化验部门做化验。化验后,再回到门诊室。

解决这种不便的方法便是引进门面模式。可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。这个接待员就是门面模式的体现,病人只接触接待员,由接待员负责与医院的各个部门打交道。

什么是门面模式

门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

就如同医院的接待员一样,门面模式的门面类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与门面对象打交道,而不需要与子系统内部的很多对象打交道。


二、 门面模式的结构

门面模式是对象的结构模式。门面模式没有一个一般化的类图描述,下图演示了一个门面模式的示意性对象图:

 

在这个对象图中,出现了两个角色:

门面(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。

子系统(subsystem)角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。


三、 门面模式的实现

一个系统可以有几个门面类

【GOF】的书中指出:在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只能有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统有一个门面类,整个系统可以有数个门面类。

为子系统增加新行为

初学者往往以为通过继承一个门面类便可在子系统中加入新的行为,这是错误的。门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。


四、 在什么情况下使用门面模式

  • 为一个复杂子系统提供一个简单接口
  • 提高子系统的独立性
  • 在层次化结构中,可以使用Facade模式定义系统中每一层的入口。


五、 一个例子

我们考察一个保安系统的例子,以说明门面模式的功效。一个保安系统由两个录像机、三个电灯、一个遥感器和一个警报器组成。保安系统的操作人员需要经常将这些仪器启动和关闭。

不使用门面模式的设计

首先,在不使用门面模式的情况下,操作这个保安系统的操作员必须直接操作所有的这些部件。下图所示就是在不使用门面模式的情况下系统的设计图。

 


可以看出,Client对象需要引用到所有的录像机(Camera)、电灯(Light)、感应器(Sensor)和警报器(Alarm)对象。代码如下:

using System;

public class Camera
{
  
public void TurnOn()
  
{
    Console.WriteLine(
"Turning on the camera.");
  }


  
public void TurnOff()
  
{
    Console.WriteLine(
"Turning off the camera.");
  }


  
public void Rotate(int degrees)
  
{
    Console.WriteLine(
"Rotating the camera by {0} degrees.", degrees);
  }

}


public class Light
{

  
public void TurnOff()
  
{
    Console.WriteLine(
"Turning on the light.");
  }


  
public void TurnOn()
  
{
    Console.WriteLine(
"Turning off the light.");
  }


  
public void ChangeBulb()
  
{
    Console.WriteLine(
"changing the light-bulb.");
  }

}


public class Sensor
{
  
public void Activate()
  
{
    Console.WriteLine(
"Activating the sensor.");
  }


  
public void Deactivate()
  
{
    Console.WriteLine(
"Deactivating the sensor.");
  }


  
public void Trigger()
  
{
    Console.WriteLine(
"The sensor has triggered.");
  }

}


public class Alarm
{

  
public void Activate()
  
{
    Console.WriteLine(
"Activating the alarm.");
  }


  
public void Deactivate()
  
{
    Console.WriteLine(
"Deactivating the alarm.");
  }


  
public void Ring()
  
{
    Console.WriteLine(
"Ringing the alarm.");
  }


  
public void StopRing()
  
{
    Console.WriteLine(
"Stop the alarm.");
  }

}


public class Client
{
  
private static Camera camera1, camera2;
  
private static Light light1, light2, light3;
  
private static Sensor sensor;
  
private static Alarm alarm;

  
static Client()
  
{
    camera1 
= new Camera();
    camera2 
= new Camera();
    light1 
= new Light();
    light2 
= new Light();
    light3 
= new Light();
    sensor 
= new Sensor();
    alarm 
= new Alarm();
  }
  

  
public static void Main( string[] args )
  
{
    camera1.TurnOn();
    camera2.TurnOn();
    light1.TurnOn();
    light2.TurnOn();
    light3.TurnOn();
    sensor.Activate();
    alarm.Activate();
  }

}

 

六、 使用门面模式的设计

一个合情合理的改进方法就是准备一个系统的控制台,作为保安系统的用户界面。如下图所示:

 

程序代码如下:

using System;

public class Camera
{
  
public void TurnOn()
  
{
    Console.WriteLine(
"Turning on the camera.");
  }


  
public void TurnOff()
  
{
    Console.WriteLine(
"Turning off the camera.");
  }


  
public void Rotate(int degrees)
  
{
    Console.WriteLine(
"Rotating the camera by {0} degrees.", degrees);
  }

}


public class Light
{

  
public void TurnOff()
  
{
    Console.WriteLine(
"Turning on the light.");
  }


  
public void TurnOn()
  
{
    Console.WriteLine(
"Turning off the light.");
  }


  
public void ChangeBulb()
  
{
    Console.WriteLine(
"changing the light-bulb.");
  }

}


public class Sensor
{
  
public void Activate()
  
{
    Console.WriteLine(
"Activating the sensor.");
  }


  
public void Deactivate()
  
{
    Console.WriteLine(
"Deactivating the sensor.");
  }


  
public void Trigger()
  
{
    Console.WriteLine(
"The sensor has triggered.");
  }

}


public class Alarm
{

  
public void Activate()
  
{
    Console.WriteLine(
"Activating the alarm.");
  }


  
public void Deactivate()
  
{
    Console.WriteLine(
"Deactivating the alarm.");
  }


  
public void Ring()
  
{
    Console.WriteLine(
"Ringing the alarm.");
  }


  
public void StopRing()
  
{
    Console.WriteLine(
"Stop the alarm.");
  }

}


public class SecurityFacade
{
  
private static Camera camera1, camera2;
  
private static Light light1, light2, light3;
  
private static Sensor sensor;
  
private static Alarm alarm;

  
static SecurityFacade()
  
{
    camera1 
= new Camera();
    camera2 
= new Camera();
    light1 
= new Light();
    light2 
= new Light();
    light3 
= new Light();
    sensor 
= new Sensor();
    alarm 
= new Alarm();
  }

  
  
public void Activate()
  
{
    camera1.TurnOn();
    camera2.TurnOn();
    light1.TurnOn();
    light2.TurnOn();
    light3.TurnOn();
    sensor.Activate();
    alarm.Activate();
  }


  
public void Deactivate()
  
{
    camera1.TurnOff();
    camera2.TurnOff();
    light1.TurnOff();
    light2.TurnOff();
    light3.TurnOff();
    sensor.Deactivate();
    alarm.Deactivate();
  }

}


public class Client
{
  
private static SecurityFacade security;

  
public static void Main( string[] args )
  
{
    security 
= new SecurityFacade();
    security.Activate();
    Console.WriteLine(
"\n--------------------\n");
    security.Deactivate();
  }

}