日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

适配器模式(Adapter)和外观模式(Facade)

發布時間:2025/6/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 适配器模式(Adapter)和外观模式(Facade) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

適配器模式(Adapter)

還是先從引入說起,先來看一個問題吧,總所周知,在中國通用的電壓時 220V,

而美國電壓則是 110V,如果有經常在美國和中國之間跑的 IT 人,而其筆記本都是隨身攜帶的,

那么它的筆記本的電壓問題如何解決呢?

(因為在美國和中國電壓不同,所以一般的電器會不通用的)

而適配器在這個問題上體現得妙極妙極。

現在的筆記本都有一個電源適配器,而正是這個電源適配器來解決上面提到的適配器問題,

比如,一款索尼筆記本,其輸入電流為交流100V~240V,而輸出則是統一的直流 19.5V,

在電源適配器的一端輸入交流電流,然后通過電源適配器把電源變成需要的電壓,

也就是適配器的作用是使得一個東西適合另外一個東西。

?????????????

??????????????

下面來給出適配器模式的定義

適配器模式將一個接口轉換成另外一個接口,以符合客戶的期望。

主要用于以下情況:

比如現在我有一個舊的軟件系統,而其中有一個組件呢,它已經過時了,需要更新,

所以我又有了第三方的組件(新組件),但是舊組件的接口和新組件的接口不同,

同時,您又不想去改變現有的代碼(如果系統大的話,或許您改都改不了),

此時呢,就是讓適配器模式登場的時刻了,

您可以通過適配器模式將新組件的一些接口轉換成為你所期望的接口(也就是和新組件符合),

這樣的話,您就無需要改變原來的代碼而輕松實現從舊組件更新到新組件了。

然后呢,還有一種比較好的應用就是,比如現在很多在 Windows 上的東西都不能在 Linux 上運行,

比如一個 WINE 的工具,它呢,就允許用戶在 Linux 環境下運行 Windows 程序,這也是一種適配器。

其實呢,可以這樣來理解適配器模式的,適配器模式就是將一些對象包裝起來,

然后讓它們的接口看起來是別的接口。

還有需要提及的是:

適配器模式本來是分為了類適配器模式和對象適配器模式,但是由于類適配器模式要以多重繼承為前提,

而 C# 呢不支持多重繼承,

所以在這里只介紹對象適配器,如果有對類適配器模式感興趣的話,可以使用 C++ 來實現一下。

????????????

???????????

下面給出對象適配器的結構圖

??????????

?????????????

下面就來看一個簡單的 Demo 來說明適配器的具體作用

Demo 大概情況是這樣的,我需要使用一個新的組件來替換掉我系統中已經過時了的組件,

所以我使用了一個第三方組件,

而這個組件當中的接口的名字居然都是用中文寫的(這個可能是開發這個組件的程序員的問題所造成的),

而我現有的系統中的的舊有的組件的接口中確是通過英文來調用的組件,而由于系統過于復雜,

所以難以更改,所以我便選擇了使用適配器模式來解決這個問題:

看一下類圖吧

先來看 Target 類(Target 類代表能夠被客戶端使用的接口)

namespace Adapter
{
??? public abstract class Target
??? {
??????? //溫度
??????? /// <summary>
??????? /// 下面的接口才是可以被客戶端所識別的接口,也就是目標接口
??????? /// 而前面在被適配器類中的中文卻不能被客戶端識別,需要被適配
??????? /// </summary>
??????? public abstract void GetTemperature();

??????? //氣壓
??????? public abstract void GetPressure();

??????? //濕度
??????? public abstract void GetHumidity();

??????? //紫外線強度
??????? public abstract void GetUltraviolet();
??? }
}

再來看需要被適配的類 Adaptee(Adaptee 中的接口由于不能被客戶端識別,所以需要被適配)

using System;

namespace Adapter
{
??? class Adaptee
??? {
??????? /// <summary>
??????? /// 在被適配器類中的接口并不是客戶端需要的接口
??????? /// 比如這里是使用的中文,而我在客戶端卻必須要使用英文
??????? /// 所以在這里我必須使用適配器來適配
??????? /// </summary>
??????? public void 得到溫度()
??????? {
??????????? Console.WriteLine("您得到了今日的溫度");
??????? }

??????? public void 得到氣壓()
??????? {
??????????? Console.WriteLine("您得到了今日的氣壓");
??????? }

??????? public void 得到濕度()
??????? {
??????????? Console.WriteLine("您得到了今日的濕度");
??????? }

??????? public void 得到紫外線強度()
??????? {
??????????? Console.WriteLine("您得到了今日的紫外線強度");
??????? }
??? }
}

