一次關於用MVC改進GUI應用開發的討論

2021-04-01 07:10:58 字數 3667 閱讀 1318

昨天臨下班前跟獵手討論了乙個技術問題。今天令狐看了,指出這個解決方法治標不治本,屬於頭痛醫頭腳痛醫腳的解決方案:

但要是你直接取parent的activecontrol,這個窗體不嵌入其他窗體的時候不是又錯了?換句話說,這個窗體跟它的使用環境發生了耦合。有沒有比較好的辦法來解決這個問題?

我說了三個方法,前兩個都不算是通用的辦法,就不說了。第三個就是我在《雜而不精》一文裡我提到過的mvc模式。在這一點上,我和令狐達成一致。針對獵手這個問題使用mvc,可以把這部分功能從view剝離到control上去處理。

關於mvc,令狐有一段說明:

mvc的概念我的blog裡有提到。簡單的說,就是把介面、介面需要完成的功能、這些功能所要操作到的資料物件全部分離。這樣一來,比如你介面上有乙個按鈕和乙個選單完成同樣的功能,就只要呼叫功能類中同樣的方法即可,跟這個按鈕、選單所在的介面就無關了。

vcl中有乙個action的概念,但光有這個,支援mvc開發是遠遠不夠的。這也是我在《雜而不精》中說過,我一直在思考的問題,早就想寫乙個文章來說的,不過感覺想法還不夠成熟。今天既然拿來討論了,就先簡單空談一下。

rad讓人養成很多壞習慣。比如,即使有action這樣的,做出mvc也會不倫不類,v和c的耦合度會很高的,這很不好。我設想的是rad只做v的部分,將c部分完全剝離,這樣gui應用就會成為可測試的,用單元測試覆蓋c和m。

令狐指出其中可能存在的乙個問題:

vcl的問題我以前也想過,我覺得是因為vcl本身對mvc的支援就不夠好。因為vcl中,已經將c的東西融合到控制項本身去了,這樣你再想剝離就很困難。我以前在csdn上幫人寫過乙個mvc的演示,就碰到過這個問題。

舉個例子,比如tmemo的lines,儲存了memo中的內容,而這個lines是跟tmemo本身的操作又是相關的。我那時候是自己用乙個list去 記錄資料,但是這樣一來,就會造成lines和我自己那份資料的重複,而且有不同步的潛在可能。後來我的解決方法是完全放棄lines,每次我自己的 list更新之後,將lines清空重新設定。不過這樣對顯示效果又有影響。

但我認為還是可以剝離的,這跟vcl的關係不太大。我在最近寫的乙個blog備份程式的gui部分裡就嘗試用mvc的思想,感覺還可以。至於令狐上面的說的問題,也是可以解決的。

memo 的lines屬性是乙個tstrings *。在control裡維護乙個tstring * 成員指向memo的lines即可,由control直接去維護memo的lines,這樣就不存在一式兩份的問題。因為memo的lines屬性是不可 更改的,所以只能通過control去操作。不過程式中的其它**不能直接對memo的lines進行操作,而是要通過control進行。

把我的思路總結一下就是這樣:

我把v和c之間的關係簡化為三種:action, state, contraint

action代表對介面的操作,呼叫control的相應函式處理

state代表介面狀態的改變,由control去執行

constraint代表約束,即action和state的關聯,比如在某種狀態下不能出現某個action

這個constriant又有兩種,強約束和弱約束

弱約束可以在view中實現

強約束可能在control中實現,也可能在model中實現

有必要補充一下:其中關於constraint的想法,最早來自於去年與chechy討論他的乙個基於xml的框架時受到的啟發。

實際上弱約束的情況很少,它通常不能算作業務邏輯的一部分,為了簡單起見才把它放在view這邊實現。如令狐所說,弱約束差不多就是指控件disable或者invisible這種。不過有些disable/invisible可能還是要放到control裡的。

令狐作了一下歸納:

用 戶對介面元素進行乙個操作,發出乙個request,實際觸發乙個控制項事件,這個事件將會呼叫control,control繼續呼叫業務模組,業務模組 完成實際業務邏輯並返回,根據返回值,control做兩種可能的操作:轉向另乙個頁面,或改變當前頁面的某些狀態(或兩者兼具);而頁面上的控制項由 view類負責,view類接受到狀態改變後,對控制項實際的表現形式進行對應修改。

整個流程是:

