解析C#設(shè)計(jì)模式編程中外觀模式Facade Pattern的應(yīng)用
實(shí)例引入
在家庭影院中,有燈光,屏幕,投影機(jī),功放機(jī),DVD 播放器這幾個(gè)基本的工具:
- 燈光,可以關(guān)閉燈光和打開(kāi)燈光。
- 投影機(jī),可以打開(kāi)和關(guān)閉投影機(jī)。
- 屏幕,可以打開(kāi)和關(guān)閉。
- 功放機(jī),可以關(guān)閉音量和打開(kāi)音量。
- DVD 播放器,可以打開(kāi)播放器和關(guān)閉播放器。
以最普通的方式實(shí)現(xiàn)觀看電影,類圖如下所示:

按照類圖所示,如果要觀看電影,必須在客戶端執(zhí)行下面的操作:先打開(kāi)投影儀,再打開(kāi)功放機(jī),再打開(kāi)屏幕,再打開(kāi) DVD 播放機(jī),再打開(kāi)燈光,在經(jīng)歷了這么多操作后,才可以看一場(chǎng)電影。而在關(guān)閉電影的時(shí)候,也要先關(guān)閉投影儀,再關(guān)閉功放機(jī),再關(guān)閉屏幕,再關(guān)閉 DVD 播放機(jī),再關(guān)閉燈光。哦,這是太復(fù)雜了?。?!在客戶端居然有那么多操作,如果有一些用戶不知道如何使用其中的一個(gè)工具,那他便看不了電影!
上面其實(shí)反映的是現(xiàn)今軟件開(kāi)發(fā)系統(tǒng)中的一個(gè)比較常見(jiàn)的現(xiàn)象,客戶端程序經(jīng)常和復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)產(chǎn)生直接聯(lián)系,導(dǎo)致客戶程序隨著子系統(tǒng)的變化而變化。要想解決上面的這一串問(wèn)題,必須要簡(jiǎn)化客戶程序與子系統(tǒng)之間的交互接口,解除客戶程序和子系統(tǒng)之間的耦合,而外觀模式正好可以解決這個(gè)問(wèn)題。
外觀模式(Facade)的定義:為子系統(tǒng)中的一組接口提供一個(gè)一致的界面,用來(lái)訪問(wèn)子系統(tǒng)中的一群接口。
此模式定義了一個(gè)高層的接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。簡(jiǎn)單的說(shuō),就是外觀模式將一個(gè)或者多個(gè)類的復(fù)雜的操作進(jìn)行了隱藏,只顯示出一個(gè)一致的界面供客戶端使用。需要注意的是,外觀模式僅僅是給你提供了更為直接和容易的操作方式,它并沒(méi)有把原來(lái)的子系統(tǒng)進(jìn)行隔離,所以,如果你還需要子系統(tǒng)類的更高層的功能,還是可以使用原來(lái)的子系統(tǒng)的,這個(gè)是外觀模式的一大優(yōu)點(diǎn)。通過(guò)外觀模式可以將子系統(tǒng)的多個(gè)接口上建立一個(gè)高層接口,并且將這個(gè)高層接口提供給客戶端使用,這樣便可以解除掉客戶端和復(fù)雜子系統(tǒng)之間的耦合。

通過(guò)上圖可以看出,外觀模式實(shí)現(xiàn)提供簡(jiǎn)單的接口(OpenMovie 和 CloseMovie)給客戶端,也給客戶端和子系統(tǒng)之間實(shí)現(xiàn)了解耦。下面通過(guò)代碼來(lái)實(shí)現(xiàn)上面的這個(gè) Demo。

