设计模式系列(十一)外观模式(Facade Pattern)

设计模式系列(十一)外观模式(Facade Pattern)


    外观模式提供了统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。外观模式的意图是简化接口,让用户更加方便使用。举例来说,如果在家里布置了一个家庭影院,包括电影显示器,遥控器,爆米花机器等,当你想要享受一场电影的时候,你需要打开显示器、操作遥控器的一系列按钮、打开爆米花机器等,也就是说享受一场电影需要一个十分复杂的操作,然后等看电影结束后还需要逆向执行这一系列操作,这个过程十分复杂,大家估计都不想一步步自己亲手去做。而外观模式就是将家庭影院这个子系统的接口进行进一步封装,封装成一个高层接口,即所谓的外观,然后让用户直接调用高层接口,而高层接口负责调用低层接口,那么用户就可以直接选择看电影,紧接着上面的一系列操作就会自动执行,结束观影也是同样的道理,用户相当于只调用了高层接口的一个函数,其他的调用由高层函数执行,从而简化了接口。

    虽然外观模式简化了接口,但是并不是说用户只能选择一键观影去调用高层接口,当用户想自己执行这一系列的操作时,也可以随时自己再去调用低层的这些操作,所以外观模式只是提供了一个简化的接口让用户调用,至于用户是否去调用是用户的选择,用户可以调用高层接口也可以选择去调用低层接口。

    外观模式不仅简化了接口,而且也将客户从组件的子系统中解耦。外观和适配器可以包装许多类,但是外观的意图是简化接口,而适配器的意图是将接口转换成不同的接口。对于前面讲过的装饰者模式则是不改变接口,只是加入一定的责任或行为。

    这里需要提到一个设计原则:“最少知识原则”,即只和你的密友谈话,意思就是说尽量不要让太多的类耦合在一起,例如不要对调用后返回的对象直接再进行调用,JAVA中的system.out.println()就是一个典型违反了最少知识原则的使用,不过这个设计原则要从整体来看,有时候使用,有时候不使用,要根据实际的系统权衡。

    外观模式中的主要角色有:

(1)外观类(Facade):这个外观类就是用来封装子系统的接口,提供一个简单的接口给用户,一般外观类中都会包含很多子系统的对象,即使用了组合的方法,将子系统组合进外观中,然后将工作委托给子系统执行;

(2)客户(Clients):客户通过直接调用外观类中的简化后的接口来实现自己的需求;

(3)子系统(Subsystem):这个是要被外观类封装的低层接口的集合,一般有很多复杂的类,这些类之间也有复杂的关系,由外观类来进行封装简化。

    在以下情况下可以考虑使用外观模式:

(1)设计初期阶段,应该有意识的将不同层分离,层与层之间建立外观模式;

(2) 开发阶段,子系统越来越复杂,增加外观模式提供一个简单的调用接口;

(3)维护一个大型遗留系统的时候,可能这个系统已经非常难以维护和扩展,但又包含非常重要的功能,为其开发一个外观类,以便新系统与其交互。

    外观模式的优点是:

(1)松散耦合,即外观模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。

(2) 简单易用,即外观模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观交互就可以了,相当于外观类为外部客户端使用子系统提供了一站式服务;

(3)更好的划分访问层次,即通过合理使用Facade,可以帮助我们更好的划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好的隐藏了内部的细节。

    外观模式的缺点是:就像上面说过的一样,使用外观模式后,用户也可以选择调用低层的接口,所以会让用户有所迷惑。

    下面来看一个外观模式的例子,该例主要由三个文件组成,依次是:FacadePattern.h、FacadePattern.cpp、FacadePatternTest.cpp。

// 外观模式
// FacadePattern.h文件

#ifndef FACADE
#define FACADE

#include 
#include 
#include 
#include 

using std::string;
using std::cout;
using std::endl;
using std::vector;

// 外观模式中子系统的所有类
class Amplifier;
class Tuner;
class DvdPlayer;
class CdPlayer;
class Projector;
class TheaterLights;
class Screen;
class PopcornPopper;

