Facade(外觀)模式

最近在看設計模式相關的書,感覺這篇文章對Facade模式的分析甚至比書上還好,說以就轉了這篇文章,不過這篇文章有些地方把Facade模式寫成了faade模式,大家看時注意一下。

動機(Motivate):
    在軟件開發系統中,客戶程序經常會與複雜系統的內部子系統之間產生耦合,而導致客戶程序隨着子系統的變化而變化。那麼如何簡化客戶程序與子系統之間的交互接口?如何將複雜系統的內部子系統與客戶程序之間的依賴解耦?
意圖(Intent):
   
爲子系統中的一組接口提供一個一致的界面, Facade 模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
                                                                         --------《設計模式》GOF
結構圖(Struct):
               
               
           
適用性:

    1 .爲一個複雜子系統提供一個簡單接口。

    2 .提高子系統的獨立性。

    3 .在層次化結構中,可以使用 Facade 模式定義系統中每一層的入口。
生活中的例子:
               
代碼實現:
   
我們平時的開發中其實已經不知不覺的在用 Faade 模式,現在來考慮這樣一個抵押系統,當有一個客戶來時,有如下幾件事情需要確認:到銀行子系統查詢他是否有足夠多的存款,到信用子系統查詢他是否有良好的信用,到貸款子系統查詢他有無貸款劣跡。只有這三個子系統都通過時纔可進行抵押。我們先不考慮 Faade 模式,那麼客戶程序就要直接訪問這些子系統,分別進行判斷。類結構圖下:
                
   
在這個程序中,我們首先要有一個顧客類,它是一個純數據類,並無任何操作,示意代碼:

 1  // 顧客類
 2  public  class  Customer
 3  {
 4       private  string  _name;
 5  
 6       public  Customer( string  name)
 7      {
 8           this ._name  =  name;
 9      }
10  
11       public  string  Name
12      {
13           get  {  return  _name; }
14      }
15  }


下面這三個類均是子系統類,示意代碼:

 1  // 銀行子系統
 2  public  class  Bank
 3  {
 4       public  bool  HasSufficientSavings(Customer c,  int  amount)
 5      {
 6          Console.WriteLine( " Check bank for  "  +  c.Name);
 7           return  true ;
 8      }
 9  }
10  
11  // 信用子系統
12  public  class  Credit
13  {
14       public  bool  HasGoodCredit(Customer c)
15      {
16          Console.WriteLine( " Check credit for  "  +  c.Name);
17           return  true ;
18      }
19  }
20  
21  // 貸款子系統
22  public  class  Loan
23  {
24       public  bool  HasNoBadLoans(Customer c)
25      {
26          Console.WriteLine( " Check loans for  "  +  c.Name);
27           return  true ;
28      }
29  }


看客戶程序的調用:

 1  // 客戶程序
 2  public  class  MainApp
 3  {
 4       private  const  int  _amount  =  12000 ;
 5  
 6       public  static  void  Main()
 7      {
 8          Bank bank  =  new  Bank();
 9          Loan loan  =  new  Loan();
10          Credit credit  =  new  Credit();
11  
12          Customer customer  =  new  Customer( " Ann McKinsey " );
13  
14           bool  eligible  =  true ;
15  
16           if  ( ! bank.HasSufficientSavings(customer, _amount))
17          {
18              eligible  =  false ;
19          }
20           else  if  ( ! loan.HasNoBadLoans(customer))
21          {
22              eligible  =  false ;
23          }
24           else  if  ( ! credit.HasGoodCredit(customer))
25          {
26              eligible  =  false ;
27          }
28  
29          Console.WriteLine( " /n "  +  customer.Name  +  "  has been  "  +  (eligible  ?  " Approved "  :  " Rejected " ));
30          Console.ReadLine();
31      }
32  }


