標籤:

重構是永遠都無法避免的

GacUI的大部分功能已經做完了,現在正在完善工具鏈。在把GacGen.exe做得更好的過程中,遇到的一個問題就是,報告錯誤的時候如何指出錯誤的地方在XML文件裡面的位置。其實聽起來好像也沒什麼,但是這裡面有兩個事情把報告位置變得困難了。

第一個是GacUI的XML資源支持類似CSS那樣的精神的東西,那麼當你的樣式匹配到了一個錯誤的節點上,從而錯誤地操作了對象的屬性的時候,我不僅要告訴你展開樣式的地點(界面文件里),我還得指出到底是那個地方弄錯了(樣式文件里)。因此每次展開節點的時候,我得把構造節點的各種信息的來源位置鋪滿在整棵樹裡面。這個問題已經解決了,就是苦力已點,到處都要處理位置信息。

第二個是GacUI的XML資源允許你寫 Workflow腳本。這個腳本的種類有很多,譬如說就是完整的腳本文件聲明了一些庫函數,譬如說屬性的data binding的表達式,譬如說控制項的事件的處理函數,甚至是分散在XML裡面的各種名字啊。所有的這些信息最終會合成為一個完整的腳本文件拿去編譯。錯誤的XML當然會生成錯誤的腳本,但是有些東西是沒有辦法在生成前就檢查的,所以勢必生成後的腳本也可能會產生編譯錯誤。那這個時候我如何從腳本的編譯錯誤的位置信息(在這裡是AST的指針)給你還原成XML文件裡面的位置信息呢?

其實實現起來也不難,就是你在生成的腳本的時候,每一個AST上面都掛一些東西就好了。但是實際操作起來特別苦力,這次苦力已經苦力到讓我不想寫了,但是東西又不能不做,所以只能從 Vlpp項目裡面提供的Parser生成器 (概念上等同於ANTLR)開始改,讓它幫我生成Parser的時候,順便按需生成大量的苦力代碼,到時候直接用就好了。

因此,為了報告錯誤信息,我就得去改編譯器的前端生成器,就不得不繼續做一個中等規模的重構了。添加新功能覺得很痛苦,就是一個需要重構的信號。而如何才能安心重構呢?這就需要你有單元測試。在一個項目裡面,這些東西一環扣一環,少了哪一個,你都會在需求變更的時候覺得很蛋疼。

Workflow腳本的定位其實是C++的一個語法糖。我還是建議大多數東西用C++來做,最後通過Workflow腳本做膠水,通過MVVM完美粘進GUI裡面。這樣寫GUI的時候可以直面GUI的概念(包括狀態機和數據綁定等),實現邏輯的時候可以依賴C++的所有優點,東西做起來就更容易了。

在這裡預告一下,因為這次要重構前端,順便改Workflow的編譯器,我決定把coroutine的工作往前提。在 TODO.md 的下半部分有一個我寫了差不多的「草案」,講的就是如何在Workflow裡面添加coroutine的語法,於是就可以完成C#的yield return和async await(跟kotlin一樣可以讓你自己寫庫來實現),但是在我這是做成stackless的。除此之外,為了更加方便地實現UI的邏輯,這次coroutine也可以把一個狀態機描述成一個介面的實現,切換狀態的時候,個個邊的輸入其實就是對介面的成員函數的調用。這麼做之後,MVVM威力將會猛增,配合await甚至可以綁定到一個伺服器端的工作流(就像當年WPF、WCF和WF一起用的時候那樣)。最後coroutine還會提供對動畫的支持。

當然這麼說好像聽起來很複雜,但是其實這是一個非常簡單的東西,給一個語言添加好用的coroutine,不需要多少知識就可以做到。我已經跟 @RednaxelaFX 說好了,等coroutine寫完了,我就寫一篇教程發到他的專欄裡面去。標題我也想好了,就叫《考不上三本也能讀懂系列——如何實現coroutine》,文章裡面將盡量只依賴於編程常識和不超過高中的數學知識(逃

推薦閱讀:

代碼的版本越高越難以被讀懂,是一種必然嗎?
給一個巨大的繼承體系的基類增加新的屬性,如何降低重構強度?
這個例子中的if else也要重構掉嗎?
為什麼軟體開發需要重構?

TAG:GacLib | 重构 | CC |