MFC和QT等UI框架的特点
UI設(shè)計(jì)的3大原則:
- 面向?qū)ο?#xff1b;
- MVC;
- 消息隊(duì)列驅(qū)動(dòng);
直到現(xiàn)在各個(gè)UI系統(tǒng),包括題主所提到的MFC、WPF、Qt,也包括其它,諸如Android SDK、Cocoa的構(gòu)建仍舊建立在這3大原則的基礎(chǔ)上。
要提到MFC,就不得不先提到Windows SDK,后者是隨Windows 1.0所提供的操作系統(tǒng)API。Windows 1.0在1985年發(fā)售,盡管在此之前已有施樂的Star、蘋果的Lisa和Mac OS這樣的圖形界面操作系統(tǒng),但Windows 1.0畢竟是第一個(gè)大規(guī)模發(fā)行的圖形操作系統(tǒng),需要直面各樣的開發(fā)者與普通用戶的問題,也算是“第一個(gè)吃螃蟹的”。既然是第一個(gè),當(dāng)然是不成熟的。這種不成熟即有當(dāng)時(shí)技術(shù)條件的限制,也有經(jīng)驗(yàn)上的匱乏,我用一個(gè)例子簡要說明一下。例如,我需要顯示一個(gè)窗口,Windows SDK看上去是這樣的:
int WinMain() {//1 register a window classWNDCLASS wndclass;wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WndProc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = NULL;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = NULL;wndclass.lpszClassName = "MainWndClass";RegisterClass(&wndclass);//2 create a winowHWND hwnd = CreateWindow("MainWndClass","Window Title",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);//3 message loopMSG msg;while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}return 0; }對,你沒有看錯(cuò),那個(gè)叫CreateWindow的函數(shù)的有11個(gè)參數(shù)!按照面向?qū)ο蟮挠^念與簡潔性的做法,它不應(yīng)該是這樣的嗎(一個(gè)Gtk的例子):
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_title(window, "window title"); gtk_window_set_position(window, GTK_WIN_POS_CENTER_ALWAYS); gtk_window_set_default_size(window, 400, 300); gtk_window_set_resizable(window, TRUE);原因是當(dāng)時(shí)的機(jī)器內(nèi)存太小,一個(gè)函數(shù)的鏈接符號要占有一個(gè)字長的地址空間,所以能省就省。這也是當(dāng)時(shí)諸多系統(tǒng)API設(shè)計(jì)的慣例,如之后蘋果的Carbon庫,參數(shù)多到慘不忍睹的函數(shù)也是比比皆是。這就是所謂的“技術(shù)限制”的一例。
此外,上例中第一步“注冊窗口類”和第三步“消息循環(huán)”有必要強(qiáng)制要求用戶來指明各個(gè)參數(shù)項(xiàng)嗎。從后來的實(shí)踐看,答案是沒有必要。但作為“第一個(gè)吃螃蟹的”,怎么會(huì)知道。這就是所謂的“經(jīng)驗(yàn)上的匱乏”一例。
作為后起之秀的Qt,同樣是顯示一個(gè)窗口,用戶代碼就簡潔得多:
int main(int argc, char *argv[]){//1 create application instanceQApplication app(argc, argv);//2 create a windowQWidget *window = new QWidget;window->setWindowTitle("title");window->show();//3 message loopreturn app.exec(); }正由于Windows SDK的不如意,于是就出現(xiàn)的MFC。
這里我想插一個(gè)題外話:為什么用戶終端系統(tǒng)(包括Windows、mac OS這樣的PC端,Android、iOS這樣的移動(dòng)平臺,也包括Play Station、xbox這樣的家用主機(jī)),所提供的的API和程序庫接口,編程語言都是類C(C、C++、ObjC、Java等)的。這主要是技術(shù)積累的緣故。早期探索用戶終端系統(tǒng)的開拓者,使用的C語言,先驅(qū)們在這一語言基礎(chǔ)上構(gòu)建起各種基礎(chǔ)設(shè)施,包括教育環(huán)境與人才儲(chǔ)備,項(xiàng)目管理的流程與經(jīng)驗(yàn),開發(fā)、分析、測試工具,建議做法與禁忌。作為后來者當(dāng)然不會(huì)舍棄這些基礎(chǔ)設(shè)施而另起爐灶從零開始,于是使用類C語言就成了傳統(tǒng)。可以預(yù)見:下一代的用戶終端與的程序庫接口,也是類C的編程語言。
我們回到MFC的話題。MFC只是對Windows SDK的封裝,按后世的看法,MFC不按“套路”出牌!包括作為同時(shí)代競爭產(chǎn)品的Borland公司的VCL,還有稍晚的Java awt,也是不按“套路”出招!這里我需要解釋下圖形庫構(gòu)建的“套路”是什么,讀者才能理解什么叫“不走套路”。
對于一個(gè)圖形界面的程序,大致可以分為3個(gè)層:
+----------------------+ | user application | +----------------------+ | ui framework | +----------------------+ | operation system api | +----------------------+最下面的是操作系統(tǒng)API,無論是UI庫還是用戶程序,要實(shí)現(xiàn)某功能最終都是要依賴于操作系統(tǒng)API的。關(guān)鍵點(diǎn)在于,操作系統(tǒng)需要提供多少數(shù)量的API函數(shù),才足以構(gòu)建一個(gè)圖形界面程序。答案是:很少。
首先,操作系統(tǒng)需要為UI框架和用戶程序提供一塊屏幕區(qū)域:
struct window *window_create();其次,這塊區(qū)域需要能夠接收用戶事件(這里以鼠標(biāo)點(diǎn)擊為例):
enum mouse_event {mouse_event_down,mouse_event_move,mouse_event_up };typedef void (*mouse_handler)(struct window *, mouse_event event, int x, int y);void window_set_mouse_handler(struct window *, mouse_handler handler);最后,UI框架與用戶程序需要在這塊區(qū)域繪制各種控件(下例是矩形):
struct context *context_get_form_window(struct window *);void context_set_color(struct context *, int rgba); void context_fill_rect(struct context *, int x, int y, int width, int height);只要有了這3類API,一個(gè)圖形界面程序就可以構(gòu)建出來了,即使如今復(fù)雜的3D圖像程序也是如此。這些作為基礎(chǔ)API的函數(shù)數(shù)量很少,往大的說不到一百個(gè),往小的說,十多個(gè)也能成事。龐大的UI框架,諸如Qt、Xamarin、WinForm,僅僅是以這幾十個(gè)操作系統(tǒng)API函數(shù)作為自己的基礎(chǔ);對操作系統(tǒng)極小的依賴,也是可移植的保障;也是UI框架構(gòu)建的“套路”。
所以說微軟的MFC、Borland的VCL不走“套路”,因?yàn)樗鼈兌际菍indows SDK的封裝,而不是選擇少量Windows SDK核心函數(shù)并在其上構(gòu)建自己的工作邏輯。因?yàn)閃indows SDK使用繁瑣,于是有了MFC,但MFC是對Windows SDK的直接封裝,雖然提升了易用性,但終究要沿用Windows SDK所定義的工作流程。當(dāng)然這種做法也是有好處的,例如工作量更小、用戶可以更方便地直接調(diào)用系統(tǒng)API。
說穿了,MFC(當(dāng)然也包括VCL)需要解決的是“有無”的問題。Windows 1.0這樣的圖形界面操作系統(tǒng)已經(jīng)開始在大眾領(lǐng)域普及,開發(fā)者也需要一套框架來開發(fā)圖形界面程序。MFC在上世紀(jì)90年代取得了巨大的商業(yè)成功,但以后世程序員的眼光看,MFC還是太稚氣了。我曾經(jīng)接觸過一些學(xué)習(xí)Windows程序開發(fā)的新人,他們對MFC充滿鄙夷,認(rèn)為MFC是上個(gè)世紀(jì)的遺留產(chǎn)物,它概念多、晦澀難懂、代碼量大、界面也丑,他們更傾向于Qt、WPF這樣的新秀。他們的結(jié)論是對的,MFC確實(shí)是上個(gè)世紀(jì)的遺留產(chǎn)物;但推導(dǎo)過程不對,MFC與Qt、WPF本質(zhì)上是一樣的東西,在MFC上遇到的問題,在使用Qt、WPF時(shí)極可能也會(huì)遇到,不過是早還是晚。只能說“MFC對新人極不友好”。
在MFC同時(shí)代,也正是上世紀(jì)80、90年代,出現(xiàn)了對后世UI框架設(shè)計(jì)有極大影響的技術(shù)方案:用web瀏覽器作為圖形界面程序的容器。由于篇幅的限制,我這里直接說結(jié)論:作為一個(gè)整體性的技術(shù)方案,它失敗了。失敗的原因可以大致歸為3點(diǎn):
- 其一是觀念上的落后。基于html、js、css的網(wǎng)頁技術(shù),最初的設(shè)計(jì)意圖是解決文檔瀏覽的問題,采用html為主,js、css相輔的設(shè)計(jì)方式。這樣的設(shè)計(jì)在界面表現(xiàn)上有優(yōu)勢,但也僅此而已,其它方面諸如程序生命周期控制、數(shù)據(jù)處理等就太糟糕了。
- 其二是基礎(chǔ)設(shè)施的不如意。相關(guān)的人才與技術(shù)儲(chǔ)備太欠缺,所能實(shí)現(xiàn)的產(chǎn)出物,無論在功能上還是性能上都無法與MFC、VCL這樣的傳統(tǒng)程序相同并論。
- 其三是技術(shù)開拓者的急功近利。當(dāng)時(shí)熱衷于以瀏覽器作為圖形程序容器的技術(shù)方案的廠商,如網(wǎng)景,還有后來的google和facebook,它們的意圖很明顯,就是挑戰(zhàn)微軟的平臺霸權(quán),建立有利于自身的生態(tài)環(huán)境(說人話就是:招攬開發(fā)者、提供生產(chǎn)工具、生產(chǎn)特定的程序、平臺吃流量)。充滿鼓動(dòng)性的價(jià)值觀營銷在具有叛逆屬性的年輕人中可以快速取得影響力,但得不到主流的支持和強(qiáng)勢技術(shù)團(tuán)隊(duì)的示范,一切都是空。
盡管在新世紀(jì)的第二個(gè)10年以web前端為技術(shù)基礎(chǔ)的UI框架,如electron、reactive native,取得了喜人的成果,但與上世紀(jì)的雛形相比,它們所在的大環(huán)境與技術(shù)本質(zhì)已有了根本的區(qū)別。
但失敗的方案并不代表沒有借鑒意義。本世紀(jì)初出現(xiàn)了一種被稱為“direct ui”的思潮,其中一大觀點(diǎn)就是程序啟動(dòng)時(shí)加載xml文件作為UI。眼亮的同仁一定會(huì)說:這不就是資源編輯器莫。對的,這就是現(xiàn)代意義的資源編輯器的雛形。另外也多有程序會(huì)在自身嵌入一個(gè)web容器來顯示特定內(nèi)容的UI,至今Android、iOS App上這樣的做法也相當(dāng)常見。出于篇幅的限制這里的技術(shù)因果就不展開了。
上世紀(jì)90年代末到本世紀(jì)初是UI庫百花齊放的年代,如今在PC領(lǐng)域,為人熟知的Qt、Gtk、wxWidgets都出現(xiàn)在這個(gè)時(shí)間段,這是與大環(huán)境有關(guān)。
- 首先MFC解決了“有無”的問題,接下來就是“好用難用”的問題;
- 其次由于前一個(gè)10年圖形系統(tǒng)的普及,業(yè)界在圖形程序的開發(fā)上有了大量的人才、技術(shù)、經(jīng)驗(yàn)上的積累,開發(fā)一套UI庫對于小團(tuán)隊(duì)來說也是敢想敢干的事;
- 再次定制化的需求開始出現(xiàn),是需要能實(shí)現(xiàn)快速開發(fā)的程序框架(WTL),還是可以實(shí)現(xiàn)跨平臺的程序框架(Qt)?是需要能實(shí)現(xiàn)常見功能的中小程序框架(wxWidgets),還是得安裝幾個(gè)G的包但可以實(shí)現(xiàn)復(fù)雜應(yīng)用的程序框架(WPF)?不同的團(tuán)隊(duì)面對不同的任務(wù),一定會(huì)有不同的選擇。
- 此外當(dāng)時(shí)微軟作為處于壟斷地位但又不思進(jìn)取的操作系統(tǒng)廠商,也有推波助瀾的作用。本世紀(jì)第一個(gè)10年,MFC已很老舊,但微軟卻一直沒有給出升級方案;微軟一味將開發(fā)者向.Net上推,但遷移成本太高,并且后者的性能問題也遲遲不能解決。于是開發(fā)者只能自己想辦法。
Qt是傳統(tǒng)PC領(lǐng)域程序框架集大成者,除了UI,它還提供數(shù)據(jù)庫、多媒體等功能,已經(jīng)不是單純的UI框架,而是一套完整的程序開發(fā)解決方案;wxWidgets就要輕量級一些;WPF則是微軟官方作為取代曾經(jīng)的MFC、后來的WinForm的替代方案。
我這里談一下跨平臺開發(fā)的一些經(jīng)驗(yàn)。問:使用跨平臺的程序框架開發(fā)程序,相比于每個(gè)目標(biāo)平臺單獨(dú)組建一個(gè)開發(fā)組,前者有更高的性價(jià)比?回答是:不一定。開發(fā)程序不僅僅是程序員的事,還涉及到產(chǎn)品策劃、美術(shù)、市場運(yùn)營、第三方技術(shù)供應(yīng)商等方方面面,具體問題要具體分析。事實(shí)上資金與人力充足的團(tuán)隊(duì),更傾向于不同目標(biāo)平臺組建各自的開發(fā)組。
新世紀(jì)的第二個(gè)10年,以iOS、Android主導(dǎo)的移動(dòng)平臺興起。Qt推出了移動(dòng)平臺版本,但終究沒有形成氣候;微軟推出UWP,WPF還是被舍棄了。這又是另一個(gè)話題了。
作者:wzsayiie
鏈接:https://www.zhihu.com/question/23480014/answer/658825482
來源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
總結(jié)
以上是生活随笔為你收集整理的MFC和QT等UI框架的特点的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker build Dockfil
- 下一篇: QT和MFC中怎么使用继承基类的自定义控