外观模式(Façade Pattern)
概述
在軟件開發系統中,客戶程序經常會與復雜系統的內部子系統之間產生耦合,而導致客戶程序隨著子系統的變化而變化。那么如何簡化客戶程序與子系統之間的交互接口?如何將復雜系統的內部子系統與客戶程序之間的依賴解耦?這就是要說的Fa?ade 模式。
意圖
為子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。[GOF 《設計模式》]
示意圖
門面模式沒有一個一般化的類圖描述,下面是一個示意性的對象圖:
圖1 Fa?ade模式示意性對象圖
生活中的例子
外觀模式為子系統中的接口定義了一個統一的更高層次的界面,以便于使用。當消費者按照目錄采購時,則體現了一個外觀模式。消費者撥打一個號碼與客服代表聯系,客服代表則扮演了這個"外觀",他包含了與訂貨部、收銀部和送貨部的接口。
圖2使用電話訂貨例子的外觀模式對象圖
Facade模式解說
我們平時的開發中其實已經不知不覺的在用Fa?ade模式,現在來考慮這樣一個抵押系統,當有一個客戶來時,有如下幾件事情需要確認:到銀行子系統查詢他是否有足夠多的存款,到信用子系統查詢他是否有良好的信用,到貸款子系統查詢他有無貸款劣跡。只有這三個子系統都通過時才可進行抵押。我們先不考慮Fa?ade模式,那么客戶程序就要直接訪問這些子系統,分別進行判斷。類結構圖下:
圖3
在這個程序中,我們首先要有一個顧客類,它是一個純數據類,并無任何操作,示意代碼:
?
?
//顧客類public?classCustomer
{
????private?string?_name;
????public?Customer(string?name)
????{
????????this._name?=?name;
????}
????public?string?Name
????{
????????get?{?return?_name;?}
????}
}
下面這三個類均是子系統類,示意代碼:
public?classBank
{
????public?bool?HasSufficientSavings(Customer?c,?int?amount)
????{
????????Console.WriteLine("Check?bank?for?"?+?c.Name);
????????return?true;
????}
}
//信用子系統
public?classCredit
{
????public?bool?HasGoodCredit(Customer?c)
????{
????????Console.WriteLine("Check?credit?for?"?+?c.Name);
????????return?true;
????}
}
//貸款子系統
public?classLoan
{
????public?bool?HasNoBadLoans(Customer?c)
????{
????????Console.WriteLine("Check?loans?for?"?+?c.Name);
????????return?true;
????}
}
來看客戶程序的調用:
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模式的情況下,客戶程序與三個子系統都發生了耦合,這種耦合使得客戶程序依賴于子系統,當子系統變化時,客戶程序也將面臨很多變化的挑戰。一個合情合理的設計就是為這些子系統創建一個統一的接口,這個接口簡化了客戶程序的判斷操作。看一下引入Fa?ade模式后的類結構圖:
圖4
門面類Mortage的實現如下:
?
//外觀類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;
????}
}
顧客類和子系統類的實現仍然如下:
?
//銀行子系統public?classBank
{
????public?bool?HasSufficientSavings(Customer?c,?int?amount)
????{
????????Console.WriteLine("Check?bank?for?"?+?c.Name);
????????return?true;
????}
}
//信用證子系統
public?classCredit
{
????public?bool?HasGoodCredit(Customer?c)
????{
????????Console.WriteLine("Check?credit?for?"?+?c.Name);
????????return?true;
????}
}
//貸款子系統
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;?}
????}
}
而此時客戶程序的實現:
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發生依賴,也就是Mortgage屏蔽了子系統之間的復雜的操作,達到了解耦內部子系統與客戶程序之間的依賴。
.NET架構中的Fa?ade模式
Fa?ade模式在實際開發中最多的運用當屬開發N層架構的應用程序了,一個典型的N層結構如下:
圖5
在這個架構中,總共分為四個邏輯層,分別為:用戶層UI,業務外觀層Business Fa?ade,業務規則層Business Rule,數據訪問層Data Access。其中Business Fa?ade層的職責如下:
l???????? 從“用戶”層接收用戶輸入
l???????? 如果請求需要對數據進行只讀訪問,則可能使用“數據訪問”層
l???????? 將請求傳遞到“業務規則”層
l???????? 將響應從“業務規則”層返回到“用戶”層
l???????? 在對“業務規則”層的調用之間維護臨時狀態
對這一架構最好的體現就是Duwamish示例了。在該應用程序中,有部分操作只是簡單的從數據庫根據條件提取數據,不需要經過任何處理,而直接將數據顯示到網頁上,比如查詢某類別的圖書列表。而另外一些操作,比如計算定單中圖書的總價并根據顧客的級別計算回扣等等,這部分往往有許多不同的功能的類,操作起來也比較復雜。如果采用傳統的三層結構,這些商業邏輯一般是會放在中間層,那么對內部的這些大量種類繁多,使用方法也各異的不同的類的調用任務,就完全落到了表示層。這樣勢必會增加表示層的代碼量,將表示層的任務復雜化,和表示層只負責接受用戶的輸入并返回結果的任務不太相稱,并增加了層與層之間的耦合程度。于是就引入了一個Fa?ade層,讓這個Facade來負責管理系統內部類的調用,并為表示層提供了一個單一而簡單的接口。看一下Duwamish結構圖:
圖6
從圖中可以看到,UI層將請求發送給業務外觀層,業務外觀層對請求進行初步的處理,判斷是否需要調用業務規則層,還是直接調用數據訪問層獲取數據。最后由數據訪問層訪問數據庫并按照來時的步驟返回結果到UI層,來看具體的代碼實現。
在獲取商品目錄的時候,Web UI調用業務外觀層:
?
?
productSystem?=?newProductSystem();categorySet???=productSystem.GetCategories(categoryID);
業務外觀層直接調用了數據訪問層:
{
????//
????//?Check?preconditions
????//
????ApplicationAssert.CheckCondition(categoryId?>=?0,"Invalid?Category?Id",ApplicationAssert.LineNumber);
????//
????//?Retrieve?the?data
????//
????using?(Categories?accessCategories?=?new?Categories())
????{
????????return?accessCategories.GetCategories(categoryId);
????}
????
}
在添加訂單時,UI調用業務外觀層:
{
????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);
}
業務外觀層調用業務規則層:
?
publicOrderData?AddOrder(OrderData?order){
????//
????//?Check?preconditions
????//
????ApplicationAssert.CheckCondition(order?!=?null,?"Order?is?required",?ApplicationAssert.LineNumber);
????
????(new?BusinessRules.Order()).InsertOrder(order);
????return?order;
}
業務規則層進行復雜的邏輯處理后,再調用數據訪問層:
{????
????//
????//?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]
效果及實現要點
1.Fa?ade模式對客戶屏蔽了子系統組件,因而減少了客戶處理的對象的數目并使得子系統使用起來更加方便。
2.Fa?ade模式實現了子系統與客戶之間的松耦合關系,而子系統內部的功能組件往往是緊耦合的。松耦合關系使得子系統的組件變化不會影響到它的客戶。
3.如果應用需要,它并不限制它們使用子系統類。因此你可以在系統易用性與通用性之間選擇。
適用性
1.為一個復雜子系統提供一個簡單接口。
2.提高子系統的獨立性。
3.在層次化結構中,可以使用Facade模式定義系統中每一層的入口。
總結
Fa?ade模式注重的是簡化接口,它更多的時候是從架構的層次去看整個系統,而并非單個類的層次。
轉載于:https://www.cnblogs.com/whitetiger/archive/2007/03/31/694814.html
總結
以上是生活随笔為你收集整理的外观模式(Façade Pattern)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WSUS专题之二:部署与规划1
- 下一篇: 生活的目标是什么