解析C#設(shè)計模式編程中外觀模式Facade Pattern的應(yīng)用
實例引入
在家庭影院中,有燈光,屏幕,投影機,功放機,DVD 播放器這幾個基本的工具:
- 燈光,可以關(guān)閉燈光和打開燈光。
- 投影機,可以打開和關(guān)閉投影機。
- 屏幕,可以打開和關(guān)閉。
- 功放機,可以關(guān)閉音量和打開音量。
- DVD 播放器,可以打開播放器和關(guān)閉播放器。
以最普通的方式實現(xiàn)觀看電影,類圖如下所示:
按照類圖所示,如果要觀看電影,必須在客戶端執(zhí)行下面的操作:先打開投影儀,再打開功放機,再打開屏幕,再打開 DVD 播放機,再打開燈光,在經(jīng)歷了這么多操作后,才可以看一場電影。而在關(guān)閉電影的時候,也要先關(guān)閉投影儀,再關(guān)閉功放機,再關(guān)閉屏幕,再關(guān)閉 DVD 播放機,再關(guān)閉燈光。哦,這是太復(fù)雜了?。?!在客戶端居然有那么多操作,如果有一些用戶不知道如何使用其中的一個工具,那他便看不了電影!
上面其實反映的是現(xiàn)今軟件開發(fā)系統(tǒng)中的一個比較常見的現(xiàn)象,客戶端程序經(jīng)常和復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)產(chǎn)生直接聯(lián)系,導(dǎo)致客戶程序隨著子系統(tǒng)的變化而變化。要想解決上面的這一串問題,必須要簡化客戶程序與子系統(tǒng)之間的交互接口,解除客戶程序和子系統(tǒng)之間的耦合,而外觀模式正好可以解決這個問題。
外觀模式(Facade)的定義:為子系統(tǒng)中的一組接口提供一個一致的界面,用來訪問子系統(tǒng)中的一群接口。
此模式定義了一個高層的接口,這個接口使得這一子系統(tǒng)更加容易使用。簡單的說,就是外觀模式將一個或者多個類的復(fù)雜的操作進(jìn)行了隱藏,只顯示出一個一致的界面供客戶端使用。需要注意的是,外觀模式僅僅是給你提供了更為直接和容易的操作方式,它并沒有把原來的子系統(tǒng)進(jìn)行隔離,所以,如果你還需要子系統(tǒng)類的更高層的功能,還是可以使用原來的子系統(tǒng)的,這個是外觀模式的一大優(yōu)點。通過外觀模式可以將子系統(tǒng)的多個接口上建立一個高層接口,并且將這個高層接口提供給客戶端使用,這樣便可以解除掉客戶端和復(fù)雜子系統(tǒng)之間的耦合。
通過上圖可以看出,外觀模式實現(xiàn)提供簡單的接口(OpenMovie 和 CloseMovie)給客戶端,也給客戶端和子系統(tǒng)之間實現(xiàn)了解耦。下面通過代碼來實現(xiàn)上面的這個 Demo。
幾個播放工具的代碼:
using System; namespace Facade { /// <summary> /// 投影儀 /// </summary> public class Projector { public void OpenProjector() { Console.WriteLine("打開投影儀"); } 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> /// 功放機 /// </summary> public class Amplifier { public void OpenAmplifier() { Console.WriteLine("打開功放機"); } public void CloseAmplifier() { Console.WriteLine("關(guān)閉功放機"); } } } using System; namespace Facade { /// <summary> /// 屏幕 /// </summary> public class Screen { public void OpenScreen() { Console.WriteLine("打開屏幕"); } public void CloseScreen() { Console.WriteLine("關(guān)閉屏幕"); } } } using System; namespace Facade { /// <summary> /// DVD播放器 /// </summary> public class DVDPlayer { public void OpenDVDPlayer() { Console.WriteLine("打開 DVD 播放器"); } public void CloseDVDPlayer() { Console.WriteLine("關(guān)閉 DVD 播放器"); } } } using System; namespace Facade { /// <summary> /// 燈光 /// </summary> public class Light { public void OpenLight() { Console.WriteLine("打開燈光"); } public void CloseLight() { Console.WriteLine("關(guān)閉燈光"); } } }
外觀類中的代碼:
namespace Facade { /// <summary> /// 定義一個外觀 /// </summary> public class MovieFacade { /// <summary> /// 在外觀類中必須保存有子系統(tǒng)中各個對象 /// </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> /// 關(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)容,即外觀模式并沒有把子系統(tǒng)和客戶端隔離開來,只是提供了整潔的接口給客戶端,如果客戶端想訪問復(fù)雜子系統(tǒng)中的接口時還是一樣的可以訪問的。比如在上面的 Demo 中的設(shè)置了寬屏和普通等模式。
外觀模式的結(jié)構(gòu)總結(jié)
看完外觀模式的實現(xiàn)之后,為了幫助理清外觀模式中類之間的關(guān)系,下面給出上面實現(xiàn)代碼中類圖:
然而對于外觀模式而言,是沒有一個一般化的類圖描述,下面演示一個外觀模式的示意性對象圖來加深大家對外觀模式的理解:
在上面的對象圖中有兩個角色:
門面(Facade)角色:客戶端調(diào)用這個角色的方法。該角色知道相關(guān)的一個或多個子系統(tǒng)的功能和責(zé)任,該角色會將從客戶端發(fā)來的請求委派帶相應(yīng)的子系統(tǒng)中去。
子系統(tǒng)(subsystem)角色:可以同時包含一個或多個子系統(tǒng)。每個子系統(tǒng)都不是一個單獨的類,而是一個類的集合。每個子系統(tǒng)都可以被客戶端直接調(diào)用或被門面角色調(diào)用。對于子系統(tǒng)而言,門面僅僅是另外一個客戶端,子系統(tǒng)并不知道門面的存在。
外觀的優(yōu)缺點
優(yōu)點:
外觀模式對客戶屏蔽了子系統(tǒng)組件,從而簡化了接口,減少了客戶處理的對象數(shù)目并使子系統(tǒng)的使用更加簡單。
外觀模式實現(xiàn)了子系統(tǒng)與客戶之間的松耦合關(guān)系,而子系統(tǒng)內(nèi)部的功能組件是緊耦合的。松耦合使得子系統(tǒng)的組件變化不會影響到它的客戶。
缺點:
如果增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼,這樣就違背了”開——閉原則“(不過這點也是不可避免)。
使用場景
在以下情況下可以考慮使用外觀模式:
外一個復(fù)雜的子系統(tǒng)提供一個簡單的接口
提供子系統(tǒng)的獨立性
在層次化結(jié)構(gòu)中,可以使用外觀模式定義系統(tǒng)中每一層的入口。其中三層架構(gòu)就是這樣的一個例子。
總結(jié)
到這里外觀模式的介紹就結(jié)束了,外觀模式,為子系統(tǒng)的一組接口提供一個統(tǒng)一的接口,該模式定義了一個高層接口,這一個高層接口使的子系統(tǒng)更加容易使用。并且外觀模式可以解決層結(jié)構(gòu)分離、降低系統(tǒng)耦合度和為新舊系統(tǒng)交互提供接口功能。
相關(guān)文章
Winform實現(xiàn)鼠標(biāo)可穿透的窗體鏤空效果
這篇文章主要介紹了Winform實現(xiàn)鼠標(biāo)可穿透的窗體鏤空效果的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-10-10CPF?使用C#的Native?AOT?發(fā)布程序的詳細(xì)過程
這篇文章主要介紹了CPF?使用C#的Native?AOT?發(fā)布程序,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具體一定的參考借鑒價值,需要的朋友可以參考下2022-03-03c# SQLHelper(for winForm)實現(xiàn)代碼
數(shù)據(jù)連接池c# SQLHelper 實現(xiàn)代碼2009-02-02C# TabControl控件中TabPage選項卡切換時的觸發(fā)事件問題
這篇文章主要介紹了C# TabControl控件中TabPage選項卡切換時的觸發(fā)事件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04C# 使用鼠標(biāo)點擊對Chart控件實現(xiàn)數(shù)據(jù)提示效果
這篇文章主要介紹了C# 使用鼠標(biāo)點擊對Chart控件實現(xiàn)數(shù)據(jù)提示效果,文章給予上一篇的詳細(xì)內(nèi)容做延伸介紹,需要的小伙伴可任意參考一下2022-08-08