一起谈.NET技术,谈.net开发人员应该熟悉的开发模式

简介:   我们总会有这样一个经验:一个系统最不容易也最不应该变化的部分是领域逻辑,最容易变化也最应该变化的是数据的呈现方式。  在java的各种应用中可以说是到处可见mvc,j2ee贯穿mvc的概念,android的开发方式也是类mvc的,mvc结构对于做过java应用的人而言简直就是司空见惯。

  我们总会有这样一个经验:一个系统最不容易也最不应该变化的部分是领域逻辑,最容易变化也最应该变化的是数据的呈现方式。

  在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的时候貌似还没有这么方便的设计器。。。)

目录
相关文章
|
11天前
|
人工智能 开发框架 量子技术
【专栏】.NET 技术:驱动创新的力量
【4月更文挑战第29天】.NET技术,作为微软的开发框架,以其跨平台、开源和语言多样性驱动软件创新。它在云计算、AI/ML、混合现实等领域发挥关键作用,通过Azure、ML.NET等工具促进新兴技术发展。未来,.NET将涉足量子计算、微服务和无服务器计算,持续拓宽软件开发边界,成为创新的重要推动力。掌握.NET技术,对于开发者而言,意味着握有开启创新的钥匙。
|
11天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
11天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
11天前
|
开发框架 Cloud Native 开发者
【专栏】剖析.NET 技术的核心竞争力
【4月更文挑战第29天】本文探讨了.NET框架在软件开发中的核心竞争力:1) .NET Core实现跨平台与云原生技术的融合,支持多操作系统和容器化;2) 提升性能和开发者生产力,采用JIT、AOT优化,提供C#新特性和Roslyn编译器平台;3) 支持现代化应用架构,包括微服务和容器化,内置安全机制;4) 丰富的生态系统和社区支持,拥有庞大的开发者社区和微软的持续投入。这些优势使.NET在竞争激烈的市场中保持领先地位。
|
11天前
|
开发框架 .NET 开发者
【专栏】领略.NET 技术的创新力量
【4月更文挑战第29天】.NET技术自ASP.NET起历经创新,现以.NET Core为核心,展现跨平台能力,提升性能与生产力,支持现代化应用架构。.NET Core使开发者能用同一代码库在不同操作系统上构建应用,扩展至移动和物联网领域。性能提升,C#新特性简化编程,Roslyn编译器优化代码。拥抱微服务、容器化,内置安全机制,支持OAuth等标准。未来.NET 6将引入更快性能、Hot Reload等功能,预示着.NET将持续引领软件开发潮流,为开发者创造更多机会。
|
11天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
11天前
|
人工智能 前端开发 Cloud Native
【专栏】洞察.NET 技术的开发趋势
【4月更文挑战第29天】本文探讨了.NET技术的三大发展趋势:1) 跨平台与云原生技术融合,通过.NET Core支持轻量级、高性能应用,适应云计算和微服务;2) 人工智能与机器学习的集成,如ML.NET框架,使开发者能用C#构建AI模型;3) 引入现代化前端开发技术,如Blazor,实现前后端一致性。随着.NET 8等新版本的发布,期待更多创新技术如量子计算、AR/VR的融合,.NET将持续推动软件开发的创新与进步。
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0
|
16天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
22 0
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0