解析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)觀看電影,類(lèi)圖如下所示:
按照類(lèi)圖所示,如果要觀看電影,必須在客戶端執(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è)類(lèi)的復(fù)雜的操作進(jìn)行了隱藏,只顯示出一個(gè)一致的界面供客戶端使用。需要注意的是,外觀模式僅僅是給你提供了更為直接和容易的操作方式,它并沒(méi)有把原來(lái)的子系統(tǒng)進(jìn)行隔離,所以,如果你還需要子系統(tǒng)類(lèi)的更高層的功能,還是可以使用原來(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)閉燈光"); } } }
外觀類(lèi)中的代碼:
namespace Facade { /// <summary> /// 定義一個(gè)外觀 /// </summary> public class MovieFacade { /// <summary> /// 在外觀類(lèi)中必須保存有子系統(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)之后,為了幫助理清外觀模式中類(lèi)之間的關(guān)系,下面給出上面實(shí)現(xiàn)代碼中類(lèi)圖:
然而對(duì)于外觀模式而言,是沒(méi)有一個(gè)一般化的類(lèi)圖描述,下面演示一個(gè)外觀模式的示意性對(duì)象圖來(lái)加深大家對(duì)外觀模式的理解:
在上面的對(duì)象圖中有兩個(gè)角色:
門(mén)面(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ú)的類(lèi),而是一個(gè)類(lèi)的集合。每個(gè)子系統(tǒng)都可以被客戶端直接調(diào)用或被門(mén)面角色調(diào)用。對(duì)于子系統(tǒng)而言,門(mén)面僅僅是另外一個(gè)客戶端,子系統(tǒng)并不知道門(mén)面的存在。
外觀的優(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)可能需要修改外觀類(lèi)或客戶端的源代碼,這樣就違背了”開(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)文章
Winform實(shí)現(xiàn)鼠標(biāo)可穿透的窗體鏤空效果
這篇文章主要介紹了Winform實(shí)現(xiàn)鼠標(biāo)可穿透的窗體鏤空效果的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10CPF?使用C#的Native?AOT?發(fā)布程序的詳細(xì)過(guò)程
這篇文章主要介紹了CPF?使用C#的Native?AOT?發(fā)布程序,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具體一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03c# SQLHelper(for winForm)實(shí)現(xiàn)代碼
數(shù)據(jù)連接池c# SQLHelper 實(shí)現(xiàn)代碼2009-02-02WPF實(shí)現(xiàn)環(huán)(圓)形菜單的示例代碼
這篇文章主要介紹了如何利用WPF繪制一個(gè)簡(jiǎn)單的環(huán)形菜單,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下2022-07-07C# TabControl控件中TabPage選項(xiàng)卡切換時(shí)的觸發(fā)事件問(wèn)題
這篇文章主要介紹了C# TabControl控件中TabPage選項(xiàng)卡切換時(shí)的觸發(fā)事件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04C# 使用鼠標(biāo)點(diǎn)擊對(duì)Chart控件實(shí)現(xiàn)數(shù)據(jù)提示效果
這篇文章主要介紹了C# 使用鼠標(biāo)點(diǎn)擊對(duì)Chart控件實(shí)現(xiàn)數(shù)據(jù)提示效果,文章給予上一篇的詳細(xì)內(nèi)容做延伸介紹,需要的小伙伴可任意參考一下2022-08-08