柚子快報(bào)激活碼778899分享:C# 設(shè)計(jì)模式之適配器模式
柚子快報(bào)激活碼778899分享:C# 設(shè)計(jì)模式之適配器模式
總目錄
前言
在實(shí)際的開發(fā)過程中,由于需求的變化和擴(kuò)展,我們的代碼也需要做相應(yīng)的擴(kuò)展。想象這樣一個(gè)場(chǎng)景,原項(xiàng)目中接口返回的數(shù)據(jù)是XML格式的數(shù)據(jù),但現(xiàn)在來了一個(gè)新客戶,它期望接口返回的數(shù)據(jù)類型為json格式的。想要實(shí)現(xiàn)要么就是改原有接口,但這樣就違反了開閉原則,容易出現(xiàn)未知bug,影響到老客戶的正常使用。而如果寫一個(gè)適配器類也就是轉(zhuǎn)換類(第三方類),將原本返回的XML格式數(shù)據(jù)轉(zhuǎn)換成json格式數(shù)據(jù),而具體數(shù)據(jù)是怎么來的則直接用原有接口方法就可以,這就是適配器模式。
1 基本介紹
定義:將一個(gè)類的接口轉(zhuǎn)換成客戶端所期望的另一個(gè)接口,使得原本由于接口不兼容而無法協(xié)同工作的類能夠一起工作。適配器模式有類的適配器模式和對(duì)象的適配器模式兩種形式適配器模式(Apapter Pattern)是一種結(jié)構(gòu)型設(shè)計(jì)模式,用來解決現(xiàn)有對(duì)象與客戶端期待接口不一致的問題適配器模式中的角色:
目標(biāo)角色(Target):描述了其他類與客戶端代碼合作時(shí)必須遵循的協(xié)議。客戶角色(Client):與符合Target接口的對(duì)象協(xié)同。被適配(服務(wù)類,功能類)(Adaptee):定義一個(gè)已經(jīng)存在并已經(jīng)使用的接口,這個(gè)接口需要適配。 客戶端與其接口不兼容, 因此無法直接調(diào)用其功能。適配器(Adapter) :適配器模式的核心。適配器接受客戶端通過適配器接口發(fā)起的調(diào)用,同時(shí)根據(jù)其內(nèi)在邏輯調(diào)用對(duì)應(yīng)服務(wù)類??蛻舳舜a只需通過接口與適配器交互即可, 無需與具體的服務(wù)類耦合。
現(xiàn)實(shí)生活中空調(diào)插頭一般都是三頭的,但如果家里只有兩孔插座,那必然是插不進(jìn)去的。而如果提供一個(gè)擁有三孔插座和兩頭插頭的轉(zhuǎn)換器的話,那空調(diào)可以先插在這個(gè)轉(zhuǎn)換器上,然后這個(gè)轉(zhuǎn)換器再插在插座上就可以了。本質(zhì)并沒有變,只是將二孔插座包裝了一下,向外界提供了一個(gè)三孔插座的外觀以供客戶使用。 適配的本質(zhì)就是轉(zhuǎn)換,將不滿足使用條件的東西通過第三方類進(jìn)行加工處理成可使用的東西。
2 使用場(chǎng)景
系統(tǒng)需要復(fù)用現(xiàn)有類,但是接口又與復(fù)用環(huán)境要求不一致的情況。舊系統(tǒng)與新系統(tǒng)的兼容:可以使新系統(tǒng)能夠無縫地與老舊系統(tǒng)進(jìn)行通信。第三方組件的集成:適配器可以將第三方組件的接口轉(zhuǎn)換為符合我們系統(tǒng)需求的接口形式,從而能夠順利地集成到我們的系統(tǒng)中。多個(gè)類庫之間的互操作:適配器模式可以起到橋梁的作用。
3 實(shí)現(xiàn)方式
1. 類適配器
以常見的數(shù)據(jù)庫輔助接口為例
//數(shù)據(jù)庫輔助接口【目標(biāo)角色】
public interface IDbHelper
{
//負(fù)責(zé)執(zhí)行數(shù)據(jù)查詢 Query
void ExecDQL(string sql);
//負(fù)責(zé)執(zhí)行數(shù)據(jù)操作 Create,Delete,Update
void ExecDML(string sql);
}
// SqlServer的輔助類 實(shí)現(xiàn)
public class SqlServerHelper : IDbHelper
{
public void ExecDML(string sql)
{
Console.WriteLine($"SqlServerHelper執(zhí)行了【操作】sql:{sql}");
}
public void ExecDQL(string sql)
{
Console.WriteLine($"SqlServerHelper執(zhí)行了【查詢】sql:{sql}");
}
}
// SqlServer的輔助類 實(shí)現(xiàn)
public class MySqlHelper : IDbHelper
{
public void ExecDML(string sql)
{
Console.WriteLine($"MySqlHelper執(zhí)行了【操作】sql:{sql}");
}
public void ExecDQL(string sql)
{
Console.WriteLine($"MySqlHelper執(zhí)行了【查詢】sql:{sql}");
}
}
//業(yè)務(wù)擴(kuò)展了,關(guān)系型數(shù)據(jù)庫已經(jīng)不滿足于現(xiàn)在的業(yè)務(wù)了
//需要給系統(tǒng)增加緩存,用到了非關(guān)系型數(shù)據(jù)庫
//非關(guān)系數(shù)據(jù)庫輔助類 【被適配者】
public class NoSqlHelper
{
//非關(guān)系數(shù)據(jù)庫【特有的】操作數(shù)據(jù)方法
public void ExecNoSqlDML(string str)
{
Console.WriteLine($"NoSqlHelper執(zhí)行了【操作】:{str}");
}
//非關(guān)系數(shù)據(jù)庫【特有的】查詢數(shù)據(jù)方法
public void ExecNoSqlDQL(string str)
{
Console.WriteLine($"NoSqlHelper執(zhí)行了【查詢】:{str}");
}
}
//適配器類
public class NoSqlHelperAdapter : NoSqlHelper, IDbHelper
{
//實(shí)際上調(diào)用非關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)操作方法
public void ExecDML(string sql)
{
base.ExecNoSqlDML(sql);
}
public void ExecDQL(string sql)
{
base.ExecNoSqlDQL(sql);
}
}
客戶端調(diào)用
public static void Main(string[] args)
{
//客戶端可以通過適配器來使用IDbHelper這個(gè)數(shù)據(jù)庫輔助類
//因?yàn)橥ㄟ^NoSqlHelperAdapter 適配器類,已經(jīng)將其包裝成了IDbHelper
IDbHelper dbHelper = new NoSqlHelperAdapter();
dbHelper.ExecDML("...");
Console.ReadLine();
}
從實(shí)例中可以看出,類適配器主要是用繼承來實(shí)現(xiàn)的,但如果有很多個(gè)類進(jìn)行適配,這個(gè)方式就不支持了。
2. 對(duì)象適配器
相對(duì)于類適配器,這部分代碼不變
//數(shù)據(jù)庫輔助類
public interface IDbHelper
{
//負(fù)責(zé)執(zhí)行數(shù)據(jù)查詢 Query
void ExecDQL(string sql);
//負(fù)責(zé)執(zhí)行數(shù)據(jù)操作 Create,Delete,Update
void ExecDML(string sql);
}
// SqlServer的輔助類 實(shí)現(xiàn)
public class SqlServerHelper : IDbHelper
{
public void ExecDML(string sql)
{
Console.WriteLine($"SqlServerHelper執(zhí)行了【操作】sql:{sql}");
}
public void ExecDQL(string sql)
{
Console.WriteLine($"SqlServerHelper執(zhí)行了【查詢】sql:{sql}");
}
}
// SqlServer的輔助類 實(shí)現(xiàn)
public class MySqlHelper : IDbHelper
{
public void ExecDML(string sql)
{
Console.WriteLine($"MySqlHelper執(zhí)行了【操作】sql:{sql}");
}
public void ExecDQL(string sql)
{
Console.WriteLine($"MySqlHelper執(zhí)行了【查詢】sql:{sql}");
}
}
//業(yè)務(wù)擴(kuò)展了,關(guān)系型數(shù)據(jù)庫已經(jīng)不滿足于現(xiàn)在的業(yè)務(wù)了
//需要給系統(tǒng)增加緩存,用到了非關(guān)系型數(shù)據(jù)庫
//非關(guān)系數(shù)據(jù)庫輔助類
public class NoSqlHelper
{
//非關(guān)系數(shù)據(jù)庫操作數(shù)據(jù)
public void ExecNoSqlDML(string str)
{
Console.WriteLine($"NoSqlHelper執(zhí)行了【操作】:{str}");
}
//非關(guān)系數(shù)據(jù)庫查詢數(shù)據(jù)
public void ExecNoSqlDQL(string str)
{
Console.WriteLine($"NoSqlHelper執(zhí)行了【查詢】:{str}");
}
}
僅僅是適配類的代碼發(fā)生改變
//適配器
public class NoSqlHelperAdapter : IDbHelper
{
//引用非關(guān)系數(shù)據(jù)庫輔助類 的實(shí)例
public NoSqlHelper noSqlHelper = new NoSqlHelper();
public void ExecDML(string sql)
{
//通過實(shí)例調(diào)用相關(guān)數(shù)據(jù)操作的方法
noSqlHelper.ExecNoSqlDML(sql);
}
public void ExecDQL(string sql)
{
noSqlHelper.ExecNoSqlDQL(sql);
}
}
從實(shí)例中可以看出,對(duì)象適配器其實(shí)就是在適配器類中創(chuàng)建了一個(gè)被適配者的實(shí)例,從而將兩者聯(lián)系在一起。這種方式采用 “對(duì)象組合”的方式,更符合松耦合。
從兩個(gè)案例上知道,適配器模式并不是項(xiàng)目一開始就會(huì)用到的,而是隨著需求的變更和擴(kuò)展,我們不得已才開發(fā)一個(gè)適配器類,將新增的功能通過適配器類 “包一層” 的方式轉(zhuǎn)換為我們?cè)袑?duì)外提供的接口,使得我們可以在不修改原有代碼的基礎(chǔ)上來復(fù)用現(xiàn)有類,很好地符合 “開閉原則”
4 優(yōu)缺點(diǎn)分析
類的適配器模式:
優(yōu)點(diǎn):
可以在不修改原有代碼的基礎(chǔ)上來復(fù)用現(xiàn)有類,很好地符合 “開閉原則”可以重新定義Adaptee(被適配的類)的部分行為,因?yàn)樵陬愡m配器模式中,Adapter是Adaptee的子類僅僅引入一個(gè)對(duì)象,并不需要額外的字段來引用Adaptee實(shí)例(這個(gè)即是優(yōu)點(diǎn)也是缺點(diǎn))。 缺點(diǎn):
采用了 “多繼承”的實(shí)現(xiàn)方式,帶來了不良的高耦合。
對(duì)象的適配器模式:
優(yōu)點(diǎn):
可以在不修改原有代碼的基礎(chǔ)上來復(fù)用現(xiàn)有類,很好地符合 “開閉原則”(這點(diǎn)是兩種實(shí)現(xiàn)方式都具有的)采用 “對(duì)象組合”的方式,更符合松耦合。 缺點(diǎn):
使得重定義Adaptee(被適配的類)的行為較困難,這就需要生成Adaptee的子類并且使得Adapter引用這個(gè)子類而不是引用Adaptee本身。
結(jié)語
希望以上內(nèi)容可以幫助到大家,如文中有不對(duì)之處,還請(qǐng)批評(píng)指正。
參考資料: c#中適配器模式詳解 C#設(shè)計(jì)模式(7)-適配器模式
柚子快報(bào)激活碼778899分享:C# 設(shè)計(jì)模式之適配器模式
精彩文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。