程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Enterprise Library深入解析與靈活應用(3):倘若將Unity、PIAB、Exception Ha

Enterprise Library深入解析與靈活應用(3):倘若將Unity、PIAB、Exception Ha

編輯:關於.NET

Enterprise Library深入解析與靈活應用(3):倘若將Unity、PIAB、Exception Handling引入MVP模式

最近在做一個Smart Client Software Factory的項目。熟悉SCSF或者CAB的都應該很清楚MVP這種設計模式。MVP是MVC的一種變體,View和 Mode分別關注於UI的呈現和業務模型,View和Mode完全分離,View通過Presenter實現對業務模型的訪問,Presenter“間接”地調 用View實現對UI的操作。對於MVP中的異常處理,我們是直接通過Enterprise Library的Exception Handling Application Block來實現的。具 體的做法是:在View中的每個控件的事件中添加try/catch block, 並在catch中通過ExceptionPolicy實現對異常的處理。這樣導致的問題是 ,相同的代碼重復散布於整個應用的各個角落,所以我又這樣的想法:通過Policy Injection以AOP的方式實現對異常的處理,當有了這個想法 後,我又多想了一步,何不再將Unity也一並整合進來。

一、MVP簡介

為了讓一些沒有接觸過MVP的讀者能夠理解後續的內容,我 先對MVP做一個簡單的介紹。如下圖所示:MVP有點類似於我們熟悉的MVC, View負責實現對UI的呈現已經與用戶進行交互,在CAB中,View一般 通過一個User Control來實現, Mode關注於具體的業務模型,獨立於View和Presenter。View具有一個Presenter的引用,當View需要調用Mode 的時候(比如需要訪問Mode傳入查詢條件獲取數據),通過Presenter訪問Mode。對於Presenter來說,它需要對View進行操作(比如數據成功 獲取後,將其顯示到View中),但是Presenter並不會“直接”對View本身進行引用,而是引用View的接口(IView),所以View對 象是一個不穩定的對象,而Presenter僅僅需要View中一些固定的操作,所以對將這些操作定義在IView interface中,將對View的依賴轉化成 對IView的依賴,這也充分體現了面向結構變成的原則。

二、模擬簡單的MVP

接下來我們通過一個簡單的場景來模擬MVP。這是我常用的計算器的例子,整體的構成如下圖所示:

1、ICalculator:Calculator的接口,定義 了一個用於進行除法運算的操作

namespace Artech.UnityInMVP
{
  public interface ICalculator
   {
    int Divide(int op1, int op2);
  }
}

2、Calculator:實現了ICalculator接口

namespace Artech.UnityInMVP
{
  public class Calculator:ICalculator
  {
    public  int Divide(int op1, int op2)
    {
      return op1 / op2;
    }
  }
}

3、CalculatePresenter:被View調用進行數學運算,並將運算結果顯示到View中

namespace Artech.UnityInMVP
{
   public class CalculatePresenter
  {
    public CalculatePresenter()
    {
      this.Calculator = new Calculator();
    }

    public ICalculateView View
    { get; set; }

    public ICalculator Calculator
    { get; set; }

    public void Calculate(int op1, int op2)
    {
      int result = this.Calculator.Divide(op1, op2);
       this.View.DisplayResult(result);
    }
  }
}

在CalculatePresenter具有一個ICalculateView 屬性,通過該屬性實現對於運算結果的顯示。之所以定義ICalculator 是想解除對具體的Calculator的依賴,但是到目前為止,這個目標還沒 有達到,因為在構造函數中還是依賴於Calculator。

4、ICalculateView :定義了一個用於顯示運算結果的操作,該操作被 CalculatePresenter調用

namespace Artech.UnityInMVP
{
  public interface ICalculateView
  {
    void DisplayResult(int result);
  }
}

5、CalculateView:在本例中是一個Form,並實現了 ICalculateView

namespace Artech.UnityInMVP
{
  public partial class CalculateView : ICalculateView
  {   

     public CalculatePresenter Presenter
    { get; set; }