view's event->process control->business control->model->model return->business control return->process cotrol's state change or open new view->view class(change form's control)

基本上就是這樣,這樣做還有乙個很大的好外就是,可以比較方便地更換view層。比如從pc移到pda,或移到web。

令狐補充了一點:

這 樣的話,view層由兩部分組成:form和view class,form負責視覺化、使用者事件接收,view class負責處理狀態的改變。然後,你所謂的強約束,可以幾乎肯定是做在process control或business control層;而弱約束,幾乎肯定是做在view class

如果考慮到view層的更換,是必須加入view class的,這是我開始沒有考慮到的,我原來設想中這部分功能是在control裡的。所以還是有很多不夠成熟的地方,需要再討論討論。最主要的是需要經過實踐的考驗才行。

令狐又提了兩個重要的問題:

1、windows的控制項有的時候是介面相應先於邏輯處理,比如checkbox,你一點它就直接改變狀態了,但是後端的邏輯處理可能又會重複修改它的狀態。

2、還有個更嚴重的問題,對某些控制項狀態或內容的修改,會觸發一些事件,這樣會造成一些事件的遞迴呼叫或control的難以控制

在傳統的web程式中,這個問題比較不明顯,因為你在view上做的操作都不會產生實際的影響,直到submit才會去伺服器進行mvc的流程,但是客戶端就沒這麼簡單了,每個操作都可能會這樣跑一圈

事實上,這兩個問題在傳統rad開發中都存在,特別是第二個問題尤其嚴重,並且都很難解決。我起初考慮在gui應用開發中採用mvc模式,很重要的乙個原因就是為了解決如上面的第二個問題。

對 於第乙個問題是這樣:當check時就會產生action,它呼叫到control這邊時就可以根據情況處理,如果知道這是乙個需要model做長時間處 理的,就可以先改變介面的狀態,比如把游標設定成hourglass,在model處理完成之後再更新checkbox狀態,並恢復游標。當然,最終的狀 態要以model的處理結果而定,由control去更新view的state。

而第二個問題的解決之道就在於所有的event都會轉為control的action請求,當第乙個請求到達control時,就會啟動相關的constraint。之後的請求就會根據constraint發生不同的作用——比如有些就會被遮蔽掉。

令狐又提出第三個技術問題:還有乙個問題就是環境上下文如何傳遞

這個問題在gui應用中還是比較好解決,但在web應用中就麻煩一些。在gui應用中,上下文可以直接或間接(通過指標指向view中的控制項屬性,如前面說的那個lines)記錄在control中。但因為web應用是無狀態的,所以需要額外的持久化機制。

令狐說:持久化機制在伺服器端利用資料庫、在客戶端利用cookie,都有辦法可以做到。

但實現方法肯定是與gui應用不同的,要麻煩一些。

我那個試點程式對上面說的這些沒有全部實現,只嘗試了一下v和c的分離,所以說我還要再考慮考慮。

我同意令狐說的:看看實現在討論比較實際,否則是空對空的。試著用上面的理論作個實現出來,如果能提公升到框架是最好不過了。就算只是個思想也是好的,不過要實現出來有些問題才好討論。

提公升到框架還是有難度的,我的目標是整理出一套切實可行的做法,以改進gui應用開發。

mvc 記一次「專案」的歷程

大二上半學期因為選修課的原因,答應幫老師完善學院的選課系統。在這之前沒有做過乙個可以成為 專案 的專案,本著挑戰自己的原則和可以不上選修課的福利,斷斷續續用了一學期的時間來完善這個選課系統。接受這個專案的時候,專案已經被乙個學姐做的差不多了,基本框架模型都有了。值得一提的是,學姐是現學現做,用的是我...

總結一次dubbo應用(一)

地點 北京海淀區 組織 乙個新成立的體育分公司 根據產品原型 階段性需求分析後考慮採用 dubbo 框架 同時與spring cloud 框架做過比較,spring cloud 雖是巨人,可惜爬上巨人的肩膀耗費時間比較長而且沒必要大材小用,本次只應用多端介面共用 隨捨棄spring cloud 採用...

一次關於sklearn crfsuite的安裝之旅

為了在windows上跑crf,我需要安裝sklearn crfsuite,最開始想到的是使用pycharm進行環境配置,裝上了sklearn crfsuite。跑起來,額,出錯了 正在訓練評估crf模型.traceback most recent call last file c users cc...