设计模式 - 外观模式

外观模式还是相当普遍的,先来看看几个例子:

1. 一台电脑有很多东西组成,包括硬件,软件。硬件又包含主板,CPU,内存什么的,软件又包含操作系统,操作系统里面又有一大堆非常复杂的驱动,各个驱动之间又有很多关系。那么我们怎么启动这么复杂的一个东西呢?我们所要做的就是按一下电源开关。当用户按下电源开关后,操作系统会把电脑运行起来,中间过程那是相当的复杂。对于用户来说,电源开关就是一个简单使用的接口,是一个facade。如果我们想进入安全模式,OK,在启动过程中按一下F8,那么这是另外一个facade,F8会调用一系列子系统来进入安全模式。

2. 一个手机有很多东西组成,那么怎么启动手机呢?也就是按一下电源。

3. 现在的汽车大多数都是手自一体的,那么自动档就是一个FACADE,通过自动档系统,用户根本就无需知道怎么时候挂1档,什么时候挂5档。当然对于那些有需求的驾驶员来说,他可以使用手动档(子系统)。

通过以上几个例子,我们基本可以知道外观模式(FACADE)其实就是给子系统包装一下,然后提供一个简单的接口让用户可以比较容易的使用。

 

GOF设计模式中是这么定义的:

意图

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

 

结构图

上面的结构图,我们假设有一个子系统,子系统里面有很多子部件,子部件之间有很多耦合关系,那么如果让用户来调用这些子系统也不是不可以,但是可能会比较麻烦,或者让一个对子系统架构不熟悉的程序员来调用的话,还可能出现错误。如果我们可以提供一个facade类,这个facade会把子系统包装起来,然后里面提供一个Run函数,用户只需要调用Facade的Run就可以了,这就隐藏了子系统的调用过程,对于客户来说减少了大量的工作。

这里举一个非常简单的例子,我们还是继续沿用Composite和Decorator里面用过的例子。这次我们假设我们有一个子系统,这个子系统可以画直线,画正方形,画照片。子系统包括:CLine,CRect和CPhoto。其中CRect耦合了CLine,也就是说CRect是用CLine画出来的。子系统结构如下:

假如我们现在要画一个带框的照片(就是一个正方形,里面有个照片),该怎么画呢?可以这么做:

[cpp]  view plain copy
  1. CLine* line = new CLine();  
  2.     CRect* rect = new CRect(line);  
  3.     CPhoto* photo = new CPhoto();  
  4.   
  5.     rect->Draw();  
  6.     photo->Draw();  
  7.   
  8.     delete rect;  
  9.     delete photo;  

先生成一个CLine对象,然后传给CRect对象,再生成一个CPhone对象,然后按照顺序调用CRect的Draw()和CPhone的Draw()。

没有问题,我成功地画了一个正方形,然后正方形里面有个照片。但是这里有个问题:假如我们的程序员对这套子系统不熟悉怎么办呢?他得先学习一下这套子系统,包括子系统里面的子部件的关系,然后他才能正确地画出带框照片。如果我们在这套子系统的基础上提供一个Facade,会不会好点呢?给出代码:

[cpp]  view plain copy
  1. #pragma once  
  2.   
  3. #include <iostream>  
  4.   
  5. class CGraphic  
  6. {  
  7. public:  
  8.     virtual void Draw() = 0;  
  9. };  
  10.   
  11. class CLine: public CGraphic  
  12. {  
  13. public:  
  14.     virtual void Draw()  
  15.     {  
  16.         std::cout << "Draw line\n";  
  17.     }  
  18. };  
  19.   
  20. class CRect: public CGraphic  
  21. {  
  22. public:  
  23.     CRect(CLine* line): _line(line)  
  24.     {  
  25.   
  26.     }  
  27.   
  28.     virtual ~CRect()  
  29.     {  
  30.         if (_line)  
  31.         {  
  32.             delete _line;  
  33.             _line = NULL;  
  34.         }  
  35.     }  
  36.   
  37.     virtual void Draw()  
  38.     {  
  39.         _line->Draw();  
  40.         std::cout << "Draw rect\n";  
  41.     }  
  42. protected:  
  43.     CLine* _line;  
  44. };  
  45.   
  46. class CPhoto: public CGraphic  
  47. {  
  48. public:  
  49.     void Draw()  
  50.     {  
  51.         std::cout << "draw picture\n";  
  52.     }  
  53. };  
  54.   
  55. class CBorderPhotoFacade  
  56. {  
  57. public:  
  58.     static CBorderPhotoFacade* GetInst()  
  59.     {  
  60.         static CBorderPhotoFacade facade;  
  61.         return &facade;  
  62.     }  
  63.   
  64.     virtual void DrawPhoto()  
  65.     {  
  66.         CLine* line = new CLine();  
  67.         CRect* rect = new CRect(line);  
  68.         CPhoto* photo = new CPhoto();  
  69.   
  70.         rect->Draw();  
  71.         photo->Draw();  
  72.   
  73.         delete rect;  
  74.         delete photo;  
  75.     }  
  76. protected:  
  77.     CBorderPhotoFacade()  
  78.     {  
  79.   
  80.     }  
  81. };  

上面的代码中,CGraphic,CLine,CRect,CPhoto共同组成了一个子系统。然后CBorderPhotoFacade是一个外观类,里面就提供了一个函数DrawPhoto(),这个函数会画出一个带框照片。那么客户端就可以这么调用:

[cpp]  view plain copy
  1. CBorderPhotoFacade::GetInst()->DrawPhoto();  

跟前面的版本相比较,从客户端的角度来讲,就是用CBorderPhotoFacade::DrawPhoto()代替了之前一系列子系统类的调用。也就是说,我们通过CBorderPhotoFacade类提供了一个高层接口,使得用户只需要调用这个高层接口,而无需去调用复杂的子系统。这个就是Facade模式的作用。
 

 优点:

1. 它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目使得子系统使用起来更加方便。上面的例子里面,客户端根本无需知道CLine,CRect,CPhoto,直接调用CBorderPhotoFacade就可以了。

2. 它实现了子系统和客户之间的松耦合关系;

3. 如果应用需要,客户还可以继续使用子系统。Facade只是提供一个高层接口,如果高层接口不能满足客户需要,那么直接调用子系统就是了。

 

进阶:

例子里面我们只是提供了一个Facade类,那么当我们需要提供多个Facade的时候,我们可以给Facade提供一个接口,然后子类化Facade。这样更灵活,当然付出的代价也大了,因为需要多增加几个类。或者我们可以给Facade类增加一个参数factory,然后把子系统对象的创建放到factory里面,也就是引入抽象工厂模式。这些都视情况而定,如果子系统比较复杂,那么就可以考虑Facade子类化或者直接引入抽象工厂类等等。

 

另外,通常来讲,Facade都是单例对象。我们的例子里面也把Facade类做成了单例类。


转载:http://blog.csdn.net/zj510/article/details/8139499

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值