设计模式 - 外观模式

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

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画出来的。子系统结构如下:

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

CLine* line = new CLine();
	CRect* rect = new CRect(line);
	CPhoto* photo = new CPhoto();

	rect->Draw();
	photo->Draw();

	delete rect;
	delete photo;

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

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

#pragma once

#include <iostream>

class CGraphic
{
public:
	virtual void Draw() = 0;
};

class CLine: public CGraphic
{
public:
	virtual void Draw()
	{
		std::cout << "Draw line\n";
	}
};

class CRect: public CGraphic
{
public:
	CRect(CLine* line): _line(line)
	{

	}

	virtual ~CRect()
	{
		if (_line)
		{
			delete _line;
			_line = NULL;
		}
	}

	virtual void Draw()
	{
		_line->Draw();
		std::cout << "Draw rect\n";
	}
protected:
	CLine* _line;
};

class CPhoto: public CGraphic
{
public:
	void Draw()
	{
		std::cout << "draw picture\n";
	}
};

class CBorderPhotoFacade
{
public:
	static CBorderPhotoFacade* GetInst()
	{
		static CBorderPhotoFacade facade;
		return &facade;
	}

	virtual void DrawPhoto()
	{
		CLine* line = new CLine();
		CRect* rect = new CRect(line);
		CPhoto* photo = new CPhoto();

		rect->Draw();
		photo->Draw();

		delete rect;
		delete photo;
	}
protected:
	CBorderPhotoFacade()
	{

	}
};

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

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类做成了单例类。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值