談.net開發人員應該熟悉的開發模式

作者: 姜 萌@cnblogs  來源: 博客園  發布時間: 2010-12-20 23:10  閱讀: 17257 次  推薦: 2   原文鏈接   [收藏]  
摘要:下面是.NET程序員應該熟悉的幾種開發模式,當然也就包括了MVC模式等等。希望對大家有所幫助。

  我們總會有這樣一個經驗:一個系統最不容易也最不應該變化的部分是領域邏輯,最容易變化也最應該變化的是數據的呈現方式。

  在java的各種應用中可以說是到處可見mvc,j2ee貫穿mvc的概念,android的開發方式也是類mvc的,mvc結構對于做過java應用的人而言簡直就是司空見慣。而在.net這邊,由于之前微軟為大家提供的各種winform、asp.net項目典范(比如那個petshop series)將“三層”概念很好的灌輸到了.net程序員的大腦中,許多.net開發者凡是做個東西都要搬出自己最拿手的IModel、IDAL這樣的神器。

  其實mvc與所謂的“三層架構”是兩個層次上的東西,前者是一種結構模式,而后者則是分層的角度去說。

  一件很奇怪的事情,許多人知道“三層”卻不知道mvc,其實這要歸結與.net的早期開發技術asp.net和winform這些page controller的典范讓許多人對三層夸夸其談卻對mvc視而不見甚至一無所知。什么是page controller模式呢?搞.net的大多都用過winform和webform,這種xxxform用起來很直觀,我們想要做一個程序,ok,最簡單的方式就是拖拖拽拽幾個控件,然后在一個叫code behind的東西里寫這些UI事件的處理邏輯,加一大堆變量用于記錄數據和狀態,這樣一個程序就能出爐。這種開發方式對于一些小軟件系統的開發其實效率還是蠻高的,后來人們看到其弊端---一旦修改UI,事件處理就要跟著變,但是業務還是那個業務,憑什么要修改非UI的代碼?于是有人提出“三層”,最樸素的理解就是將原本那堆事件處理里的code分成業務代碼和數據庫訪問代碼并轉移到其它類中,做多了就把那坨UI叫做UI,那坨業務代碼叫做BLL,那坨DAO叫做DAL。也就是這種架構:

image

  而對于j2ee的開發者來說熟悉的是下圖。 

image

  (說明:這兩幅圖copy自是daxnet文)

  MVC是什么

  MVC是一個很經典的結構,并且其又其思想衍生出很多變種比如MVP,MVVP。傳統的MVC結構之一是這樣的(拿主動型mvc來說):

image

  比如web開發(比如asp.net mvc或者是java的web開發方式),view就是純web頁面或者webservice,當提交一個表單/調用webservice或者ajax后會將數據提交給controller(當然期間可能會經過各種filterchain、listener這樣的東西)controller調用相應的業務模塊來處理這個請求,最終結果會更新View的顯示。

  MVP

  對于非天然mvc的框架

  對于asp.net/winform而言,雖然可以通過改造讓其支持mvc結構的開發(比如通過定制IHttpModule、IHttpHandler云云),但是在企業看來這些都算是邪門武功(因為這樣會喪失xxxform在開發上的很多特性比如快速開發)。大多數使用的是mvp模式。什么是mvp呢?其實mvp是mvc的一個變種。因為用winform或者webform的話form始終是個阻礙mvc開發的問題。那么好,我們仍然使用designer和codebehind,其實一個架構設計的好壞是取決于人而不是具體的技術的,只要我們OO一時強page controller一樣好用。

image

  在MVP模式中我們需要自己定制各個View(web頁面或者窗體)對應的IView和IPresenter、IModel。IView要對IPresenter暴露操作UI、數據綁定的接口,IPresenter對IView要暴露當UI事件觸發需要調用的接口,IPresenter根據IView傳遞過來的請求調用業務接口并根據結果操作UI。舉個簡單的例子,一個計算“x+y=?”的程序。如果我們這樣定義IPresenter和IView

 
public interface IPresenter
{
IView View {
get; set; }
void CalculateResult();
}


public interface IView
{
IPresenter Presenter {
get; set; }
void ShowResult(string result);
int ValueOne { get; }
int ValueTwo { get; }
}

  IPresenter的實現如下(這里從簡把IModel去掉了)

