zoukankan      html  css  js  c++  java
  • 浅析 MVC Pattern

    一、前言

    最近做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);
        }
    View Code IModel
        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);
                    }
                }
            }
        }
    View Code ConcreteModel

    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);
        }
    View Code IDataObserver

    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;
            }
        }
    View Code BaseRequest

     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);
            }
        }
    View Code ConcreteComboxRequest

    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;
            }
        }
    View Code ConcreteTreeviewRequest

    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
            }
        }
    View Code ConcreteDataGridViewRequest

    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);
            }
        }
    View Code ConcreteController
  • 相关阅读:
    Geogebra指令
    大表修改表结构
    mongodb获取配置参数getParameter
    mysql的binlog增量日志解析,提供增量数据订阅和消费
    chaosblade-exec-os项目的burnio.go文件解读
    mysql的where条件中的字段不要加函数
    mysql释放大表空间的正确之道
    mysql服务器问题
    purge binary logs to 'mysql-bin.039790';
    golang操作mysql数据库的规范
  • 原文地址:https://www.cnblogs.com/kkx5211/p/11528887.html
Copyright © 2011-2022 走看看