可以看到,在不用Faade 模式的情況下,客戶程序與三個子系統都發生了耦合,這種耦合使得客戶程序依賴於子系統,當子系統 化時,客戶程序也將面臨很多變化的挑戰。一個合情合理的設計就是爲這些子系統創建一個統一的接口,這個接口簡化了客戶程序的判斷操作。看一下引入 Faade 模式後的類結構圖:
          

外觀類 Mortage 的實現如下:

 1  / 外觀類
 2  public  class  Mortgage
 3  {
 4       private  Bank bank  =  new  Bank();
 5       private  Loan loan  =  new  Loan();
 6       private  Credit credit  =  new  Credit();
 7  
 8       public  bool  IsEligible(Customer cust,  int  amount)
 9      {
10          Console.WriteLine( " {0} applies for {1:C} loan/n " ,
11            cust.Name, amount);
12  
13           bool  eligible  =  true ;
14  
15           if  ( ! bank.HasSufficientSavings(cust, amount))
16          {
17              eligible  =  false ;
18          }
19           else  if  ( ! loan.HasNoBadLoans(cust))
20          {
21              eligible  =  false ;
22          }
23           else  if  ( ! credit.HasGoodCredit(cust))
24          {
25              eligible  =  false ;
26          }
27  
28           return  eligible;
29      }
30  }

顧客類和子系統類的實現仍然如下:

 1  // 銀行子系統
 2  public  class  Bank
 3  {
 4       public  bool  HasSufficientSavings(Customer c,  int  amount)
 5      {
 6          Console.WriteLine( " Check bank for  "  +  c.Name);
 7           return  true ;
 8      }
 9  }
10  
11  // 信用證子系統
12  public  class  Credit
13  {
14       public  bool  HasGoodCredit(Customer c)
15      {
16          Console.WriteLine( " Check credit for  "  +  c.Name);
17           return  true ;
18      }
19  }
20  
21  // 貸款子系統
22  public  class  Loan
23  {
24       public  bool  HasNoBadLoans(Customer c)
25      {
26          Console.WriteLine( " Check loans for  "  +  c.Name);
27           return  true ;
28      }
29  }
30  
31  // 顧客類
32  public  class  Customer
33  {
34       private  string  name;
35  
36       public  Customer( string  name)
37      {
38           this .name  =  name;
39      }
40  
41       public  string  Name
42      {
43           get  {  return  name; }
44      }
45  }

而此時客戶程序的實現:

 1  // 客戶程序類
 2  public  class  MainApp
 3  {
 4       public  static  void  Main()
 5      {
 6           // 外觀
 7          Mortgage mortgage  =  new  Mortgage();
 8  
 9          Customer customer  =  new  Customer( " Ann McKinsey " );
10           bool  eligable  =  mortgage.IsEligible(customer,  125000 );
11  
12          Console.WriteLine( " /n "  +  customer.Name  +
13               "  has been  "  +  (eligable  ?  " Approved "  :  " Rejected " )); 
14          Console.ReadLine();
15      }
16  }

可以看到引入Faade 模式後,客戶程序只與 Mortgage 發生依賴,也就是 Mortgage 屏蔽了子系統之間的複雜的操作,達到了解耦內部子系統與客戶程序之間的依賴。

.NET 架構中的 Faade 模式

 

Faade 模式在實際開發中最多的運用當屬開發 N 層架構的應用程序了,一個典型的 N 層結構如下:

              


    在這個架構中,總共分爲四個邏輯層,分別爲:用戶層 UI ,業務外觀層 Business Faade ,業務規則層 Business Rule ,數據訪問層 Data Access 。其中 Business Faade 層的職責如下:

l         從“用戶”層接收用戶輸入

l         如果請求需要對數據進行只讀訪問,則可能使用“數據訪問”層

l         將請求傳遞到“業務規則”層

l         將響應從“業務規則”層返回到“用戶”層

l         在對“業務規則”層的調用之間維護臨時狀態