class CdPlayer
{
public:
	CdPlayer(string description, Amplifier* amplifier)
	{
		this->description = description;
		this->amplifier = NULL;
	}

	void on();
	void off();
	void eject();
	void play(string title);
	void play(int track);
	void stop();
	void pause();
	string toString();
private:
	string description;
	int currentTrack;
	Amplifier* amplifier;
	string title;
};

class Amplifier 
{
public:
	Amplifier(string description) 
	{
		this->description = description;
		this->tuner = NULL;
		this->dvd   = NULL;
		this->cd    = NULL;
	}

	void on();
	void off();
	void setStereoSound();
	void setSurroundSound();
	void setVolume(int level);
	void setTuner(Tuner* tuner);
	void setDvd(DvdPlayer* dvd);
	void setCd(CdPlayer* cd);
	string toString();
private:
	string description;
	Tuner* tuner;
	DvdPlayer* dvd;
	CdPlayer* cd;
};

class DvdPlayer 
{
public:
	DvdPlayer(string description, Amplifier* amplifier) 
	{
		this->description = description;
		this->amplifier = amplifier;
	}

	void on();
	void off();
	void eject();
	void play(string movie);
	void play(int track);
	void stop();
	void pause();
	void setTwoChannelAudio();
	void setSurroundAudio();
	string toString();
private:
	string description;
	int currentTrack;
	Amplifier* amplifier;
	string movie;
};

class Projector 
{
public:
	Projector(string description, DvdPlayer* dvdPlayer) 
	{
		this->description = description;
		this->dvdPlayer = dvdPlayer;
	}

	void on();
	void off();
	void wideScreenMode();
	void tvMode();
	string toString();
private:
	string description;
	DvdPlayer *dvdPlayer;
};

class Screen 
{
public:
	Screen(string description) 
	{
		this->description = description;
	}

	void up();
	void down();
	string toString();
private:
	string description;
};

class Tuner 
{
public:
	Tuner(string description) 
	{
		this->description = description;
	}

	void on();
	void off();
	void setFrequency(double frequency);
	void setAm();
	void setFm();
	string toString();
private:
	string description;
	double frequency;
};

class TheaterLights 
{
public:
	TheaterLights(string description) 
	{
		this->description = description;
	}

	void on();
	void off();
	void dim(int level);
	string toString();
private:
	string description;
};

class PopcornPopper 
{
public:
	PopcornPopper(string description) 
	{
		this->description = description;
	}

	void on();
	void off();
	void pop();
	string toString();
private:
	string description;
};

// 外观模式中的外观类
class HomeTheaterFacade 
{
public:
	HomeTheaterFacade(Amplifier *amp,
					  Tuner *tuner,
					  DvdPlayer *dvd,
					  CdPlayer *cd,
					  Projector *projector,
					  Screen *screen,
					  TheaterLights *lights,
					  PopcornPopper *popper) 
	{

		this->amp = amp;
		this->tuner = tuner;
		this->dvd = dvd;
		this->cd = cd;
		this->projector = projector;
		this->screen = screen;
		this->lights = lights;
		this->popper = popper;
	}

	void watchMovie(string movie);
	void endMovie();
	void listenToCd(string cdTitle);
	void endCd();
	void listenToRadio(double frequency);
	void endRadio();
private:
	Amplifier *amp;
	Tuner *tuner;
	DvdPlayer *dvd;
	CdPlayer *cd;
	Projector *projector;
	TheaterLights *lights;
	Screen *screen;
	PopcornPopper *popper;
};

#endif

// FacadePattern.cpp文件

#include "FacadePattern.h"

// 外观模式中子系统的所有类
void CdPlayer::on()
{
	cout << description + " on" << endl;
}

void CdPlayer::off()
{
	cout << description + " off" << endl;
}

void CdPlayer::eject() {
	title = "";
	cout << description + " eject" << endl;
}

void CdPlayer::play(string title)
{
	this->title = title;
	currentTrack = 0;
	cout << description + " playing \"" + title + "\"" << endl;
}