然后就要看適配器中的代碼部分了(適配器將不能被客戶端識別的接口間接轉換為可以被識別的接口)

namespace Adapter
{
??? public class Adapter:Target
??? {
??????? //在適配器中必須要維護一個被適配器類的對象
??????? private Adaptee adaptee = new Adaptee();

???? ?? /// <summary>
??????? /// 通過適配器來適配原來不能被客戶端所認識的接口
??????? /// </summary>
??????? public override void GetTemperature()
??????? {
??????????? adaptee.得到溫度();
??????? }

??????? public override void GetPressure()
??????? {
??????????? adaptee.得到氣壓();
??????? }

??????? public override void GetHumidity()
??????? {
??????????? adaptee.得到濕度();
??????? }

??????? public override void GetUltraviolet()
??????? {
??????????? adaptee.得到紫外線強度();
??????? }
??? }
}

最后再來看客戶端就 OK 了

using System;

namespace AdapterTest
{
??? class Program
??? {
??????? static void Main(string[] args)
??????? {
???????? ?? //實例化一個適配器給目標接口
??????????? Adapter.Target target = new Adapter.Adapter();

??????????? //下面的這些就是客戶端可以被識別了接口了

??????????? target.GetTemperature();
??????????? target.GetPressure();
??????????? target.GetHumidity();
??????????? target.GetUltraviolet();

??????????? Console.ReadKey();
??????? }
??? }
}

效果如下

????????

????????????

可以看出,上面的適配器就是一個中介人,它把本來客戶端的請求轉換成了 Adaptee 所代表的接口所能理解的請求。

或者說是,把本來客戶端不認識的 Adaptee 間接介紹給了客戶端認識,不過注意是間接。

下面還來總結一下客戶端使用適配器的過程:

首先是:客戶通過目標接口調用適配器的方法,并且對適配器發出請求。

然后呢:適配器使用被適配器接口把請求轉換成了被適配器者的一個或多個調用接口。

最后呢:客戶端接收到調用的結果,但是客戶端并不會知道請求的處理者是被適配者(客戶端和被適配者是完全解耦的),

???????????? 其也不知道適配器在這中間起到的作用。

???????

同時也可以總結一下適配器模式的適用性:

如果你想使用一個已經存在了的接口,而這個接口卻不符合你的需求,此時就可以考慮使用適配器模式。

適配器模式在 . NET 中的應用

這里要介紹的就是 DataAdapter

使用過 ADO.NET 等數據訪問之類的操作的話,應該都使用過 DataAdapter

DataAdapter 的主要作用是用來在 DataSet 和數據源之間提供一個適配器功能來實現檢索和保存數據。

因為數據源有可能是 DB2 啊,SqlServer 啊,Oracle 啊等等,而這些數據庫的數據在組織上都有一定的差別,

所以,它們的接口單單對于 DataSet 來說的話,是有區別的,而且區別還比較大,

而我們總不可能針對每一種數據庫都使用不同的 DataSet 來保存和檢索數據吧,我們希望的是提供一種統一的 DataSet ,

而其既可以對 Oracle 使用,又可以對 DB2 等等數據庫使用,也就是一種通用的 DataSet 類型。

所以在這里面便可以使用適配器模式了,

我們在數據源和 DataSet 之間插入一個適配器 DataAdapter ,通過適配器來實現對各種數據庫的不同應用(提供給客戶端統一接口),

而在 DataSet 中,其只能看到 DataAdapter 這一層,對于數據源的話,它是不需要過問的,

這樣也就是實現了一種通用的 DataSet。

?????????????

????????????

?????????????

外觀模式(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 中也可以清晰地看出,

外觀模式可以提供一個簡潔的外觀接口來實現將一個復雜的子系統變得容易使用。

同時,在客戶端還是可以訪問原來子系統中的復雜的接口的。

好了,外觀模式的介紹就到這里了。

???????????

??????????

?????????????

下面將要介紹的是裝飾者模式,適配器模式,外觀模式三者之間的區別:

裝飾者模式的話,它并不會改變接口,而是將一個一個的接口進行裝飾,也就是添加新的功能。

適配器模式是將一個接口通過適配來間接轉換為另一個接口。

外觀模式的話,其主要是提供一個整潔的一致的接口給客戶端。

總結

以上是生活随笔為你收集整理的适配器模式(Adapter)和外观模式(Facade)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。