外观模式(Facade)
下面我从《Head First Design Patterns》中的例子看一下外观模式。
例子是这样描述的,说是美国有很多人搞家庭影院(我考虑一种最简单的方式,也就是全部是打开和关闭),在家庭影院中,首先必须要有灯光,屏幕,投影机,功放机,DVD 播放器这几个基本的工具,
而灯光呢可以关闭灯光,打开灯光,
投影机呢,可以打开和关闭投影机,
屏幕呢,也可以打开和关闭,
功放机的话,关闭音量,打开音量,
DVD 播放器的话可以打开播放器和关闭播放器。
以最普通的方式来实现观看电影的话,估计类图会如下所示:
然后我要打开看电影的话,我必须在客户端执行下面的操作:
先打开投影仪,再打开功放机,再打开屏幕,再打开 DVD 播放机,再打开灯光。在经历了这么多操作后,您才可以看一场电影(看得多不爽啊,居然这么多操作,太复杂了)。而后在关闭的时候,你还是要先关闭投影仪,再关闭功放机,再关闭屏幕,再关闭 DVD 播放机,再关闭灯光。
哦,这是太复杂了!!!在客户端居然有那么多操作(问题是还有一些用户可能不知道如何使用其中的一个工具那他便看不了电影),用户简直会烦死去!!!
上面其实反映的是一个现今软件开发系统中的一个比较常见的现象,
那就是客户端程序经常和复杂系统的内部子系统产生直接联系,而导致客户程序随着子系统的变化而变化。
而上面的例子中呢,客户端程序便是用户的操作,而复杂系统的内部子系统代表的就是这些工具的一些使用接口。
上面的例子中我还只是使用了最简单的工具操作接口,即简单的打开和关闭,如果在子系统,即各个工具中还有新的功能的话呢?那么必然会导致客户端代码得变化。
要想解决上面的这一串问题,你必须要简化客户程序与子系统之间的交互接口(要使得不会使用所有工具的用户也可以实现观看电影),然后就是要解除客户程序和子系统之间的耦合,而外观模式正好可以解决这个问题。
一、外观模式(Facade)的定义
为子系统中的一组接口提供一个一致的界面,用来访问子系统中的一群接口,
此模式定义了一个高层的接口,这个接口使得这一子系统更加容易使用。
简单的说,就是外观模式将一个或者多个类的复杂的操作进行了隐藏,只显示出一个一致的界面供客户端使用。
还有需要注意的是,外观模式仅仅是给你提供了更为直接和容易的操作方式,它并没有把原来的子系统进行隔离,
所以,如果你还需要子系统类的更高层的功能,还是可以使用原来的子系统的,这个是外观模式的一大优点。同时,通过外观模式可以子系统的多个接口上建立一个高层接口,并且将这个高层接口提供给客户端使用,
这样便可以解除掉客户端和复杂子系统之间的耦合。
同时,外观模式也可以使我们遵循迪米特法则(也就是最小知识原则)。
二、外观模式结构图
从上面的类图就可以看出了,确实通过外观模式可以实现提供简单的接口(OpenMovie 和 CloseMovie)给客户端,也给客户端和子系统之间实现了解耦。
三、代码实现
下面就通过代码来实现上面的这个 Demo
(这个 Demo 为了演示,在 Projector 即投影仪类中添加了两个方法,即设置为宽屏模式播放,或者是标准模式播放)
在这里还是贴出一下类图吧
下面就先来看几个播放工具的代码吧
using System;
namespace Facade
{
/// <summary>
/// 投影仪
/// </summary>
public class Projector
{
public void OpenProjector()
{
Console.WriteLine("打开投影仪");
}public void CloseProjector()
{
Console.WriteLine("关闭投影仪");
}public void SetWideScreen()
{
Console.WriteLine("投影仪状态为宽屏模式");
}public void SetStandardScreen()
{
Console.WriteLine("投影仪状态为标准模式");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// 功放机
/// </summary>
public class Amplifier
{
public void OpenAmplifier()
{
Console.WriteLine("打开功放机");
}public void CloseAmplifier()
{
Console.WriteLine("关闭功放机");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// 屏幕
/// </summary>
public class Screen
{
public void OpenScreen()
{
Console.WriteLine("打开屏幕");
}public void CloseScreen()
{
Console.WriteLine("关闭屏幕");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// DVD播放器
/// </summary>
public class DVDPlayer
{
public void OpenDVDPlayer()
{
Console.WriteLine("打开 DVD 播放器");
}public void CloseDVDPlayer()
{
Console.WriteLine("关闭 DVD 播放器");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// 灯光
/// </summary>
public class Light
{
public void OpenLight()
{
Console.WriteLine("打开灯光");
}public void CloseLight()
{
Console.WriteLine("关闭灯光");
}
}
}下面再贴出外观类中的代码
namespace Facade
{
/// <summary>
/// 定义一个外观
/// </summary>
public class MovieFacade
{
/// <summary>
/// 在外观类中必须保存有子系统中各个对象
/// </summary>
private Projector projector;
private Amplifier amplifier;
private Screen screen;
private DVDPlayer dvdPlayer;
private Light light;public MovieFacade()
{
projector = new Projector();
amplifier = new Amplifier();
screen = new Screen();
dvdPlayer = new DVDPlayer();
light = new Light();
}/// <summary>
/// 打开电影
/// </summary>
public void OpenMovie()
{
//先打开投影仪
projector.OpenProjector();
//再打开功放
amplifier.OpenAmplifier();
//再打开屏幕
screen.OpenScreen();
//再打开 DVD
dvdPlayer.OpenDVDPlayer();
//再打开灯光
light.OpenLight();
}/// <summary>
/// 关闭电影
/// </summary>
public void CloseMovie()
{
//关闭投影仪
projector.CloseProjector();
//关闭功放
amplifier.CloseAmplifier();
//关闭屏幕
screen.CloseScreen();
//关闭 DVD
dvdPlayer.CloseDVDPlayer();
//关闭灯光
light.CloseLight();
}
}
}最后贴出客户端代码
using System;
namespace FacadeTest
{
class Program
{
static void Main(string[] args)
{
Facade.MovieFacade movie = new Facade.MovieFacade();
Facade.Projector projector = new Facade.Projector();
//首先是观看电影
movie.OpenMovie();Console.WriteLine();
//然后是将投影仪模式调到宽屏模式
projector.SetWideScreen();
//再将投影仪模式调回普通模式
projector.SetStandardScreen();
Console.WriteLine();
//最后就是关闭电影了
movie.CloseMovie();Console.ReadKey();
}
}
}
效果如下
从上面的截图可以看出,我还是可以在客户端中使用子系统中的内容,即外观模式并没有把子系统和客户端隔离开来,其只是提供了整洁的接口给客户端,但是,如果客户端想访问复杂子系统中的接口时还是一样的可以访问的,比如在上面的 Demo 中就访问了子系统中的投影仪,并且设置了宽屏和普通等模式。
从上面的 Demo 中也可以清晰地看出,
外观模式可以提供一个简洁的外观接口来实现将一个复杂的子系统变得容易使用。
同时,在客户端还是可以访问原来子系统中的复杂的接口的。
好了,外观模式的介绍就到这里了。