界面程序分离思路

目前web开发中面临的一个问题是,很难分清楚哪些是程序员负责的,哪些是美工负责的。在同一个公司还好办,沟通容易,协调也简单。如果是多方合作的项目,一方负责界面一方负责程序,这样分工就很难,由于界面是和一些控制逻辑邦定在一起的,这部分工作很难说是属于哪方,并且涉及界面的部分也难以并行进行。

如现在的一个项目,A方:负责页面(html,aspx)B方:负责程序。今天A方把几个页面调整了一下关系,明天又把弹出的对话框改成页面,后天又在页面上加了几个文本框……,A的调整很可能涉及到B的工作,即使业务逻辑没有改变,页面改变时B也要做一些代码的调整。B很希望有一种方式可以从这种情况下解脱出来,B希望界面改变时自己不修改任何代码,A的修改对B是透明的。

MVC可能是一个选择,但他解耦出来的是M,见图(最后有两个详细的图):

单击在新窗口中打开图片,Ctrl+滚轮缩放图片

V和C关联还很紧密,MV,MC之间可以用observer模式,这样形成了弱关联,但仍然会遇到开始时的问题。

对业务的封装很多,如B为A提供一个充值接口,这样业务不改变时B不会去修改代码。能不能通过一种方式,把界面也抽象出一个接口,把此接口提供给A,这样在界面修改时,B就不用做代码的修改了。B就从此解脱了(太高兴了,终于可以……)。

提供界面接口的模式被成为MVP,MVC的变种。可以参考下面的文章:

http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/MVP.mspx

这篇文章通过一个例子说明了MVP,但是他涉及到了很多其他的东西如:nunit,反到把MVP给隐藏掉了。文章带的代码很好,提供了很好的分层方式,对说明MVP不合适,但在实践的项目中却是很好的模型。

下面MVP的类图,View是UI,IView是B要给A定义的界面接口,这样界面改变时(如,上面说的几种情况),只要接口不改变,B就不用修改代码。Presenter类似于MVC的C,但是他要比C负责更多的东西。

单击在新窗口中打开图片,Ctrl+滚轮缩放图片

下面是例子的类图,要比上面单纯的MVP复杂一些,增加了一个Service层,此层分离了P和域模型、数据层。此处的UI是指上图的View和IVIew,此图也并没有详细的说明例子的类关系,只有把此图和上图结合才能比较详细的说明例子中的类(把UI用View和Iview替换掉,不想自己画了)。

单击在新窗口中打开图片,Ctrl+滚轮缩放图片

解决方案中包含14个项目其中六个是测试项目。8个项目中DataAccess、Domain、DTO、ValueObject是和数据有关系的属于M(可细分为Domain和DA);Controls和UI指V;Presentation是指P;Task指Service。可以看出关系还是很复杂的,6个测试项目中还包含UI的测试。

V的代码:
public partial class ViewCustomers : Page, IViewCustomerView
{
    private ViewCustomerPresenter presenter;

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        presenter = new ViewCustomerPresenter(this);
        this.customerDropDownList.SelectedIndexChanged += delegate
        {
          presenter.DisplayCustomerDetails();
        };
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            presenter.Initialize();
        }
    }

    public ILookupList CustomerList
    {
        get { return new WebLookupList(this.customerDropDownList); }
    }

    public string CompanyName
    {
        set { this.companyNameLabel.InnerText = value; }
    }
    public string ContactName
    {
        set { this.contactNameLabel.InnerText = value; }
    }
    //省略
}

其中属于B的工作是IViewCustomerView 的实现,属于IView,这是为实现MVP,B必须增加的接口,虽然增大了部分工作量,但为了以后少修改还是十分值的。另一个是ViewCustomerPresenter的实现,属于P,类似于MVC的C,P在此例子中通过Task(图中的Service)和DTO、Domain关联,并没有直接访问他们,隔离了业务逻辑和P,用了Facede模式,能适应多种域模型,如以后用webservice提供数据。。

P的实现:可见P依赖于IView和Modle(同过ICustomerTask),Service和P的作用要包含MVC的C,在此例子中C主要是由Service实现的。
public class ViewCustomerPresenter
{
    private readonly IViewCustomerView view;
    private readonly ICustomerTask task;

    public ViewCustomerPresenter(IViewCustomerView view) : this(view, new CustomerTask()) { }

    //依赖注入:DI,IOC
    public ViewCustomerPresenter(IViewCustomerView view, ICustomerTask task)
    {
        this.view = view;
        this.task = task;
    }

    public void Initialize()
    {
        task.GetCustomerList().BindTo(view.CustomerList);
    }

    public void DisplayCustomerDetails()
    {
        int? customerId = SelectedCustomerId;

        if (customerId.HasValue)
        {
            CustomerDTO customer = task.GetDetailsForCustomer(customerId.Value);
            UpdateViewFrom(customer);
        }
    }
    //省略
}

Modle部分很简单,DataAccess封装了通用的数据库访问;Domain域模型;DTO域到对象类的转换;ValueObject是辅助DTO的。分了四个项目,很清楚,不过实际上也可以不用分这么多。

Controls项目是为了测试UI而加上的(当然还可以分离UI的功能),虽然属于V的部分,即A的工作,但是也可以和UI项目合在一起。此项目和UI都用到了ILookupList(DTO),因此V还是依赖于DTO的,ILookupList是列表框的更高层的抽象,代码:
public class WebLookupList : ILookupList
{
    private ListControl underlyingList;

    public WebLookupList(ListControl underlyingList)
    {
        this.underlyingList = underlyingList;
    }

    public void Add(ILookupDTO dto)
    {
        underlyingList.Items.Add(new ListItem(dto.Text, dto.Value));
    }

    public void Clear()
    {
        underlyingList.Items.Clear();
    }

    public ILookupDTO SelectedItem
    {
        get
        {
            ListItem item = underlyingList.SelectedItem;
            return new SimpleLookupDTO(item.Value, item.Text);
        }
    }
}

这样只要IViewCustomerView接口定下来,A所要求的界面就和B再无关系了。
看了MVP,这下B终于开心了,因为A和B的所负责的部分分的很清晰;定义接口以后就可以完全的并行开始工作;最重要的是界面改变时,B可以开心做做其他工作,而不必天天被A烦。

此后,B遇到有关界面的项目时,就可以提供给A一个IView接口,这样只要接口不改变,界面不论怎么变都和B无关了,即使A换成WinForm。

例子中涉及到很多接口,完全是面向接口编程,我觉得有些是没有必要的如Domain项目中的接口,只所以还提供接口是为了测试,但有些是不能少的,如ILookupList,是很好的面向对象的例子。

附:两个MVC图
单击在新窗口中打开图片,Ctrl+滚轮缩放图片

单击在新窗口中打开图片,Ctrl+滚轮缩放图片  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值