對這一架構最好的體現就是 Duwamish 示例了。在該應用程序中,有部分操作只是簡單的從數據庫根據條件提取數據,不需要經過任何處理,而直接將數據顯示到網頁上,比如查詢某類別的圖書列表。而另外一些操作,比如計算定單中圖書的總價並根據顧客的級別計算回扣等等,這部分往往有許多不同的功能的類,操作起來也比較複雜。如果採用傳統的三層結構,這些商業邏輯一般是會放在中間層,那麼對內部的這些大量種類繁多,使用方法也各異的不同的類的調用任務,就完全落到了表示層。這樣勢必會增加表示層的代碼量,將表示層的任務複雜化,和表示層只負責接受用戶的輸入並返回結果的任務不太相稱,並增加了層與層之間的耦合程度。於是就引入了一個 Faade 層,讓這個 Facade 來負責管理系統內部類的調用,併爲表示層提供了一個單一 而簡單的接口。看一下Duwamish結構圖:                                                                       

從圖中可以看到,UI層 將請求發送給業務外觀層,業務外觀層對請求進行初步的處理,判斷是否需要調用業務規則層,還是直接調用數據訪問層獲取數據。最後由數據訪問層訪問數據庫並按 照來時的步驟返回結果到 UI 層,來看具體的代碼實現。

在獲取商品目錄的時候, Web UI 調用業務外觀層:

1  productSystem  =  new  ProductSystem();
2  categorySet    =  productSystem.GetCategories(categoryID);


業務外觀層直接調用了數據訪問層:

 1  public  CategoryData GetCategories( int  categoryId)
 2  {
 3       //
 4       //  Check preconditions
 5       //
 6      ApplicationAssert.CheckCondition(categoryId  >=  0 , " Invalid Category Id " ,ApplicationAssert.LineNumber);
 7       //
 8       //  Retrieve the data
 9       //
10       using  (Categories accessCategories  =  new  Categories())
11      {
12           return  accessCategories.GetCategories(categoryId);
13      }
14      
15  }


在添加訂單時,UI調用業務外觀層:

1  public  void  AddOrder()
2  {
3      ApplicationAssert.CheckCondition(cartOrderData  !=  null " Order requires data " , ApplicationAssert.LineNumber);
4  
5       // Write trace log.
6      ApplicationLog.WriteTrace( " Duwamish7.Web.Cart.AddOrder:/r/nCustomerId:  "  +
7                                  cartOrderData.Tables[OrderData.CUSTOMER_TABLE].Rows[ 0 ][OrderData.PKID_FIELD].ToString());
8      cartOrderData  =  ( new  OrderSystem()).AddOrder(cartOrderData);
9  }


業務外觀層調用業務規則層:

 1  public  OrderData AddOrder(OrderData order)
 2  {
 3       //
 4       //  Check preconditions
 5       //
 6      ApplicationAssert.CheckCondition(order  !=  null " Order is required " , ApplicationAssert.LineNumber);
 7      
 8      ( new  Busine***ules.Order()).InsertOrder(order);
 9       return  order;
10  }

 


