zoukankan      html  css  js  c++  java
  • MVC还是MVVM?或许VMVC更适合WinForm客户端

    MVC还是MVVM?或许VMVC更适合WinForm客户端

    最近开始重构一个稍嫌古老的C/S项目,原先采用的技术栈是『WinForm』+『WCF』+『EF』。相对于现在铺天盖地的B/S架构来说,看上去似乎和Win95一样古老,很多新入行的,可能就没有见过经典的C/S架构的系统。事实上,作为企业信息管理系统,包括ERP/CRM/SCM等,桌面客户端还是很OK的。

    这次重构原定的目标有两个:

    1、客户端还是WinForm不变,但使用MVC模式重写;

    2、WCF改成WebAPI。

    经过2周时间的尝试和探索,重构计划变更为:

    1、使用VMVC模式来重构WinForm客户端;

    2、用WCF实现伪WebAPI,其本质还是个WCF服务,但实现了RESTful风格的WebAPI。

    这次和大家分享我对客户端架构的一些探索,就不展开服务端相关的话题了。那么,什么是VMVC呢?呵呵,这个是我发明的新名称,和MVC的区别在于用ViewModel替换了Model。ViewModel和View之间实现双向数据绑定,View上面的交互产生的操作指令,还是由Controller接收,然后通过对ViewModel的操作,更新View的数据。

    简单地说,就是ViewModel负责数据流,View负责显示和接受用户指令,而Controller则居中调度。示意图如下:

    由于实现了数据双向绑定,所以在一定程度上简化了数据的存储。只需要执行ViewModel上的Save()方法,就可以将新的数据通过WebAPI存储到数据库了。

    ViewModel的职责非常明确,就是一个数据流引擎!所以基本上都是Load()、Save()、Show()、Refresh()、Close()这些无脑方法,一丁点的业务逻辑都木有。非常适合有一定编程经验,但不了解业务逻辑的程序员编写。

    而View就更简单了,完全由VS的窗体设计器生成。UI设计师从此不需要PS了,根据产品原型直接拖控件就OK。

    最后,所有的业务逻辑都写在Controller里面,这样就为自动化测试提供了可能。测试工程师只需要编写一段测试代码替代Controller,同时对View的数据进行注入就可以跑单元测试。

    下面是我用于尝试这种模式的示例,希望能够起到抛砖引玉的作用。

    代码结构:

    Controller(部分代码),通过订阅View上面的确定按钮点击事件实现用户操作的委托:

     1         /// <summary>
     2         /// 修改服务器配置
     3         /// </summary>
     4         private void ConfigServer()
     5         {
     6             _SetModel = new SetModel();
     7 
     8             // 订阅确定按钮点击事件
     9             _SetModel.View.ConfirmButton.Click += SetConfirm_Click;
    10             _SetModel.ShowDialog();
    11         }
    12 
    13         /// <summary>
    14         /// 点击确定按钮
    15         /// </summary>
    16         /// <param name="sender"></param>
    17         /// <param name="e"></param>
    18         private void SetConfirm_Click(object sender, EventArgs e)
    19         {
    20             if (!_SetModel.Test()) return;
    21 
    22             _SetModel.Save();
    23             _SetModel.Close();
    24         }

    ViewModel:

      1 using System;
      2 using System.Windows.Forms;
      3 using Insight.Utils.Client;
      4 using Insight.Utils.Common;
      5 using Insight.WS.Client.Common.Utils;
      6 using Insight.WS.Client.MainApp.Views;
      7 
      8 namespace Insight.WS.Client.MainApp.Models
      9 {
     10     public class SetModel
     11     {
     12         public LoginSet View = new LoginSet();
     13 
     14         private string _Address = Config.BaseAddress();
     15         private string _Port = Config.Port();
     16         private bool _SaveUser = Config.IsSaveUserInfo();
     17 
     18         /// <summary>
     19         /// 构造方法,初始化控件初始值
     20         /// 通过订阅事件实现双向数据绑定
     21         /// </summary>
     22         public SetModel()
     23         {
     24             View.AddressInput.EditValueChanged += AddressChanged;
     25             View.AddressInput.Text = _Address;
     26 
     27             View.PortInput.EditValueChanged += PortChanged;
     28             View.PortInput.Text = _Port;
     29 
     30             View.SaveUserCheckBox.CheckStateChanged += SaveUserChanged;
     31             View.SaveUserCheckBox.Checked = _SaveUser;
     32         }
     33 
     34         /// <summary>
     35         /// 显示对话框
     36         /// </summary>
     37         public void ShowDialog()
     38         {
     39             View.ShowDialog();
     40         }
     41 
     42         /// <summary>
     43         /// 关闭对话框
     44         /// </summary>
     45         public void Close()
     46         {
     47             View.DialogResult = DialogResult.OK;
     48             View.Close();
     49         }
     50 
     51         /// <summary>
     52         /// 测试服务器连通性
     53         /// </summary>
     54         /// <returns>bool 是否通过连通性测试</returns>
     55         public bool Test()
     56         {
     57             var url = $"http://{_Address}:{_Port}/commonapi/v1.0/test";
     58             var result = new HttpClient(url).Request(Params.Token);
     59             if (result.Code != "400") return true;
     60 
     61             Messages.ShowError("请配置正确的服务器地址和端口号!");
     62             return false;
     63         }
     64 
     65         /// <summary>
     66         /// 保存设置
     67         /// </summary>
     68         public void Save()
     69         {
     70             if (!_SaveUser) Config.SaveUserName(string.Empty);
     71 
     72             Config.SaveIsSaveUserInfo(_SaveUser);
     73             Config.SaveAddress(_Address, _Port);
     74 
     75             Params.InsightServer = $"http://{_Address}:{_Port}";
     76         }
     77 
     78         /// <summary>
     79         /// 服务器地址发生变化
     80         /// </summary>
     81         /// <param name="sender"></param>
     82         /// <param name="e"></param>
     83         private void AddressChanged(object sender, EventArgs e)
     84         {
     85             _Address = View.AddressInput.Text;
     86         }
     87 
     88         /// <summary>
     89         /// 服务端口发生变化
     90         /// </summary>
     91         /// <param name="sender"></param>
     92         /// <param name="e"></param>
     93         private void PortChanged(object sender, EventArgs e)
     94         {
     95             _Port = View.PortInput.Text;
     96         }
     97 
     98         /// <summary>
     99         /// 保存用户账号选项发生变化
    100         /// </summary>
    101         /// <param name="sender"></param>
    102         /// <param name="e"></param>
    103         private void SaveUserChanged(object sender, EventArgs e)
    104         {
    105             _SaveUser = View.SaveUserCheckBox.Checked;
    106         }
    107     }
    108 }

    标签: MVC设计模式

  • 相关阅读:
    真正的e时代
    在线手册
    UVA 10616 Divisible Group Sums
    UVA 10721 Bar Codes
    UVA 10205 Stack 'em Up
    UVA 10247 Complete Tree Labeling
    UVA 10081 Tight Words
    UVA 11125 Arrange Some Marbles
    UVA 10128 Queue
    UVA 10912 Simple Minded Hashing
  • 原文地址:https://www.cnblogs.com/grj001/p/12224373.html
Copyright © 2011-2022 走看看