幾個(gè)播放工具的代碼:
using System;
namespace Facade
{
/// <summary>
/// 投影儀
/// </summary>
public class Projector
{
public void OpenProjector()
{
Console.WriteLine("打開(kāi)投影儀");
}
public void CloseProjector()
{
Console.WriteLine("關(guān)閉投影儀");
}
public void SetWideScreen()
{
Console.WriteLine("投影儀狀態(tài)為寬屏模式");
}
public void SetStandardScreen()
{
Console.WriteLine("投影儀狀態(tài)為標(biāo)準(zhǔn)模式");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// 功放機(jī)
/// </summary>
public class Amplifier
{
public void OpenAmplifier()
{
Console.WriteLine("打開(kāi)功放機(jī)");
}
public void CloseAmplifier()
{
Console.WriteLine("關(guān)閉功放機(jī)");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// 屏幕
/// </summary>
public class Screen
{
public void OpenScreen()
{
Console.WriteLine("打開(kāi)屏幕");
}
public void CloseScreen()
{
Console.WriteLine("關(guān)閉屏幕");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// DVD播放器
/// </summary>
public class DVDPlayer
{
public void OpenDVDPlayer()
{
Console.WriteLine("打開(kāi) DVD 播放器");
}
public void CloseDVDPlayer()
{
Console.WriteLine("關(guān)閉 DVD 播放器");
}
}
}
using System;
namespace Facade
{
/// <summary>
/// 燈光
/// </summary>
public class Light
{
public void OpenLight()
{
Console.WriteLine("打開(kāi)燈光");
}
public void CloseLight()
{
Console.WriteLine("關(guān)閉燈光");
}
}
}
外觀類中的代碼:
namespace Facade
{
/// <summary>
/// 定義一個(gè)外觀
/// </summary>
public class MovieFacade
{
/// <summary>
/// 在外觀類中必須保存有子系統(tǒng)中各個(gè)對(duì)象
/// </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>
/// 打開(kāi)電影
/// </summary>
public void OpenMovie()
{
//先打開(kāi)投影儀
projector.OpenProjector();
//再打開(kāi)功放
amplifier.OpenAmplifier();
//再打開(kāi)屏幕
screen.OpenScreen();
//再打開(kāi) DVD
dvdPlayer.OpenDVDPlayer();
//再打開(kāi)燈光
light.OpenLight();
}
/// <summary>
/// 關(guān)閉電影
/// </summary>
public void CloseMovie()
{
//關(guān)閉投影儀
projector.CloseProjector();
//關(guān)閉功放
amplifier.CloseAmplifier();
//關(guān)閉屏幕
screen.CloseScreen();
//關(guān)閉 DVD
dvdPlayer.CloseDVDPlayer();
//關(guān)閉燈光
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();
//然后是將投影儀模式調(diào)到寬屏模式
projector.SetWideScreen();
//再將投影儀模式調(diào)回普通模式
projector.SetStandardScreen();
Console.WriteLine();
//最后就是關(guān)閉電影了
movie.CloseMovie();
Console.ReadKey();
}
}
}
從上例中可以看出,可以在客戶端中使用子系統(tǒng)中的內(nèi)容,即外觀模式并沒(méi)有把子系統(tǒng)和客戶端隔離開(kāi)來(lái),只是提供了整潔的接口給客戶端,如果客戶端想訪問(wèn)復(fù)雜子系統(tǒng)中的接口時(shí)還是一樣的可以訪問(wèn)的。比如在上面的 Demo 中的設(shè)置了寬屏和普通等模式。
外觀模式的結(jié)構(gòu)總結(jié)
看完外觀模式的實(shí)現(xiàn)之后,為了幫助理清外觀模式中類之間的關(guān)系,下面給出上面實(shí)現(xiàn)代碼中類圖:

然而對(duì)于外觀模式而言,是沒(méi)有一個(gè)一般化的類圖描述,下面演示一個(gè)外觀模式的示意性對(duì)象圖來(lái)加深大家對(duì)外觀模式的理解:

在上面的對(duì)象圖中有兩個(gè)角色:
門面(Facade)角色:客戶端調(diào)用這個(gè)角色的方法。該角色知道相關(guān)的一個(gè)或多個(gè)子系統(tǒng)的功能和責(zé)任,該角色會(huì)將從客戶端發(fā)來(lái)的請(qǐng)求委派帶相應(yīng)的子系統(tǒng)中去。
子系統(tǒng)(subsystem)角色:可以同時(shí)包含一個(gè)或多個(gè)子系統(tǒng)。每個(gè)子系統(tǒng)都不是一個(gè)單獨(dú)的類,而是一個(gè)類的集合。每個(gè)子系統(tǒng)都可以被客戶端直接調(diào)用或被門面角色調(diào)用。對(duì)于子系統(tǒng)而言,門面僅僅是另外一個(gè)客戶端,子系統(tǒng)并不知道門面的存在。
外觀的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
外觀模式對(duì)客戶屏蔽了子系統(tǒng)組件,從而簡(jiǎn)化了接口,減少了客戶處理的對(duì)象數(shù)目并使子系統(tǒng)的使用更加簡(jiǎn)單。
外觀模式實(shí)現(xiàn)了子系統(tǒng)與客戶之間的松耦合關(guān)系,而子系統(tǒng)內(nèi)部的功能組件是緊耦合的。松耦合使得子系統(tǒng)的組件變化不會(huì)影響到它的客戶。
缺點(diǎn):
如果增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼,這樣就違背了”開(kāi)——閉原則“(不過(guò)這點(diǎn)也是不可避免)。
使用場(chǎng)景
在以下情況下可以考慮使用外觀模式:
外一個(gè)復(fù)雜的子系統(tǒng)提供一個(gè)簡(jiǎn)單的接口
提供子系統(tǒng)的獨(dú)立性
在層次化結(jié)構(gòu)中,可以使用外觀模式定義系統(tǒng)中每一層的入口。其中三層架構(gòu)就是這樣的一個(gè)例子。
總結(jié)
到這里外觀模式的介紹就結(jié)束了,外觀模式,為子系統(tǒng)的一組接口提供一個(gè)統(tǒng)一的接口,該模式定義了一個(gè)高層接口,這一個(gè)高層接口使的子系統(tǒng)更加容易使用。并且外觀模式可以解決層結(jié)構(gòu)分離、降低系統(tǒng)耦合度和為新舊系統(tǒng)交互提供接口功能。
相關(guān)文章
C#通過(guò)創(chuàng)建Windows服務(wù)啟動(dòng)程序的方法詳解
這篇文章主要介紹了C#通過(guò)創(chuàng)建Windows服務(wù)啟動(dòng)程序的方法,較為詳細(xì)的分析了C#創(chuàng)建Windows服務(wù)應(yīng)用程序的步驟與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-06-06
使用XmlSerializer序列化List對(duì)象成XML格式(list對(duì)象序列化)
這篇文章主要介紹了使用XmlSerializer序列化List對(duì)象成XML格式(list對(duì)象序列化),需要的朋友可以參考下2014-03-03
C#使用表達(dá)式樹(shù)實(shí)現(xiàn)對(duì)象復(fù)制的示例代碼
這篇文章主要介紹了C#使用表達(dá)式樹(shù)實(shí)現(xiàn)對(duì)象復(fù)制,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01
使用WPF實(shí)現(xiàn)加載動(dòng)畫(huà)效果
在應(yīng)用程序加載大量數(shù)據(jù)或執(zhí)行復(fù)雜操作時(shí),為用戶提供一個(gè)良好的加載體驗(yàn)變得至關(guān)重要,加載動(dòng)畫(huà)是其中一個(gè)有效的方式,下面我們就來(lái)看看如何使用WPF實(shí)現(xiàn)簡(jiǎn)單的加載動(dòng)畫(huà)效果吧2024-03-03
基于集合的子集與集合的全排列的相關(guān)問(wèn)題
本篇文章介紹了,集合的子集與集合的全排列的相關(guān)系列問(wèn)題說(shuō)明,需要的朋友參考下2013-05-05
C# winform登陸框驗(yàn)證碼的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了C# winform登陸框驗(yàn)證碼的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08

