2013年12月13日 星期五

C# Design Pattern - Facade 表象模式

Facade Pattern是一個非常簡單的結構,主要是將眾多Class重新包裝成一個更好用的Class。
軟體的發展,Client程式經常會與複雜系統的內部子系統之間產生耦合,而導致客戶程式隨著子系統變化。
替子系統裡的許多介面定義一套統一的介面,讓子系統更方便使用。所以它的目的是簡化客戶程式與子系統之間的交互介面,將複雜系統的內部子系統與客戶程式之間的依賴解耦。




程式結構適用:

  • 為一個複雜子系統提供一個簡單介面。
  • 提高子系統的獨立性。
  • 在層次化結構中,可以使用Facade Pattern定義系統中每一層的入口。

以借貸的方式來序述Facade的程式結構:

建構一個介面

public class Mortgage
{
    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 class Bank
{
    public bool HasSufficientSavings(Customer c, int amount)
    {
        Console.WriteLine("確認銀行戶名: " + c.Name);
        return true;
    }

}

信用證子系統類別
public class Credit
{
    public bool HasGoodCredit(Customer c)
    {
        Console.WriteLine("確認信用戶名: " + c.Name);
        return true;
    }

}

建立貸款的子系統
public class Loan
{
    public bool HasNoBadLoans(Customer c)
    {
        Console.WriteLine("Check loans for " + c.Name);
        return true;
    }

}

顧客類別
public class Customer
{
    private string name;
    public Customer(string name)
    {
        this.name = name;
    }

    public string Name
    {
        get { return name; }
    }

}


實現模式
客戶程式類
public class MainApp
{
    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();
    }

}

Client程式只依賴Mortgage,讓Mortgage遮罩了子系統之間的複雜的操作,達到內部子系統與Client程式之間的依賴。

Façade Pattern與.Net的實現,分為四個邏輯層,UI、Business Facade,Business Rule,Data Access。






Business Facade運用在.NET的職責:

  • 從UI接收用戶輸入,請求需要對資料進行唯讀訪問,使用Data Access
  • 將請求傳遞到Business Rule
  • 將回應從Business Rule返回到UI,使其調用之間維護臨時狀態

UI將Request發送給Business Facade讓對請求進行初步的處理,判斷是否需要調用Business Rule,還是直接調用Data Access獲取資料。最後由Data Access訪問Database並按照來時的步驟返回結果到UI,來看具體的代碼實現。

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

public CategoryData GetCategories(int categoryId)
{
    ApplicationAssert.CheckCondition(categoryId >= 0, "Invalid Category Id", ApplicationAssert.LineNumber);
    using (Categories accessCategories = new Categories())
    {
        return accessCategories.GetCategories(categoryId);
    }
}

public void AddOrder()
{
    ApplicationAssert.CheckCondition(cartOrderData != null, "Order requires data", ApplicationAssert.LineNumber);
    ApplicationLog.WriteTrace("Duwamish7.Web.Cart.AddOrder:\r\nCustomerId: " +
        cartOrderData.Tables[OrderData.CUSTOMER_TABLE].Rows[0][OrderData.PKID_FIELD].ToString());
    cartOrderData = (new OrderSystem()).AddOrder(cartOrderData);
}

public OrderData AddOrder(OrderData order)
{
    ApplicationAssert.CheckCondition(order != null, "Order is required", ApplicationAssert.LineNumber);
    (new BusinessRules.Order()).InsertOrder(order);
    return order;
}

public bool InsertOrder(OrderData order)
{
    bool isValid = true;
    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;
    }

    isValid &= IsValidField(order, OrderData.SHIPPING_ADDRESS_TABLE, OrderData.SHIP_TO_NAME_FIELD, 40);
    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);
    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;
        }
    }

    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;

}

UI調用業務外觀層,業務外觀層直接調用了資料訪問層,Data Access確認了先決的條件,取得了資料。在添加訂單時,UI調用Business Facade,Business Facade調用Business Role,Business Role進行複雜的邏輯處理後,再調用Data Access。

Facade模式不僅簡化了整個元件系統的介面,對於元件內部與外部客戶程式達到了一種解耦的效果,內部子系統不會影響到Facade介面的變化。Facade更注重從架構的層次去看整個系統,而不是單個類的層次。

-雲遊山水為知已逍遙一生而忘齡- 電腦神手

沒有留言:

張貼留言