1. 程式人生 > >淺析 MVC Pattern

淺析 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

&n