Presenter
namespace ClientLibrary
{
    
public class Presenter : IPresenter
    {
        
private IView _view;
        
public IView View
        {
            
get
            {
                
return _view;
            }
            
set
            {
                _view 
= value;
                _view.Presenter 
= this;
            }
        }

        
private static readonly string RESULT_FORMATTER = "{0}+{1},the result is {2}";
        
public void CalculateResult()
        {
            
if (_view != null)
            {
                var result 
= string.Format(RESULT_FORMATTER, _view.ValueOne, _view.ValueTwo, _view.ValueOne + _view.ValueTwo);
                _view.ShowResult(result);
                
this.A = 123;
            }
        }
        
private int _a;
        
public int A
        {
            
set
            {
                A 
= value;
            }
        }
    }
}

View的實現如下(那silverlight為例,換成別的也行)

MainPage
namespace debug
{
    
public partial class MainPage : UserControl, IView
    {
        
public MainPage()
        {
            InitializeComponent();
        }

        
private IPresenter _presenter;

        
private void btn_Click(object sender, RoutedEventArgs e)
        {
            
if (_presenter != null)
            {
                _presenter.CalculateResult();
            }
            
#region hidden
            /*int total = 0;
            try
            {
                total = int.Parse(tb1.Text) + int.Parse(tb2.Text);
                MessageBox.Show("計算結果:" + total.ToString());
            }
            catch (Exception ex)
            {
                MessageBox.Show("出錯啦" + ex.ToString());
            }
            finally
            {
                tb1.Text = string.Empty;
                tb2.Text = string.Empty;
            }
*/
            #endregion

        }

        
public IPresenter Presenter
        {
            
get
            {
                
return _presenter;
            }
            
set
            {
                _presenter 
= value;
            }
        }

        
public void ShowResult(string result)
        {
            MessageBox.Show(result);
        }

        
public int ValueOne
        {
            
get { return int.Parse(tb1.Text); }
        }

        
public int ValueTwo
        {
            
get { return int.Parse(tb2.Text); }
        }
    }
}

  一個很簡單的東西,看上去寫成的要多些那么一坨東西,但是好處是顯而易見的,就是更換view非常方便,根本不用去改你的IPresenter、Presenter和業務。一切都是接口調用而不依賴具體實現,這就是好處。

  你必須要懂的MVVM

  對于.NET平臺的開發人員,托微軟的福分我們擁有一種更為強大的模型---MVVM。這應該算是做WPF/Silverlight應用的人必懂的一種結構,WPF/silverlight天生支持數據綁定和命令綁定(不過sl在命令綁定上還比較弱),這就為我們使用MVVM創造了可能。

  View是什么呢,純的View只有xaml或者附帶必要的只與View本身相關邏輯代碼。ViewModel,你可以把它理解為View具體呈現內容所依賴數據的一個抽象,在MVVM中View與ViewModel總會有一種綁定關系,一旦ViewModel中被綁定的數據發生改變View上的數據就會跟著變,相反也有可能,比如你的賬號密碼框內容發生變化,關聯的ViewModel中的數據就會被框架自動通知到。

  在wpf/silverlight中,綁定是通過xaml語法來完成(雖然你可以選擇用c#來寫但不符合mvvm的宗旨),并且綁定雙方的通知機制是有框架來完成,也就是說一個會xaml和blend的美工只需事先和coder商量下“咱們的xx和xx是在哪個ViewModel上叫XXX的屬性的XXX屬性……”問題之后就可以各干各的了。那么ViewModel怎么寫,咋view中又怎么綁定到viewmodel呢?首先我們談ViewModel。

  說道ViewModel你需要知道依賴屬性和依賴對象的概念,這是wpf/silverlight的基礎所以不多說。有兩種方式寫ViewModel。第一種是自己去實現INotifyPropertyChanged接口,并在屬性變化時去調用NotifyPropertyChanged事件。

  為了方便我們定義一個ViewModelBase的抽象基類,然后讓其他ViewModel繼承這個基類。

ViewModelBase
public abstract class ViewModelBase : System.ComponentModel.INotifyPropertyChanged, IDisposable 
    { 
        
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 
        
protected void OnPropertyChanged(string propertyName) 
        { 
            
if (PropertyChanged != null
            { 
                var arg 
= new System.ComponentModel.PropertyChangedEventArgs(propertyName); 
                PropertyChanged(
this, arg); 
            } 
        } 
        
public virtual void Dispose() 
        { 
           
        } 
    }
 
DemoViewModel
public class DemoViewModel : ViewModelBase
    {
        
#region fields
        private string _propertyA;
        
#endregion
        #region presentation properties
        public string PropertyA
        {
            
get
            {
                
return _propertyA;
            }
            
set
            {
                
if (_propertyA != value)
                {
                    _propertyA 
= value;
                    
base.OnPropertyChanged("PropertyA");
                }
            }
        }
        
#endregion
    }

  第二種是利用DependencyObject和DependencyProperty。

PeopleItemViewModel
public class PeopleItemViewModel : DependencyObject, IPeopleItemViewModel
    {
        
public PeopleItemViewModel()
        {
            
        }
        
public static readonly DependencyProperty SimpleUserDataProperty = DependencyProperty.Register("SimpleUserData"typeof(SimpleUserData), typeof(PeopleItemViewModel));
        
public static readonly DependencyProperty RelativeSimpleUserDataProperty = DependencyProperty.Register("RelativeSimpleUserData"typeof(ObservableCollection<SimpleUserData>), typeof(PeopleItemViewModel));
        
public static readonly DependencyProperty AllSimpleUserDataProperty = DependencyProperty.Register("AllSimpleUserData"typeof(ObservableCollection<SimpleUserData>), typeof(PeopleItemViewModel));

        
public SimpleUserData SimpleUserData
        {
            
get
            {
                
return (SimpleUserData)base.GetValue(SimpleUserDataProperty);
            }
            
set
            {
                
if (!base.CheckAccess())
                {
                    Dispatcher.Invoke(
new Action(
                        () 
=>
                        {
                            SimpleUserData 
= value;
                        }));
                }
                
else
                    base.SetValue(SimpleUserDataProperty, value);
            }
        }
        
public ObservableCollection<SimpleUserData> RelativeSimpleUserData
        {
            
get
            {
                
return (ObservableCollection<SimpleUserData>)base.GetValue(RelativeSimpleUserDataProperty);
            }
            
set
            {
                
if (!base.CheckAccess())
                {
                    Dispatcher.Invoke(
new Action(
                        () 
=>
                        {
                            RelativeSimpleUserData 
= value;
                        }));
                }
                
else
                {
                    
base.SetValue(RelativeSimpleUserDataProperty, value);
                    var collectionView 
= CollectionViewSource.GetDefaultView(value);
                    collectionView.SortDescriptions.Add(
new SortDescription("Distance", ListSortDirection.Ascending));
                }
            }
        }
        
public ObservableCollection<SimpleUserData> AllSimpleUserData
        {
            
get
            {
                
return (ObservableCollection<SimpleUserData>)base.GetValue(AllSimpleUserDataProperty);
            }
            
set
            {
                
if (!base.CheckAccess())
                {
                    Dispatcher.Invoke(
new Action(
                        () 
=>
                        {
                            AllSimpleUserData 
= value;
                        }));
                }
                
else
                {
                    
base.SetValue(AllSimpleUserDataProperty, value);
                    var collectionView 
= CollectionViewSource.GetDefaultView(value);
                    collectionView.SortDescriptions.Add(
new SortDescription("Distance", ListSortDirection.Ascending));
                }
            }
        }
}

  在View中綁定ViewModel。

  為了方便,我們可以在app.xaml中將需要的viewmode放到全局資源字典中。

image

  然后再我們的vs視圖設計器Properties(中文版顯示的是“屬性”)頁上選擇為綁定源設置綁定目標(包括source和path等)以及必要的值轉換器等等即可。

image image image

  (PS:雖然vs很強大,但個人還是建議熟悉xaml的綁定語法,想當初用vs2008搞wpf的時候貌似還沒有這么方便的設計器。。。)

2
0
 
標簽:.NET MVC 程序員
 
 

文章列表

全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()