void CdPlayer::play(int track)
{
	char buf[4];
	if (!title.compare(""))
	{
		sprintf_s(buf, "%d", currentTrack);
		cout << description + " can't play track " + buf +
			", no cd inserted" << endl;
	}
	else
	{
		currentTrack = track;
		sprintf_s(buf, "%d", currentTrack);
		cout << description + " playing track " + buf << endl;
	}
}

void CdPlayer::stop()
{
	currentTrack = 0;
	cout << description + " stopped" << endl;
}

void CdPlayer::pause()
{
	cout << description + " paused \"" + title + "\"" << endl;
}

string CdPlayer::toString()
{
	return description;
}

void Amplifier::on()
{
	cout << description + " on" << endl;
}

void Amplifier::off()
{
	cout << description + " off" << endl;
}

void Amplifier::setStereoSound()
{
	cout << description + " stereo mode on" << endl;
}

void Amplifier::setSurroundSound()
{
	cout << description + " surround sound on (5 speakers, 1 subwoofer)" << endl;
}

void Amplifier::setVolume(int level)
{
	char buf[4];
	sprintf_s(buf, "%d", level);
	cout << description + " setting volume to " + buf << endl;
}

void Amplifier::setTuner(Tuner* tuner)
{
	cout << description + " setting tuner to " + dvd->toString() << endl;
	this->tuner = tuner;
}

void Amplifier::setDvd(DvdPlayer* dvd)
{
	cout << description + " setting DVD player to " + dvd->toString() << endl;
	this->dvd = dvd;
}

void Amplifier::setCd(CdPlayer* cd)
{
	cout << description + " setting CD player to " + cd->toString() << endl;
	this->cd = cd;
}

string Amplifier::toString()
{
	return description;
}

void DvdPlayer::on()
{
	cout << description + " on" << endl;
}

void DvdPlayer::off()
{
	cout << description + " off" << endl;
}

void DvdPlayer::eject()
{
	movie = "";
	cout << description + " eject" << endl;
}

void DvdPlayer::play(string movie)
{
	this->movie = movie;
	currentTrack = 0;
	cout << description + " playing \"" + movie + "\"" << endl;
}

void DvdPlayer::play(int track)
{
	char buf[4];
	if (!movie.compare(""))
	{
		sprintf_s(buf, "%d", track);
		cout << description + " can't play track " + buf + " no dvd inserted" << endl;
	}
	else
	{
		currentTrack = track;
		sprintf_s(buf, "%d", track);
		cout << description + " playing track " + buf + " of \"" + movie + "\"" << endl;
	}
}

void DvdPlayer::stop()
{
	currentTrack = 0;
	cout << description + " stopped \"" + movie + "\"" << endl;
}

void DvdPlayer::pause()
{
	cout << description + " paused \"" + movie + "\"" << endl;
}

void DvdPlayer::setTwoChannelAudio()
{
	cout << description + " set two channel audio" << endl;
}

void DvdPlayer::setSurroundAudio()
{
	cout << description + " set surround audio" << endl;
}

string DvdPlayer::toString() 
{
	return description;
}

void Projector::on()
{
	cout << description + " on" << endl;
}

void Projector::off()
{
	cout << description + " off" << endl;
}

void Projector::wideScreenMode()
{
	cout << description + " in widescreen mode (16x9 aspect ratio)" << endl;
}

void Projector::tvMode()
{
	cout << description + " in tv mode (4x3 aspect ratio)" << endl;
}

string Projector::toString()
{
	return description;
}

void Screen::up()
{
	cout << description + " going up" << endl;
}

void Screen::down()
{
	cout << description + " going down" << endl;
}

string Screen::toString()
{
	return description;
}

void Tuner::on()
{
	cout << description + " on" << endl;
}

void Tuner::off()
{
	cout << description + " off" << endl;
}

void Tuner::setFrequency(double frequency)
{
	char buf[4];
	sprintf_s(buf, "%d", frequency);
	cout << description + " setting frequency to " + buf << endl;
	this->frequency = frequency;
}

