MVC仍是MVVM?或許VMVC更適合WinForm客戶端

最近開始重構一個稍嫌古老的C/S項目,原先採用的技術棧是『WinForm』+『WCF』+『EF』。相對於如今鋪天蓋地的B/S架構來講,看上去彷佛和Win95同樣古老,不少新入行的,可能就沒有見過經典的C/S架構的系統。事實上,做爲企業信息管理系統,包括ERP/CRM/SCM等,桌面客戶端仍是很OK的。程序員

此次重構原定的目標有兩個:數據庫

一、客戶端仍是WinForm不變,但使用MVC模式重寫;編程

二、WCF改爲WebAPI。api

通過2周時間的嘗試和探索,重構計劃變動爲:服務器

一、使用VMVC模式來重構WinForm客戶端;架構

二、用WCF實現僞WebAPI,其本質仍是個WCF服務,但實現了RESTful風格的WebAPI。ide

此次和你們分享我對客戶端架構的一些探索,就不展開服務端相關的話題了。那麼,什麼是VMVC呢?呵呵,這個是我發明的新名稱,和MVC的區別在於用ViewModel替換了Model。ViewModel和View之間實現雙向數據綁定,View上面的交互產生的操做指令,仍是由Controller接收,而後經過對ViewModel的操做,更新View的數據。單元測試

簡單地說,就是ViewModel負責數據流,View負責顯示和接受用戶指令,而Controller則居中調度。示意圖以下:測試

因爲實現了數據雙向綁定,因此在必定程度上簡化了數據的存儲。只須要執行ViewModel上的Save()方法,就能夠將新的數據經過WebAPI存儲到數據庫了。url

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 }
View Code
相關文章
相關標籤/搜索