MATLAB App Designer教程連載5: App Designer和MVC模式
通過前面幾節的學習,我們展示了Appdesigner如下的功能:
- 新的和工業相關的控制項
- 更方便的布局方法
- 自動生成對應於布局的面向對象的代碼
App Designer的顯著優點是幫助更方便的布局,我們可以把這個優點和MVC設計模式結合起來,也就是說,把App Designer生成的代碼當做MVC中的View類的出發點,進一步的更好的組織用戶界面和數據模型。
實例如下:仍然從教程1的取款機界面出發,本節目的要實現這樣一個GUI:
不一樣的是,這次我們先設計Model類。這更符合思維的習慣,因為總是先有模型,再考慮如何設計一個用戶界面讓其和底層模型進行互動。
簡單回顧什麼是MVC模式
本節內容來自 MATLAB面向對象編程-從入門到設計模式第7.3節 MVC是Model-View-Controller的縮寫,仔細分析ATM界面的需求,程序可以被明顯地分成兩個部分。
第一部分可以歸結為模型(Model),Model反映程序的中心邏輯,在這裡很簡單,如果是存款,balance的計算方法為
balance = balance + inputn
如果是提款,則是
balance = balance - inputn
程序的第二部分可以歸結為視圖(View),顯示User Interface給用戶。View 的職責包括:
- 產生Figure和控制項對象,決定它們放在什麼位置,以及設置默認值。
- 把控制項和它們的回調函數聯合起來
可以看出,實際編程中,模型反映的是程序的邏輯,是相對穩定的,而視圖界面需要經常調整,而且界面的調整不應該影響到程序的模型。採用面向對象的思想,最顯然的做法是把界面和模型封裝到不同的類當中去,讓各個類各司其職。這叫做把界面的變化和模型解耦。還有一些和界面模型無關的功能,比如處理用戶的輸入。我們把它們歸到第三個類中去,叫做控制器(Controller)類。這樣用三個基本的類來組織整個GUI程序就是我們要介紹的模型–視圖–控制器(MVC)模式。
總的來說,MVC模式把GUI程序分解成三部分,如圖所示:
- 模型(Model):負責程序的內在邏輯。
- 視圖(View):負責構造,展示用戶界面。
- 控制器(Controller):負責處理用戶輸入。
對於一個用戶事件的響應,基本的流程如上。回調函數在Controller 中,首先由Controller中的回調函數作出第一輪的處理。 如果該響應需要涉及程序的內在邏輯,由Controller負責調用Model中的相關函數;如果Model中的某些內在狀態發生變化,還需要通知View 對象; View對象接到通知,查詢Model的內在狀態,並且在界面上作出更新,呈現給用戶。
先設計取款機的Model類
很明顯我們需要一個屬性叫做balance來記錄賬戶的餘額,它的初值將來構造函數的初始輸入。
classdef Model < handlen propertiesntbalance % 簡單起見,把balance設置成public屬性。n endn methodsntfunction obj = Model(balance)nt obj.balance = balance;ntend n endnendn
Model類必須提供方法,來允許外界來改變balance的值,於是需要分別給它添加deposit和withDraw兩個方法。最後再給model類定義一個事件,該event用來通知外界餘額變了,它通常用來提醒View更新自己的顯示。所以綜合起來,Model類看上去是這樣的:
classdef Model < handlen propertiesntbalancen endn eventsntbalanceChangedn endn methodsntfunction obj = Model(balance)nt obj.balance = balance;ntendntfunction deposit(obj,val)nt obj.balance = obj.balance + val;nt obj.notify(balanceChanged);ntendntfunction withDraw(obj,val)nt obj.balance = obj.balance - val;nt obj.notify(balanceChanged);ntendn end nendn
用App Designer布局View
參照教程1,用App Design設計布局得到的效果如下:
我們的最終目的是得到App Designer生成的,對應這個布局的面向對象的代碼,這可以通過點擊Code View然後把代碼拷貝出來得到,具體操作見教材1和教材2。
得到面向對象的代碼之後,我們還需要做如下的工作:
- 為了讓代碼更易讀懂,改變了一些屬性的名字,讓它們變得更有易讀,對應注釋 (1) 部分
- 按照MVC模式,View類必須擁有Controller和Model的對象handle, 我們給View添加兩個屬性,分別是controlObj,和modelObj,對應注釋(2)
- View對象將負責產生自己的controller對象,並且給自己的控制項註冊回調函數,這對應startupFcn中的注釋(3)部分
- 添加一個updateBalance方法,監聽model的balanceChanged事件,對應注釋(4),它用來刷新View的顯示
classdef View < matlab.apps.AppBasenn % Properties that correspond to app componentsn properties (Access = public)ntUIFigure matlab.ui.Figure % UI FigurentLabelNumericEditField matlab.ui.control.Label % BalancentviewBalance matlab.ui.control.NumericEditField % (1)ntLabelNumericEditField2 matlab.ui.control.Label % RMBntviewRMB matlab.ui.control.NumericEditField % (1)ntDepositButton matlab.ui.control.Button % (1)ntWithDrawButton matlab.ui.control.Button % (1)nntcontrolObj % (2) ntmodelObj % (2)n endnn methods (Access = private)ntfunction startupFcn(app)nt app.controlObj = Controller(app,app.modelObj); % (3)nt app.attatchToController(app.controlObj); % (3) nnt app.modelObj.addlistener(balanceChanged,@app.updateBalance); % (4)ntendn endn...n
接下來:
- 我們要實現attachToController方法,就是給兩個按鈕註冊ButtonPushed回調函數,對應注釋(5),按照MVC的規定,這些回調函數來自於controller
- 修改View的constructor,接受一個model對象做為參數,並且在內部用屬性modelobj保存下來,其餘不變,對應注釋(6)
- 最後實現updateBalance函數,直接從model出取得balance的值,更新自己的屬性(顯示),對應注釋(7)
% App initialization and constructionn methods (Access = private)ntfunction attatchToController(obj,controller) % (5)nt funcH = @controller.callback_withDrawButton;nt addlistener(obj.WithDrawButton,ButtonPushed,funcH)nnt funcH = @controller.callback_depositButton;nt addlistener(obj.DepositButton,ButtonPushed,funcH) ntendnnt% Create UIFigure and componentsntfunction createComponents(app)nnt % ... createComponents方法不變nntendn endnn methods (Access = public)nntfunction app = View(modelObj) % (6) nt app.modelObj = modelObj; % (6) nt createComponents(app)nt registerApp(app, app.UIFigure)nt runStartupFcn(app, @startupFcn)nt % register callback nt if nargout == 0nttclear appnt endntendnnntfunction updateBalance(obj,src,data) % (7) nt obj.viewBalance.Value = obj.modelObj.balance;ntendnnt% ... nn endnendn
給View添加Controller
最後一個步驟是通過controller把Model和View聯繫起來. 類定義如下.主要功能是把用戶對界面上的按鈕的點擊,轉換成對model類的withDraw和deposit方法的調用。
classdef Controller < handle n propertiesn viewObj;n modelObj;n endn methodsn function obj = Controller(viewObj,modelObj)nt obj.viewObj = viewObj;nt obj.modelObj = modelObj;n endn function callback_withDrawButton(obj,src,event)nt obj.modelObj.withDraw(obj.viewObj.viewRMB.Value); n endn function callback_depositButton(obj,src,event)nt obj.modelObj.deposit(obj.viewObj.viewRMB.Value); n endn end nendn
啟動MVC
最後我們需要一個腳本,按順序的初始化各個MVC的對象,來啟動這個GUI:
modelObj = Model(800);nviewObj = View(modelObj);n
注意我們首先聲明的Model對象,賬戶餘額800。然後再把它當做iew對象構造函數的輸入,在View對象的內部,生成了Controller對象。
App Designer + MVC 流程回顧
這裡再回顧一下為什麼要使用App Designer來布局。 在有App Designer之前,用戶可以通過GUIDE布局,得到的非面向對象的代碼,這樣的代碼和MVC模式幾乎沒有任何的兼容性,也就是說,GUIDE和面向對象的MVC模式無法同時使用,GUIDE不適用於中型大型規模的GUI設計。
在 MATLAB面向對象編程-從入門到設計模式中,我從如何程序化的構造GUI出發,介紹了MVC模式,並且提出,在面向對象GUI編程中,可以使用GUI Layout Manager來程序化的布局。讀者可以把GUI Layout Manager想像成一個透明的GUI框架,視覺上GUI Layout 提供的容器不會在最終的GUI上有任何的效果,一旦我們定義出了這個透明的框架,它幫我們自動擺放所有控制項。但這個方法我們仍然需要自己定義GUI Layout Manager中的各種容器,並且指定它們之間的包含關係。如下
現在有了App Designer,搭建GUI框架的工作也就可以省下來了,並且我們可以更加自由的擺放和對齊控制項,設置控制項的大小,設計完畢之後,通過Code View直接把代碼拷貝出來就得到到了View類的基本定義了,這是GUI設計工作流程中很大的一個進步。
推薦閱讀:
※MATLAB+seconds(1)
※MATLAB 高級數據結構連載 3:金融時間序列Financial Time Series (Part C) 跟蹤股票賬戶權益的變動
※The Trinity: MATLAB Mobile, MATLAB Online, and MATLAB Drive
※MATLAB神經網路(一):BP神經網路