Enterprise Library深入解析与灵活应用(3):倘若将Unity、PIAB、Exception Handling引入MVP模式.. .. .....
最近在做一個(gè)Smart Client Software Factory的項(xiàng)目。熟悉SCSF或者CAB的都應(yīng)該很清楚MVP這種設(shè)計(jì)模式。MVP是MVC的一種變體,View和Mode分別關(guān)注于UI的呈現(xiàn)和業(yè)務(wù)模型,View和Mode完全分離,View通過Presenter實(shí)現(xiàn)對(duì)業(yè)務(wù)模型的訪問,Presenter“間接”地調(diào)用View實(shí)現(xiàn)對(duì)UI的操作。對(duì)于MVP中的異常處理,我們是直接通過Enterprise Library的Exception Handling Application Block來(lái)實(shí)現(xiàn)的。具體的做法是:在View中的每個(gè)控件的事件中添加try/catch block, 并在catch中通過ExceptionPolicy實(shí)現(xiàn)對(duì)異常的處理。這樣導(dǎo)致的問題是,相同的代碼重復(fù)散布于整個(gè)應(yīng)用的各個(gè)角落,所以我又這樣的想法:通過Policy Injection以AOP的方式實(shí)現(xiàn)對(duì)異常的處理,當(dāng)有了這個(gè)想法后,我又多想了一步,何不再將Unity也一并整合進(jìn)來(lái)。(Source Code 下載)
一、MVP簡(jiǎn)介
為了讓一些沒有接觸過MVP的讀者能夠理解后續(xù)的內(nèi)容,我先對(duì)MVP做一個(gè)簡(jiǎn)單的介紹。如下圖所示:MVP有點(diǎn)類似于我們熟悉的MVC, View負(fù)責(zé)實(shí)現(xiàn)對(duì)UI的呈現(xiàn)已經(jīng)與用戶進(jìn)行交互,在CAB中,View一般通過一個(gè)User Control來(lái)實(shí)現(xiàn), Mode關(guān)注于具體的業(yè)務(wù)模型,獨(dú)立于View和Presenter。View具有一個(gè)Presenter的引用,當(dāng)View需要調(diào)用Mode的時(shí)候(比如需要訪問Mode傳入查詢條件獲取數(shù)據(jù)),通過Presenter訪問Mode。對(duì)于Presenter來(lái)說(shuō),它需要對(duì)View進(jìn)行操作(比如數(shù)據(jù)成功獲取后,將其顯示到View中),但是Presenter并不會(huì)“直接”對(duì)View本身進(jìn)行引用,而是引用View的接口(IView),所以View對(duì)象是一個(gè)不穩(wěn)定的對(duì)象,而Presenter僅僅需要View中一些固定的操作,所以對(duì)將這些操作定義在IView interface中,將對(duì)View的依賴轉(zhuǎn)化成對(duì)IView的依賴,這也充分體現(xiàn)了面向結(jié)構(gòu)變成的原則。
二、模擬簡(jiǎn)單的MVP
接下來(lái)我們通過一個(gè)簡(jiǎn)單的場(chǎng)景來(lái)模擬MVP。這是我常用的計(jì)算器的例子,整體的構(gòu)成如下圖所示:
1、ICalculator:Calculator的接口,定義了一個(gè)用于進(jìn)行除法運(yùn)算的操作
namespace Artech.UnityInMVP
{
??? public interface ICalculator
??? {
??????? int Divide(int op1, int op2);
??? }
}
2、Calculator:實(shí)現(xiàn)了ICalculator接口
namespace Artech.UnityInMVP
{
??? public class Calculator:ICalculator
??? {
??????? public? int Divide(int op1, int op2)
??????? {
??????????? return op1 / op2;
??????? }
??? }
}
3、CalculatePresenter:被View調(diào)用進(jìn)行數(shù)學(xué)運(yùn)算,并將運(yùn)算結(jié)果顯示到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具有一個(gè)ICalculateView 屬性,通過該屬性實(shí)現(xiàn)對(duì)于運(yùn)算結(jié)果的顯示。之所以定義ICalculator 是想解除對(duì)具體的Calculator的依賴,但是到目前為止,這個(gè)目標(biāo)還沒有達(dá)到,因?yàn)樵跇?gòu)造函數(shù)中還是依賴于Calculator。
4、ICalculateView :定義了一個(gè)用于顯示運(yùn)算結(jié)果的操作,該操作被CalculatePresenter調(diào)用
namespace Artech.UnityInMVP
{
?? public interface ICalculateView
??? {
??????? void DisplayResult(int result);
??? }
}
5、CalculateView:在本例中是一個(gè)Form,并實(shí)現(xiàn)了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的時(shí)候?qū)resenter屬性進(jìn)行初始化, 并將View對(duì)象設(shè)置為View本身。在buttonCalculate_Click中,傳入用戶輸入的操作數(shù),并調(diào)用Presenter的Calculate方法。為了處理潛在的Exception,加了一個(gè)try/catch,并在catch中調(diào)用了Enterprise Library Excepton Handling Applicaion Block進(jìn)行異常的處理。同時(shí)CalculateView 實(shí)現(xiàn)了ICalculateView的DisplayResult方法,將運(yùn)算結(jié)果顯示在TextBox中。
三、通過Unity和Policy Injection對(duì)上面的程序進(jìn)行改造
我現(xiàn)在的目標(biāo)是對(duì)上面的設(shè)計(jì)進(jìn)行改進(jìn),達(dá)到下述兩個(gè)目標(biāo):
- 通過AOP的方式進(jìn)行異常的處理,相同的try/catch頻繁出現(xiàn)不是一個(gè)好的現(xiàn)象(實(shí)際上在我們現(xiàn)在的項(xiàng)目中,除了惡異常處理,還有其他一些相識(shí)的非業(yè)務(wù)邏輯,我希望的是這些業(yè)務(wù)無(wú)關(guān)的邏輯都通過AOP實(shí)現(xiàn))。
- 解除CalculatePresenter 對(duì)Calculator的依賴,使其僅僅依賴于ICalculator。
我的思路是這樣的,將Policy Injection Application Block引入,用于實(shí)現(xiàn)Exception Handling操作;將Unity引入通過Depedency Injection實(shí)現(xiàn)對(duì)CalculatePresenter 和Calculator的解耦;同時(shí)通過Unity Extension實(shí)現(xiàn)Policy Injection和Unity的集成(參見本系列第一章).
為此我們先對(duì)CalculatePresenter進(jìn)行改造。
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的形式應(yīng)用了ExceptionCallHandler,并制定exception handling policy(在真正的項(xiàng)目開發(fā)中,我推薦通過configuration的方式應(yīng)用Policy injection)。
- 通過[Dependency]實(shí)現(xiàn)了基于Unity的Property dependency.
然后我們接著對(duì)View進(jìn)行改造,由于我們?cè)贑alculatePresenter使用了[Dependency]和[ExceptionCallHandler],我們需要通過Unity Container的方式來(lái)創(chuàng)建CalculatePresenter對(duì)象,為此我定義了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創(chuàng)建對(duì)應(yīng)的Presenter對(duì)象,這樣CalculateView就可以這樣來(lái)定義了:
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初始化時(shí),直接通過UnityContainer的Resolve方法創(chuàng)建Presenter。
我們最后來(lái)看看相關(guān)的配置:
<?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的配置,為了簡(jiǎn)單起見,我創(chuàng)建了一個(gè)自定義的ExceptionHandler:MessageBoxHandler來(lái)處理所有的exception,該handler僅僅將error message通過MessageBox顯示出來(lái),有興趣的朋友可以下載source code看看。
<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關(guān)系,實(shí)現(xiàn)了Presenter和Calculator的解耦;而extensions的配置實(shí)現(xiàn)了Policy Injection和Unity的集成,詳細(xì)實(shí)現(xiàn)可以查看本系列第一章。
<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>
這就使所有的實(shí)現(xiàn)。如何運(yùn)算出現(xiàn)異常,比如將第二個(gè)操作數(shù)設(shè)為零,我們定義的MessageBoxHandler就會(huì)被執(zhí)行,并通過MessageBox將Message顯示出來(lái),就像這樣:
?
P.S. 雖然講Policy Injection應(yīng)用到Presenter可以通過AOP的方式來(lái)進(jìn)行異常的處理,但是這要求View上的所有具有潛在異常拋出的邏輯都需要通過Presenter來(lái)實(shí)現(xiàn),因?yàn)镋xceptionHandler是應(yīng)用到Presenter上面的。
轉(zhuǎn)載于:https://www.cnblogs.com/artech/archive/2008/08/22/1273753.html
總結(jié)
以上是生活随笔為你收集整理的Enterprise Library深入解析与灵活应用(3):倘若将Unity、PIAB、Exception Handling引入MVP模式.. .. .....的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .net中用css控制GridView样
- 下一篇: HTML 标签自定义属性