外观模式(Façade Pattern)
概述
在軟件開發(fā)系統(tǒng)中,客戶程序經(jīng)常會與復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)之間產(chǎn)生耦合,而導(dǎo)致客戶程序隨著子系統(tǒng)的變化而變化。那么如何簡化客戶程序與子系統(tǒng)之間的交互接口?如何將復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)與客戶程序之間的依賴解耦?這就是要說的Fa?ade 模式。
意圖
為子系統(tǒng)中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。[GOF 《設(shè)計模式》]
示意圖
門面模式?jīng)]有一個一般化的類圖描述,下面是一個示意性的對象圖:
圖1 Fa?ade模式示意性對象圖
生活中的例子
外觀模式為子系統(tǒng)中的接口定義了一個統(tǒng)一的更高層次的界面,以便于使用。當(dāng)消費(fèi)者按照目錄采購時,則體現(xiàn)了一個外觀模式。消費(fèi)者撥打一個號碼與客服代表聯(lián)系,客服代表則扮演了這個"外觀",他包含了與訂貨部、收銀部和送貨部的接口。
圖2使用電話訂貨例子的外觀模式對象圖
Facade模式解說
我們平時的開發(fā)中其實已經(jīng)不知不覺的在用Fa?ade模式,現(xiàn)在來考慮這樣一個抵押系統(tǒng),當(dāng)有一個客戶來時,有如下幾件事情需要確認(rèn):到銀行子系統(tǒng)查詢他是否有足夠多的存款,到信用子系統(tǒng)查詢他是否有良好的信用,到貸款子系統(tǒng)查詢他有無貸款劣跡。只有這三個子系統(tǒng)都通過時才可進(jìn)行抵押。我們先不考慮Fa?ade模式,那么客戶程序就要直接訪問這些子系統(tǒng),分別進(jìn)行判斷。類結(jié)構(gòu)圖下:
圖3
在這個程序中,我們首先要有一個顧客類,它是一個純數(shù)據(jù)類,并無任何操作,示意代碼:
?
?
//顧客類public?classCustomer
{
????private?string?_name;
????public?Customer(string?name)
????{
????????this._name?=?name;
????}
????public?string?Name
????{
????????get?{?return?_name;?}
????}
}
下面這三個類均是子系統(tǒng)類,示意代碼:
public?classBank
{
????public?bool?HasSufficientSavings(Customer?c,?int?amount)
????{
????????Console.WriteLine("Check?bank?for?"?+?c.Name);
????????return?true;
????}
}
//信用子系統(tǒng)
public?classCredit
{
????public?bool?HasGoodCredit(Customer?c)
????{
????????Console.WriteLine("Check?credit?for?"?+?c.Name);
????????return?true;
????}
}
//貸款子系統(tǒng)
public?classLoan
{
????public?bool?HasNoBadLoans(Customer?c)
????{
????????Console.WriteLine("Check?loans?for?"?+?c.Name);
????????return?true;
????}
}
來看客戶程序的調(diào)用:
public?classMainApp
{
????private?const?int?_amount?=?12000;
????public?static?void?Main()
????{
????????Bank?bank?=?new?Bank();
????????Loan?loan?=?new?Loan();
????????Credit?credit?=?new?Credit();
????????Customer?customer?=?new?Customer("Ann?McKinsey");
????????bool?eligible?=?true;
????????if?(!bank.HasSufficientSavings(customer,?_amount))
????????{
????????????eligible?=?false;
????????}
????????else?if?(!loan.HasNoBadLoans(customer))
????????{
????????????eligible?=?false;
????????}
????????else?if?(!credit.HasGoodCredit(customer))
????????{
????????????eligible?=?false;
????????}
????????Console.WriteLine("\n"?+?customer.Name?+?"?has?been?"?+?(eligible???"Approved"?:?"Rejected"));
????????Console.ReadLine();
????}
}
可以看到,在不用Fa?ade模式的情況下,客戶程序與三個子系統(tǒng)都發(fā)生了耦合,這種耦合使得客戶程序依賴于子系統(tǒng),當(dāng)子系統(tǒng)變化時,客戶程序也將面臨很多變化的挑戰(zhàn)。一個合情合理的設(shè)計就是為這些子系統(tǒng)創(chuàng)建一個統(tǒng)一的接口,這個接口簡化了客戶程序的判斷操作。看一下引入Fa?ade模式后的類結(jié)構(gòu)圖:
圖4
門面類Mortage的實現(xiàn)如下:
?
//外觀類public?classMortgage
{
????private?Bank?bank?=?new?Bank();
????private?Loan?loan?=?new?Loan();
????private?Credit?credit?=?new?Credit();
????public?bool?IsEligible(Customer?cust,?int?amount)
????{
????????Console.WriteLine("{0}?applies?for?{1:C}?loan\n",
??????????cust.Name,?amount);
????????bool?eligible?=?true;
????????if?(!bank.HasSufficientSavings(cust,?amount))
????????{
????????????eligible?=?false;
????????}
????????else?if?(!loan.HasNoBadLoans(cust))
????????{
????????????eligible?=?false;
????????}
????????else?if?(!credit.HasGoodCredit(cust))
????????{
????????????eligible?=?false;
????????}
????????return?eligible;
????}
}
顧客類和子系統(tǒng)類的實現(xiàn)仍然如下:
?
//銀行子系統(tǒng)public?classBank
{
????public?bool?HasSufficientSavings(Customer?c,?int?amount)
????{
????????Console.WriteLine("Check?bank?for?"?+?c.Name);
????????return?true;
????}
}
//信用證子系統(tǒng)
public?classCredit
{
????public?bool?HasGoodCredit(Customer?c)
????{
????????Console.WriteLine("Check?credit?for?"?+?c.Name);
????????return?true;
????}
}
//貸款子系統(tǒng)
public?classLoan
{
????public?bool?HasNoBadLoans(Customer?c)
????{
????????Console.WriteLine("Check?loans?for?"?+?c.Name);
????????return?true;
????}
}
//顧客類
public?classCustomer
{
????private?string?name;
????public?Customer(string?name)
????{
????????this.name?=?name;
????}
????public?string?Name
????{
????????get?{?return?name;?}
????}
}
而此時客戶程序的實現(xiàn):
public?classMainApp
{
????public?static?void?Main()
????{
????????//外觀
????????Mortgage?mortgage?=?new?Mortgage();
????????Customer?customer?=?new?Customer("Ann?McKinsey");
????????bool?eligable?=?mortgage.IsEligible(customer,?125000);
????????Console.WriteLine("\n"?+?customer.Name?+
????????????"?has?been?"?+?(eligable???"Approved"?:?"Rejected"));?
????????Console.ReadLine();
????}
}
可以看到引入Fa?ade模式后,客戶程序只與Mortgage發(fā)生依賴,也就是Mortgage屏蔽了子系統(tǒng)之間的復(fù)雜的操作,達(dá)到了解耦內(nèi)部子系統(tǒng)與客戶程序之間的依賴。
.NET架構(gòu)中的Fa?ade模式
Fa?ade模式在實際開發(fā)中最多的運(yùn)用當(dāng)屬開發(fā)N層架構(gòu)的應(yīng)用程序了,一個典型的N層結(jié)構(gòu)如下:
圖5
在這個架構(gòu)中,總共分為四個邏輯層,分別為:用戶層UI,業(yè)務(wù)外觀層Business Fa?ade,業(yè)務(wù)規(guī)則層Business Rule,數(shù)據(jù)訪問層Data Access。其中Business Fa?ade層的職責(zé)如下:
l???????? 從“用戶”層接收用戶輸入
l???????? 如果請求需要對數(shù)據(jù)進(jìn)行只讀訪問,則可能使用“數(shù)據(jù)訪問”層
l???????? 將請求傳遞到“業(yè)務(wù)規(guī)則”層
l???????? 將響應(yīng)從“業(yè)務(wù)規(guī)則”層返回到“用戶”層
l???????? 在對“業(yè)務(wù)規(guī)則”層的調(diào)用之間維護(hù)臨時狀態(tài)
對這一架構(gòu)最好的體現(xiàn)就是Duwamish示例了。在該應(yīng)用程序中,有部分操作只是簡單的從數(shù)據(jù)庫根據(jù)條件提取數(shù)據(jù),不需要經(jīng)過任何處理,而直接將數(shù)據(jù)顯示到網(wǎng)頁上,比如查詢某類別的圖書列表。而另外一些操作,比如計算定單中圖書的總價并根據(jù)顧客的級別計算回扣等等,這部分往往有許多不同的功能的類,操作起來也比較復(fù)雜。如果采用傳統(tǒng)的三層結(jié)構(gòu),這些商業(yè)邏輯一般是會放在中間層,那么對內(nèi)部的這些大量種類繁多,使用方法也各異的不同的類的調(diào)用任務(wù),就完全落到了表示層。這樣勢必會增加表示層的代碼量,將表示層的任務(wù)復(fù)雜化,和表示層只負(fù)責(zé)接受用戶的輸入并返回結(jié)果的任務(wù)不太相稱,并增加了層與層之間的耦合程度。于是就引入了一個Fa?ade層,讓這個Facade來負(fù)責(zé)管理系統(tǒng)內(nèi)部類的調(diào)用,并為表示層提供了一個單一而簡單的接口??匆幌翫uwamish結(jié)構(gòu)圖:
圖6
從圖中可以看到,UI層將請求發(fā)送給業(yè)務(wù)外觀層,業(yè)務(wù)外觀層對請求進(jìn)行初步的處理,判斷是否需要調(diào)用業(yè)務(wù)規(guī)則層,還是直接調(diào)用數(shù)據(jù)訪問層獲取數(shù)據(jù)。最后由數(shù)據(jù)訪問層訪問數(shù)據(jù)庫并按照來時的步驟返回結(jié)果到UI層,來看具體的代碼實現(xiàn)。
在獲取商品目錄的時候,Web UI調(diào)用業(yè)務(wù)外觀層:
?
?
productSystem?=?newProductSystem();categorySet???=productSystem.GetCategories(categoryID);
業(yè)務(wù)外觀層直接調(diào)用了數(shù)據(jù)訪問層:
{
????//
????//?Check?preconditions
????//
????ApplicationAssert.CheckCondition(categoryId?>=?0,"Invalid?Category?Id",ApplicationAssert.LineNumber);
????//
????//?Retrieve?the?data
????//
????using?(Categories?accessCategories?=?new?Categories())
????{
????????return?accessCategories.GetCategories(categoryId);
????}
????
}
在添加訂單時,UI調(diào)用業(yè)務(wù)外觀層:
{
????ApplicationAssert.CheckCondition(cartOrderData?!=?null,?"Order?requires?data",?ApplicationAssert.LineNumber);
????//Write?trace?log.
????ApplicationLog.WriteTrace("Duwamish7.Web.Cart.AddOrder:\r\nCustomerId:?"?+
????????????????????????????????cartOrderData.Tables[OrderData.CUSTOMER_TABLE].Rows[0][OrderData.PKID_FIELD].ToString());
????cartOrderData?=?(new?OrderSystem()).AddOrder(cartOrderData);
}
業(yè)務(wù)外觀層調(diào)用業(yè)務(wù)規(guī)則層:
?
publicOrderData?AddOrder(OrderData?order){
????//
????//?Check?preconditions
????//
????ApplicationAssert.CheckCondition(order?!=?null,?"Order?is?required",?ApplicationAssert.LineNumber);
????
????(new?BusinessRules.Order()).InsertOrder(order);
????return?order;
}
業(yè)務(wù)規(guī)則層進(jìn)行復(fù)雜的邏輯處理后,再調(diào)用數(shù)據(jù)訪問層:
{????
????//
????//?Assume?it's?good
????//
????bool?isValid?=?true;
????//????????????
????//?Validate?order?summary
????//
????DataRow?summaryRow?=?order.Tables[OrderData.ORDER_SUMMARY_TABLE].Rows[0];
????
????summaryRow.ClearErrors();
????if?(CalculateShipping(order)?!=?(Decimal)(summaryRow[OrderData.SHIPPING_HANDLING_FIELD]))
????{
????????summaryRow.SetColumnError(OrderData.SHIPPING_HANDLING_FIELD,?OrderData.INVALID_FIELD);
????????isValid?=?false;
????}
????if?(CalculateTax(order)?!=?(Decimal)(summaryRow[OrderData.TAX_FIELD]))
????{
????????summaryRow.SetColumnError(OrderData.TAX_FIELD,?OrderData.INVALID_FIELD);
????????isValid?=?false;
????}
????//????
????//?Validate?shipping?info
????//
????isValid?&=?IsValidField(order,?OrderData.SHIPPING_ADDRESS_TABLE,?OrderData.SHIP_TO_NAME_FIELD,?40);
????//
????//?Validate?payment?info?
????//
????DataRow?paymentRow?=?order.Tables[OrderData.PAYMENT_TABLE].Rows[0];
????
????paymentRow.ClearErrors();
????
????isValid?&=?IsValidField(paymentRow,?OrderData.CREDIT_CARD_TYPE_FIELD,?40);
????isValid?&=?IsValidField(paymentRow,?OrderData.CREDIT_CARD_NUMBER_FIELD,??32);
????isValid?&=?IsValidField(paymentRow,?OrderData.EXPIRATION_DATE_FIELD,?30);
????isValid?&=?IsValidField(paymentRow,?OrderData.NAME_ON_CARD_FIELD,?40);
????isValid?&=?IsValidField(paymentRow,?OrderData.BILLING_ADDRESS_FIELD,?255);
????//
????//?Validate?the?order?items?and?recalculate?the?subtotal
????//
????DataRowCollection?itemRows?=?order.Tables[OrderData.ORDER_ITEMS_TABLE].Rows;
????
????Decimal?subTotal?=?0;
????
????foreach?(DataRow?itemRow?in?itemRows)
????{
????????itemRow.ClearErrors();
????????
????????subTotal?+=?(Decimal)(itemRow[OrderData.EXTENDED_FIELD]);
????????
????????if?((Decimal)(itemRow[OrderData.PRICE_FIELD])?<=?0)
????????{
????????????itemRow.SetColumnError(OrderData.PRICE_FIELD,?OrderData.INVALID_FIELD);
????????????isValid?=?false;
????????}
????????if?((short)(itemRow[OrderData.QUANTITY_FIELD])?<=?0)
????????{
????????????itemRow.SetColumnError(OrderData.QUANTITY_FIELD,?OrderData.INVALID_FIELD);
????????????isValid?=?false;
????????}
????}
????//
????//?Verify?the?subtotal
????//
????if?(subTotal?!=?(Decimal)(summaryRow[OrderData.SUB_TOTAL_FIELD]))
????{
????????summaryRow.SetColumnError(OrderData.SUB_TOTAL_FIELD,?OrderData.INVALID_FIELD);
????????isValid?=?false;
????}
????if?(?isValid?)
????{
????????using?(DataAccess.Orders?ordersDataAccess?=?new?DataAccess.Orders())
????????{
????????????return?(ordersDataAccess.InsertOrderDetail(order))?>?0;
????????}
????}
????else
????????return?false;
}
[MSDN]
效果及實現(xiàn)要點(diǎn)
1.Fa?ade模式對客戶屏蔽了子系統(tǒng)組件,因而減少了客戶處理的對象的數(shù)目并使得子系統(tǒng)使用起來更加方便。
2.Fa?ade模式實現(xiàn)了子系統(tǒng)與客戶之間的松耦合關(guān)系,而子系統(tǒng)內(nèi)部的功能組件往往是緊耦合的。松耦合關(guān)系使得子系統(tǒng)的組件變化不會影響到它的客戶。
3.如果應(yīng)用需要,它并不限制它們使用子系統(tǒng)類。因此你可以在系統(tǒng)易用性與通用性之間選擇。
適用性
1.為一個復(fù)雜子系統(tǒng)提供一個簡單接口。
2.提高子系統(tǒng)的獨(dú)立性。
3.在層次化結(jié)構(gòu)中,可以使用Facade模式定義系統(tǒng)中每一層的入口。
總結(jié)
Fa?ade模式注重的是簡化接口,它更多的時候是從架構(gòu)的層次去看整個系統(tǒng),而并非單個類的層次。
轉(zhuǎn)載于:https://www.cnblogs.com/whitetiger/archive/2007/03/31/694814.html
總結(jié)
以上是生活随笔為你收集整理的外观模式(Façade Pattern)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WSUS专题之二:部署与规划1
- 下一篇: 生活的目标是什么