void Tuner::setAm()
{
	cout << description + " setting AM mode" << endl;
}

void Tuner::setFm()
{
	cout << description + " setting FM mode" << endl;
}

string Tuner::toString()
{
	return description;
}

void TheaterLights::on()
{
	cout << description + " on" << endl;
}

void TheaterLights::off()
{
	cout << description + " off" << endl;
}

void TheaterLights::dim(int level)
{
	char buf[4];
	sprintf_s(buf, "%d", level);
	cout << description + " dimming to " + buf + "%" << endl;
}

string TheaterLights::toString()
{
	return description;
}

void PopcornPopper::on()
{
	cout << description + " on" << endl;
}

void PopcornPopper::off()
{
	cout << description + " off" << endl;
}

void PopcornPopper::pop()
{
	cout << description + " popping popcorn!" << endl;
}


string PopcornPopper::toString()
{
	return description;
}

void HomeTheaterFacade::watchMovie(string movie)
{
	cout << "Get ready to watch a movie..." << endl;
	popper->on();
	popper->pop();
	lights->dim(10);
	screen->down();
	projector->on();
	projector->wideScreenMode();
	amp->on();
	amp->setDvd(dvd);
	amp->setSurroundSound();
	amp->setVolume(5);
	dvd->on();
	dvd->play(movie);
}

void HomeTheaterFacade::endMovie()
{
	cout << "Shutting movie theater down..." << endl;
	popper->off();
	lights->on();
	screen->up();
	projector->off();
	amp->off();
	dvd->stop();
	dvd->eject();
	dvd->off();
}

void HomeTheaterFacade::listenToCd(string cdTitle)
{
	cout << "Get ready for an audiopile experence..." << endl;
	lights->on();
	amp->on();
	amp->setVolume(5);
	amp->setCd(cd);
	amp->setStereoSound();
	cd->on();
	cd->play(cdTitle);
}

void HomeTheaterFacade::endCd()
{
	cout << "Shutting down CD..." << endl;
	amp->off();
	amp->setCd(cd);
	cd->eject();
	cd->off();
}

void HomeTheaterFacade::listenToRadio(double frequency)
{
	cout << "Tuning in the airwaves..." << endl;
	tuner->on();
	tuner->setFrequency(frequency);
	amp->on();
	amp->setVolume(5);
	amp->setTuner(tuner);
}

void HomeTheaterFacade::endRadio()
{
	cout << "Shutting down the tuner..." << endl;
	tuner->off();
	amp->off();
}

// FacadePatternTest.cpp文件

#include "FacadePattern.h"

void main()
{
	Amplifier amp("Top-O-Line Amplifier");
	Tuner tuner("Top-O-Line AM/FM Tuner");
	DvdPlayer dvd("Top-O-Line DVD Player", &);
	CdPlayer cd("Top-O-Line CD Player", &);
	Projector projector("Top-O-Line Projector", &dvd);
	TheaterLights lights("Theater Ceiling Lights");
	Screen screen("Theater Screen");
	PopcornPopper popper("Popcorn Popper");

	HomeTheaterFacade homeTheater(&, &tuner, &dvd, &cd,
		&projector, &screen, &lights, &popper);

	cout << "-----------------------------------------" << endl;
	homeTheater.watchMovie("Raiders of the Lost Ark");

	cout << "-----------------------------------------" << endl;
	homeTheater.endMovie();
}

    该例的运行结果如图1所示,UML类图如图2所示。

设计模式系列(十一)外观模式(Facade Pattern)_第1张图片
图1 运行结果

设计模式系列(十一)外观模式(Facade Pattern)_第2张图片
图2 UML类图

    这个例子比较容易理解,HomeTheaterFacade类就是所谓的外观类,它组合了所有的子系统对象指针,然后对子系统的接口进行封装简化,提供简化后的接口给用户使用,图2中下面的一行类都是子系统中的类,可以看出这些类之间也有很多复杂的关系,只是外观类将用户调用简化了而已。

你可能感兴趣的:(C++,设计模式)