其實微軟當年的MFC更接近于“代碼描述界面”,Delphi等反而是通過專門的配置文件.DFM來描述窗體……
比如,這個來自CSDN的截圖就是一個典型.DFM文件內容:
Delphi項目構成之窗體文件(Form Files).DFM_weixin_33904756的博客-CSDN博客
事實上,這并不應該叫“代碼描述界面”,而是“窗體布局獨立于程序設計”。
把窗體布局獨立出來有極多好處。
1、集中管理,方便用程序自動維護,從而可以“所見即所得”。
相比之下,MFC經常一開始還是可以用窗體編輯器維護界面的;但做著做著……代碼中到處改吧。
反之,如果窗體編輯器可以一直保持跟蹤,而且允許你隨時隨地的想改代碼改代碼、想改布局改布局,會不會好很多?
但,如果代碼和布局混合,實現起來就太麻煩了……
尤其是,當時的各種編程語言并不普遍支持現在爛大街的嵌套聲明類/函數,也大多沒有閉包、lambda等概念。這就使得代碼結構天然和窗體結構不同;這種狀況下,想要維護窗體之間的關系,實在太難。
2、窗體布局和程序設計脫耦。
于是,設計窗體的可以專心設計窗體、修改視覺表現;寫程序的專注程序邏輯——將來只要按照約定,比如文本框輸入框名字吻合,程序就能正常運行。
3、窗體布局文件獨立。
更進一步的,還可以把窗體布局文件都獨立出來、甚至在運行時加載——這就支持了“換膚”。
事實上,只要你懂開發、只要你的應用不是太小太垃圾,哪怕僅僅“脫耦”一個好處,就值得你投資這項技術了。
因此,并不僅僅是微軟,業界絕大多數的GUI框架,從微軟的WPF到Qt的QML、GTK的Glade XML,以及Android早期的界面描述文件,全都是獨立的。而且大都采用了XML格式。
早期的確有一些采用了文本格式,如ini或其他格式。但做過界面的都知道,這玩意兒就是一層套一層來回套;可ini或者別的格式,包括json,想要清晰的表達這種嵌套關系,那就麻煩的很了。
而XML格式雖然繁瑣,但它的格式足夠規范,拿來表示這種嵌套關系,那是簡單明了、一目了然。
那么,既然XML布局文件這么好,現在為何又回歸了呢?
1、一直沒有XML布局標準
這就使得,每一種編程語言、每一套GUI庫,就有一套自己的XML格式約定。
它們自帶的XML布局圖形編輯器……只能說是差強人意。
簡單說,你不可能不借助Photoshop/gimp之類東西,就搞定一個漂亮的界面;然后,哪怕已經有了photoshop做的漂亮界面圖片,你要把它做成符合要求的、可縮放的、能夠直接放程序中使用的XML布局描述……慢慢學習吧。
遺憾的是,絕大多數的界面設計師并不會編程。讓他學會使用當前團隊使用的某種語言的界面布局編輯器,這是個很難的任務……
換句話說,最終,界面布局還是得程序員做,很難做到“UI設計師和程序員分工合作,一個提供XML布局文件、一個專注程序設計”。
如果有XML界面布局標準規范,那么或許Photoshop或別的公司不介意做一個支持xml布局的、標準的圖形工具;UI設計師只需學會使用這個工具,就可以和任何團隊配合無間。
很遺憾。做不到。他必須為配合每個團隊從頭學習不同的界面設計工具,而這顯然是不可能的——尤其是,他還得保持不同版本界面中輸入框/文本框之類組件名稱的對應關系,哪怕差一個字符都不行。
很遺憾,和被迫染上了“變量名/函數名必須規范”強迫癥的程序員不同,UI設計師真沒有這個習慣。
2、編譯器設計水平提升
和早期C++標準難產多年不同,現在編譯器設計已經走向成熟。
IDE中,你敲一個字符,它做一遍語法分析成為可能;同時,編程語言的發展,使得嵌套聲明被普遍支持——于是,窗體布局和代碼結構就很自然的對應起來了。
你看,不需要把布局文件獨立出來,一樣可以實現“所見即所得”。
如此一來,和xml布局強迫性的“文件和代碼相分離”相比,反而更清晰、更方便也更靈活了。
事實上,無論是微軟的WPF還是Qt、GTK或者其他,它們都允許程序員直接用程序生成窗體。
而且,這些GUI庫本身其實也是按照XML里面的聲明、自動調用API生成了窗體。
既然編譯器已經可以支持,既然UI設計師和程序邏輯分頭各自開發并不現實——那么,為何不合并起來呢?
程序員直接用代碼生成窗體——或者,UI編輯器直接拖放產生代碼(而不是先生成XML、后把XML轉換成代碼)——不再需要一會兒看看XML、一會兒看看代碼邏輯、生怕一個名字敲錯引用了錯誤控件……這會不會更方便一些?
3、做出一個好的抽象,極難
總之,一切的核心,就是方便做事。怎么方便怎么來。
而不同的階段、不同的技術、不同的計算能力,決定了具體的實現思路可行與否、難度/復雜度如何。
這也正是人們發明了無數種編程語言、試圖從根本上解決某些工程難題的原因——如果你真的深入了解過、寫過相關領域的典型應用,就會知道,那些看起來不起眼的“語言基礎設施”,的的確確會改變工程思路、影響開發難度。
當然,這種“每種語言都有一技之長”的狀況,只在二十年前明顯;之后,隨著計算能力的提升、編譯器設計水平提高,幾乎所有語言都相互汲取了其他語言的長處。
其中的典型,就是C++這種“特性泛濫”的語言。
這使得之后很多年里,除了性能和開發難度,所有語言看起來就沒什么差別了。
畢竟大家互相抄,你有我有全都有。
但這種泛濫的特性其實只是一種堆砌。堆砌的極致就是C++,以至于后來業界的共識就是禁止在一個不大的項目里同時使用所有C++特性,而是根據業務范圍選擇其中部分子集,以免復雜度失控。
xml布局其實也是一種“所有語言都在堆砌”的特性;它和語言的結合并不好,兩張皮,這帶來了額外的復雜性、且很難達到宣稱的效果。
kotlin其實是一種嘗試,嘗試把“界面布局”直接整合于語言中——這個特性不再是和其他部分毫無瓜葛的、可以剝離的特性,而是和語言其他成分水乳交融的一部分。
說的更抽象點:過去,各種特性的堆砌其實是“非正交”的,不同特性要么格格不入、誰都不礙誰;要么一旦產生了交互作用,那么你就必須動用類似placement new這樣的“非常手段”——也就是封裝發生了泄露。
而更理想的設計是,語言中的每一個特性都是獨立的、可以和其他特性自然互動的。不至于兩張皮一樣。
比如,過去,你在程序中create了一個view,但沒人知道、其他組件也無法和它自然交互。除非你主動通過某種奇怪的機制把這個view呈現出來,否則在程序執行到這個位置之前,這個view就不可見。也就是這個view和程序其他組件之間,是缺乏聯系的,非正交的。你必須通過某些“泄露封裝”的東西,把它顯式的添加到某個窗口,否則這個view甚至都無法正常交互;不僅如此,你還需要通過獨立的布局文件把它單獨列出來,否則就無法可視化的編輯它。
而真正正交的、相容的設計里,你create一個view,這個view馬上就可以呈現在可視化編輯器里;而且,它也可以很自然的被其他窗口/view使用——不需要復雜的、啰嗦的、奇特的語法或者布局文件幫助,而是簡單的把它賦給另一個窗口/view中的某個變量,馬上就能看到最終的效果了。
當然,作為一個用過很多蹩腳庫的人,我知道很多這種設計只是取巧——其中的重災區就是微軟。
這種取巧,使得你在做一些簡單、常見的工作時方便快捷;卻會給稍微復雜一些工程帶來巨大的麻煩、表現出庫設計者的思維缺陷——它只能解決表面問題,看起來“很好很強大”,但根本就不是深思熟慮的結果。
嗯,舉一個容易理解的典型案例吧:win10早期的網絡控制面板隱藏了ip、子網掩碼、網關、DNS等等配置信息;你想解決網絡問題,它會告訴你“正在聯網尋求解決方案”——網絡不通了,它去聯網尋找解決方案!
這就是典型的“看起來很美”的封裝——對小白來說,現在你不需要自己手動配置ip、網關、dns了,微軟可以自動幫你搞定,爽吧?
但當實際環境出現意料之外的問題時(對我來說,是win10的驅動無法正確驅動我的筆記本網卡),對專業人員來說,看一眼ip、gateway、dns配置,故障在哪就了解的七七八八了。
可為了避免“嚇到小白”,它把這些信息藏了。藏在九曲十八彎的很多個分支之后;而一旦你選錯了分支,它就“聯網尋求解決方案”——鎖死窗口,關都關不掉,一定要耗上若干分鐘!
這就為專業人員尋找問題根源、徹底解決問題人為制造了極大的麻煩。
這個狀況的根本原因是:它根本就沒有完成“網絡配置”這個任務的全部封裝工作、做不到“自動解決一切問題”同時又“不掩蓋一切必要的信息”;卻又裝的好像自己把問題解決了一樣、不留“窺探內部”的機會(或者把窺探內部做的復雜而曲折)——換句話說,就是相關業務的所有邏輯回路尚未閉合,因此,這種方案能解決80%的問題,但卻使得另外20%的問題更加的難以解決、甚至徹底無法解決。
很顯然,“完美的封裝”要求對相關領域的“完美掌控”;要對這個領域的一切業務完美支持——無論是簡單的、小白的業務,還是復雜的、專業的業務,都要用同一個思路一攬子解決,都要一視同仁的提供便利。
而這,并不是微軟的長項。
就好像MFC時代,微軟就率先給出了一個很爛但能用的、被人譏諷為“沒有封裝”的“封裝”一樣。
不過,同時代,能給出漂亮封裝的并不多,也就Borland做的最成功。
在無數次失敗之后,業界才找到了MVC這個近乎完美的架構。
所以,我一點都不奇怪微軟不去引領架構設計/編程語言發展的方向。它并不擅長這個。
至于google……這個倒是符合它的一貫形象;但能否成功,還得看實踐效果——看它能不能在開發任意復雜的程序時、仍然保持界面開發的簡潔性和直觀性。