    #region ICalculateView Members

    public void DisplayResult(int result)
    {
      this.textBoxResult.Text = result.ToString();
    }

    #endregioN

    private void buttonCalculate_Click(object sender, EventArgs e)
    {
      int op1;
      int op2;
      if(!int.TryParse (this.textBoxOp1.Text.Trim(), out op1))
      {
        return;
      }

        if(!int.TryParse(this.textBoxOp2.Text.Trim(), out op2))
      {
        return;
       }

       try
       {
         this.Presenter.Calculate(op1, op2);
        }
       catch (Exception ex)
       {
         if (ExceptionPolicy.HandleException (ex, "UI Exception Policy"))
         {
           throw;
         }
       }
    }

    private void CalculateView_Load(object sender, EventArgs e)
     {
      this.Presenter = new CalculatePresenter ();
      this.Presenter.View = this;
    }
  }
}

在Load的時候對Presenter屬性進行初始化, 並將View對象設置為View本身。在buttonCalculate_Click中,傳 入用戶輸入的操作數,並調用Presenter的Calculate方法。為了處理潛在的Exception,加了一個try/catch,並在catch中調用了Enterprise Library Excepton Handling Applicaion Block進行異常的處理。同時CalculateView 實現了ICalculateView的DisplayResult方法,將運算結 果顯示在TextBox中。

三、通過Unity和Policy Injection對上面的程序進行改造

我現在的目標是對上面的設計進行改進,達到 下述兩個目標:

通過AOP的方式進行異常的處理,相同的try/catch頻繁出現不是一個好的現象(實際上在我們現在的項目中,除了惡異 常處理,還有其他一些相識的非業務邏輯,我希望的是這些業務無關的邏輯都通過AOP實現)。

解除CalculatePresenter 對 Calculator的依賴,使其僅僅依賴於ICalculator。

我的思路是這樣的,將Policy Injection Application Block引入,用於實現 Exception Handling操作;將Unity引入通過Depedency Injection實現對CalculatePresenter 和Calculator的解耦;同時通過Unity Extension實現Policy Injection和Unity的集成(參見本系列第一章).

為此我們先對CalculatePresenter進行改造。

namespace Artech.UnityInMVP
{
  [ExceptionCallHandler("UI Exception Policy")]
   public class CalculatePresenter:MarshalByRefObject
  {
    public CalculatePresenter()
    {
       this.Calculator = new Calculator();
    }

    public ICalculateView View
    { get; set; }

    [Dependency]
    public ICalculator Calculator
    { get; set; }

    public void Calculate(int op1, int op2)
    {
      int result = this.Calculator.Divide(op1, op2);
      this.View.DisplayResult(result);
    }
  }
}

為了讓Policy Injection 能夠起作用,我讓其繼承MarshalByRefObject,並且以Custom Attribute的形式應用了ExceptionCallHandler,並制定exception handling policy(在真正的項目開發中,我推薦通過configuration的方式應用Policy injection)。

通過[Dependency]實現了基於Unity的 Property dependency.

然後我們接著對View進行改造,由於我們在CalculatePresenter使用了[Dependency]和 [ExceptionCallHandler],我們需要通過Unity Container的方式來創建CalculatePresenter對象,為此我定義了View的基類: ViewBase.

namespace Artech.UnityInMVP
{
  public partial class ViewBase : Form
  {   

    private IUnityContainer _unityContainer;

    protected IUnityContainer UnityContainer
     {
      get
      {
        if (this._unityContainer == null)
         {
          this._unityContainer = new UnityContainer();
          UnityConfigurationSection unityConfigSection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
           unityConfigSection.Containers.Default.Configure(this._unityContainer);
        }
        return this._unityContainer;
      }
    }
  }
}

在ViewBase 定義了IUnityContainer 屬性 ,用於View創建對應的Presenter對象,這樣CalculateView就可以這樣來定義了:

namespace Artech.UnityInMVP
{
  public partial class CalculateView : ViewBase, ICalculateView
  {
    public CalculateView()
     {
      InitializeComponent();          
    }