業務規則層進行復雜的邏輯處理後,再調用數據訪問層:

 1  public  OrderData AddOrder(OrderData order)
 2  {
 3       //
 4       //  Check preconditions
 5       //
 6      ApplicationAssert.CheckCondition(order  !=  null " Order is required " , ApplicationAssert.LineNumber);
 7      
 8      ( new  Busine***ules.Order()).InsertOrder(order);
 9       return  order;
10  }
11  
12  
13  業務規則層進行復雜的邏輯處理後,再調用數據訪問層:
14  public  bool  InsertOrder(OrderData order)
15  {    
16       //
17       //  Assume it's good
18       //
19       bool  isValid  =  true ;
20       //             
21       //  Validate order summary
22       //
23      DataRow summaryRow  =  order.Tables[OrderData.ORDER_SUMMARY_TABLE].Rows[ 0 ];
24      
25      summaryRow.ClearErrors();
26  
27       if  (CalculateShipping(order)  !=  (Decimal)(summaryRow[OrderData.SHIPPING_HANDLING_FIELD]))
28      {
29          summaryRow.SetColumnError(OrderData.SHIPPING_HANDLING_FIELD, OrderData.INVALID_FIELD);
30          isValid  =  false ;
31      }
32  
33       if  (CalculateTax(order)  !=  (Decimal)(summaryRow[OrderData.TAX_FIELD]))
34      {
35          summaryRow.SetColumnError(OrderData.TAX_FIELD, OrderData.INVALID_FIELD);
36          isValid  =  false ;
37      }
38       //     
39       //  Validate shipping info
40       //
41      isValid  &=  IsValidField(order, OrderData.SHIPPING_ADDRESS_TABLE, OrderData.SHIP_TO_NAME_FIELD,  40 );
42       //
43       //  Validate payment info 
44       //
45      DataRow paymentRow  =  order.Tables[OrderData.PAYMENT_TABLE].Rows[ 0 ];
46      
47      paymentRow.ClearErrors();
48      
49      isValid  &=  IsValidField(paymentRow, OrderData.CREDIT_CARD_TYPE_FIELD,  40 );
50      isValid  &=  IsValidField(paymentRow, OrderData.CREDIT_CARD_NUMBER_FIELD,   32 );
51      isValid  &=  IsValidField(paymentRow, OrderData.EXPIRATION_DATE_FIELD,  30 );
52      isValid  &=  IsValidField(paymentRow, OrderData.NAME_ON_CARD_FIELD,  40 );
53      isValid  &=  IsValidField(paymentRow, OrderData.BILLING_ADDRESS_FIELD,  255 );
54       //
55       //  Validate the order items and recalculate the subtotal
56       //
57      DataRowCollection itemRows  =  order.Tables[OrderData.ORDER_ITEMS_TABLE].Rows;
58      
59      Decimal subTotal  =  0 ;
60      
61       foreach  (DataRow itemRow  in  itemRows)
62      {
63          itemRow.ClearErrors();
64          
65          subTotal  +=  (Decimal)(itemRow[OrderData.EXTENDED_FIELD]);
66          
67           if  ((Decimal)(itemRow[OrderData.PRICE_FIELD])  <=  0 )
68          {
69              itemRow.SetColumnError(OrderData.PRICE_FIELD, OrderData.INVALID_FIELD);
70              isValid  =  false ;
71          }
72  
73           if  (( short )(itemRow[OrderData.QUANTITY_FIELD])  <=  0 )
74          {
75              itemRow.SetColumnError(OrderData.QUANTITY_FIELD, OrderData.INVALID_FIELD);
76              isValid  =  false ;
77          }
78      }
79       //
80       //  Verify the subtotal
81       //
82       if  (subTotal  !=  (Decimal)(summaryRow[OrderData.SUB_TOTAL_FIELD]))
83      {
84          summaryRow.SetColumnError(OrderData.SUB_TOTAL_FIELD, OrderData.INVALID_FIELD);
85          isValid  =  false ;
86      }
87  
88       if  ( isValid )
89      {
90           using  (DataAccess.Orders ordersDataAccess  =  new  DataAccess.Orders())
91          {
92               return  (ordersDataAccess.InsertOrderDetail(order))  >  0 ;
93          }
94      }
95       else
96           return  false ;
97  }


Facade模式的個要點:
    從客戶程序的角度來看,Facade模式不僅簡化了整個組件系統的接口,同時對於組件內部與外部客戶程序來說,從某種程度上也達到了一種“解耦”的效果----內部子系統的任何變化不會影響到Facade接口的變化。

    Facade設計模式更注重從架構的層次去看整個系統,而不是單個類的層次。Facdae很多時候更是一種架構
設計模式。
    注意區分Facade模式、Adapter模式、Bridge模式與Decorator模式。Facade模式注重簡化接口,Adapter模式注重轉換接口,Bridge模式注重分離接口(抽象)與其實現,Decorator模式注重穩定接口的前提下爲對象擴展功能。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章