一、前言
最近做CAD插件相关的工作,用到了一些模式,解决对应场景的问题。 比如插件的运行实例上使用Singleton、实例内部使用了MVC(Strategy and Observer )。
针对CAD插件,插件可以在CAD运行过程中多次打开关闭,数据状态需要保持一致,数据联动,及多种UI布局模式。
1、Singleton 维持一个全局唯一实例,使得插件运行变得有“状态” 、提升插件的打开速度。
2、MVC 对程序结构进行解耦,方便不同UI进行数据互通、复用使用多种UI布局模式。
3、在这里本文主要针对MVC进行回顾。
代码链接:https://github.com/Shawn-china/MVCDemo.git
二、场景与模式
模式很多,场景各不同通常一个模式都对应一个特定场景,所以也就没有什么万能模式解决所有问题。从"Gang of Four" 总结23种设计模式之后,又不断有新的模式被总结出来。
比如:创建型模式中Singleton 维护一个全局唯一实例、Factory负责创建实例;行为型模式解决一些运行时问题;结构型模式适用模板类问题。
使用面向对象开一个应用程序除了满足功能需求,其次还要达到OO目标。 代码层面可读、高内聚低耦合、复用易扩展维护等。
实现目标二可通过多种途径:遵循代码规范、使用模式、面向抽象、面向接口、使用组合等具体的方法。
一些常见的开发框架中也可以看到很多模式的影子, Vue.js中双向绑定使用观察者模式实现,Qt中Model/View 模式简化了UI和数据的“交互”,一些带发布订阅机制的第三方软件。
三、MVC
MVC是挪威计算机科学家:Trygve Mikkjel Heyerdahl Reenskaug,在1979年为GUI软件设计制定的模式。参考维基百科:https://en.wikipedia.org/wiki/Trygve_Reenskaug
MVC有三种类型的“对象”。
Model 应用程序数据,
View 应用程序UI,可以有多种体现形式如:winform、wpf、控制台、web页面等,
Controller定义了View对输入的处理方式。
当Model数据更改时,它将更新View。一个模型可以对应多个视图。
如下图示:
四、UML 类图
MVC有很多具体形式,可以view 先行也可以controller先行。在这里使用图一UML所描述的实现方式。注:本质上其实为 Observer 与 Strategy 复合模式。
图一
五、Code show
1、Model 实现一个数据监听 Observer,对数据数据对象进行监听,当数据变动可以通知订阅者。
public interface IModel { ArrayList DataObservers { get; set; } void RegisterObserver(IDataObserver concreteObserver, string key); void UnregisterObserver(IDataObserver concreteObserver); /// <summary> /// /// </summary> /// <param name="baseObject">Base class containing an Id field and a Name field</param> /// <param name="key"></param> void GetData(BaseObject baseObject, string key); }
public class ConcreteModel : IModel { public ArrayList DataObservers { get; set; } = new ArrayList(); public void RegisterObserver(IDataObserver concreteObserver, string key) { if (!this.DataObservers.Contains(concreteObserver)) { this.DataObservers.Add(concreteObserver); } } public void UnregisterObserver(IDataObserver concreteObserver) { if (this.DataObservers.Contains(concreteObserver)) { this.DataObservers.Remove(concreteObserver); } } public void GetData(BaseObject baseObject, string key) { this.Notity(baseObject, key); } private void Notity(BaseObject baseObject, string key) { foreach (object item in this.DataObservers) { if (((IDataObserver)item).ObserverKeys.Contains(key)) { ((IDataObserver)item).Update(baseObject); } } } }
2、view 包含一个Model 一个Controller ,实现IDataObserver接口 。示例包含三个view : 1、ComboBox 2、TreeView 3、DataGridView
IDataObserver 数据观察者
public interface IDataObserver { List<string> ObserverKeys { get; set; } string SubscriptionKey { get; set; } void Update(object data); }
BaseRequest 作为所有view类的基类,包含一个Model 一个Controller
public class BaseRequest { public static IModel ConcreteModel; public static IController ConcreteController; public BaseRequest() { ConcreteModel = ConcreteModel ?? new ConcreteModel(); ConcreteController = ConcreteController ?? new ConcreteController(ConcreteModel); } public BaseRequest(IModel concreteModel, IController concreteController) { ConcreteModel = ConcreteModel ?? concreteModel; ConcreteController = ConcreteController ?? concreteController; } }
ComboBox ,被TreeView 和 DataGridView 观察 。
public class ConcreteComboxRequest : BaseRequest, IDataObserver { private readonly ComboBox _comboBox; public ConcreteComboxRequest(ComboBox comboBox) { this._comboBox = comboBox; this.IntializeView(); } public List<string> ObserverKeys { get; set; } public string SubscriptionKey { get; set; } = $"{nameof(ConcreteComboxRequest)}"; public void Update(object data) { this._comboBox.DataSource = DataContainer.Schools; } private void IntializeView() { this._comboBox.DisplayMember = "Name"; this._comboBox.ValueMember = "Id"; this._comboBox.SelectedIndexChanged += this.cmb_SelectedValueChanged; } private void cmb_SelectedValueChanged(object sender, EventArgs e) { BaseObject baseObject = (BaseObject)this._comboBox.SelectedItem; ConcreteController.GetDatas(baseObject, this.SubscriptionKey); } }
TreeView,注册到Model监听Observer中,观察ComboBox。 同时被DataGridView 观察
public class ConcreteTreeviewRequest : BaseRequest, IDataObserver { private readonly TreeView _treeView; public ConcreteTreeviewRequest(TreeView treeView) { this._treeView = treeView; this.IntializeView(); } public List<string> ObserverKeys { get; set; } = new List<string> { $"{nameof(ConcreteComboxRequest)}" }; public string SubscriptionKey { get; set; } = $"{nameof(ConcreteTreeviewRequest)}"; public void Update(object data) { BaseObject baseObject = (BaseObject)data; this.InitializeTreeView(baseObject); } private void IntializeView() { foreach (string observerKey in this.ObserverKeys) { ConcreteModel.RegisterObserver(this, observerKey); } this._treeView.AfterSelect += this.treeView_SelectedValue; } private void InitializeTreeView(BaseObject baseObject) { this._treeView.Nodes.Clear(); List<Grade> grades = Grade.GetList(baseObject); foreach (Grade item in grades) { this.CreateTreeNode(null, item); } } private void treeView_SelectedValue(object sender, TreeViewEventArgs e) { TreeNode currentNode = this._treeView.SelectedNode; BaseObject baseObject = (BaseObject)currentNode.Tag; ConcreteController.GetDatas(baseObject, this.SubscriptionKey); } private TreeNode CreateTreeNode(TreeNode parentNode, BaseObject concreteData) { TreeNode treeNode = new TreeNode { Tag = concreteData, Name = concreteData.Id, Text = concreteData.Name }; if (parentNode == null) { this._treeView.Nodes.Add(treeNode); } else { parentNode.Nodes.Add(treeNode); } return treeNode; } }
DataGridView,,注册到Model监听Observer中,观察ComboBox、TreeView 。
public class ConcreteDataGridViewRequest : BaseRequest, IDataObserver { private readonly DataGridView _dataGridView; public ConcreteDataGridViewRequest(DataGridView dataGridView) { this._dataGridView = dataGridView; this.IntializeView(); } public List<string> ObserverKeys { get; set; } = new List<string> { $"{nameof(ConcreteTreeviewRequest)}", $"{nameof(ConcreteComboxRequest)}" }; public string SubscriptionKey { get; set; } = $"{nameof(ConcreteDataGridViewRequest)}"; public void Update(object data) { BaseObject baseObject = (BaseObject)data; this.InitializeDataGridView(baseObject); } private void InitializeDataGridView(BaseObject baseObject) { this._dataGridView.Columns.Clear(); List<Student> students = Student.GetList(baseObject); this._dataGridView.DataSource = students; } private void IntializeView() { foreach (string observerKey in this.ObserverKeys) { ConcreteModel.RegisterObserver(this, observerKey); } this._dataGridView.SelectionChanged += this.dataGridView_SelectionChanged; } private void dataGridView_SelectionChanged(object sender, EventArgs e) { // Do some business logic } }
3、Controller 包含一个 Model ,当某一view “主题”变化时,调用Model 通知对应的订阅对象。
public interface IController { void GetDatas(BaseObject baseObject, string subscriberKey); }
public class ConcreteController : IController { private readonly IModel _model; public ConcreteController(IModel model) { this._model = model; } public void GetDatas(BaseObject baseObject, string subscriberKey) { this._model.GetData(baseObject, subscriberKey); } }