    public CalculatePresenter Presenter
    { get; set; }

    #region ICalculateView Members

    public void DisplayResult(int result)
    {
      this.textBoxResult.Text = result.ToString();
    }

    #endregioN

    private void buttonCalculate_Click(object sender, EventArgs e)
     {
      int op1;
      int op2;
      if(!int.TryParse(this.textBoxOp1.Text.Trim(), out op1))
      {
        return;
      }

       if(!int.TryParse (this.textBoxOp2.Text.Trim(), out op2))
      {
        return;
      }

        this.Presenter.Calculate(op1, op2);
    }

    private void CalculateView_Load(object sender, EventArgs e)
    {
      this.Presenter = this.UnityContainer.Resolve<CalculatePresenter>();
      this.Presenter.View = this;
    }
  }
}

在buttonCalculate_Click中,根本就不需要 try/catch了,在View初始化時,直接通過UnityContainer的Resolve方法創建Presenter。

我們最後來看看相關的配置:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
  <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
 </configSections>
  <exceptionHandling>
  <exceptionPolicies>
   <add name="UI Exception Policy">
    <exceptionTypes>
     <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
      postHandlingAction="None" name="Exception">
      <exceptionHandlers>
       <add type="Artech.UnityInMVP.ExceptionHandlers.MessageBoxHandler,Artech.UnityInMVP"
         name="Custom Handler" />
      </exceptionHandlers>
     </add>
     </exceptionTypes>
   </add>
  </exceptionPolicies>
 </exceptionHandling>
  <unity>
  <containers>
   <container>
    <types>
      <type type=" Artech.UnityInMVP.ICalculator,Artech.UnityInMVP" mapTo="Artech.UnityInMVP.Calculator,Artech.UnityInMVP"/>
    </types>
     <extensions>
     <add type="Artech.UnityInMVP.UnityExtensions.PolicyInjectionExtension,Artech.UnityInMVP" />
     </extensions>
   </container>
  </containers>
 </unity>
</configuration>

其中第一部分是exceptionHandling的配置,為了簡單起見,我創建了一個自定義的 ExceptionHandler:MessageBoxHandler來處理所有的exception,該handler僅僅將error message通過MessageBox顯示出來,有興趣的朋友可 以下載source code看看。---www.bianceng.cn

<exceptionHandling>
  <exceptionPolicies>
   <add name="UI Exception Policy">
    <exceptionTypes>
     <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
       postHandlingAction="None" name="Exception">
      <exceptionHandlers>
        <add type="Artech.UnityInMVP.ExceptionHandlers.MessageBoxHandler,Artech.UnityInMVP"
         name="Custom Handler" />
      </exceptionHandlers>
     </add>
     </exceptionTypes>
   </add>
  </exceptionPolicies>
  </exceptionHandling>

第二部分是unity的配置,在<types>中定義了ICalculator和Calculator的mapping關系, 實現了Presenter和Calculator的解耦;而extensions的配置實現了Policy Injection和Unity的集成,詳細實現可以查看本系列第一章。

<unity>
  <containers>
   <container>
    <types>
       <type type=" Artech.UnityInMVP.ICalculator,Artech.UnityInMVP" mapTo="Artech.UnityInMVP.Calculator,Artech.UnityInMVP"/>
    </types>
     <extensions>
     <add type="Artech.UnityInMVP.UnityExtensions.PolicyInjectionExtension,Artech.UnityInMVP" />
     </extensions>
   </container>
  </containers>
 </unity>

這就使所有 的實現。如何運算出現異常,比如將第二個操作數設為零,我們定義的MessageBoxHandler就會被執行,並通過MessageBox將Message顯示出來 ,就像這樣:

P.S. 雖然講Policy Injection應用到Presenter可以通過AOP的方式來進行異常的處理,但是這要求View上的所有具有潛在異常拋出的邏輯都需要通過Presenter 來實現,因為ExceptionHandler是應用到Presenter上面的。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved