再談關注分離, 基於版本控制的設計

    前段時間寫了一篇關於關注分離的博客。很巧的是這段時間我們需要在項目裏增加一個新特性,又提到了關注分離。背景是這樣的:我們有一個ConfigManager類,這個類的職責是讀取和寫入配置文件。當業務邏輯有配置文件讀寫要求時,直接調用ConfigManager即可,使得配置文件對業務邏輯完全透明。當時在設計ConfigManager類的時候考慮到了配置文件的格式有可能在將來會改變,實現可能會有bug,所以加入了配置文件的版本號。兩週前,我們發現這個ConfigManager類在遇到某類配置時會拋exception,於是提交了一個patch修這個bug,並將版本號升到1.1。可是提交這個patch不久,我們又發現在某個特殊情況會引發另一個exception,不得不再提交一個patch並升級到1.2。於此同時我們遇到了一個無法迴避的兼容性問題:在安裝,卸載軟件時,我們通常是不會刪除配置信息的。也就是說客戶本來安裝了1.0的軟件,當升級到1.1時,軟件的版本號升級了,但是配置信息還是1.0的。在只有兩三個版本信息的時候這件事還是很好辦的,我們只需要每次讀取配置文件的時候檢查版本號,如果不是最新的版本就升級到最新。但是如果版本很多,並且跨越時間又很長時,這就是個很嚴重的問題了: 首先需要講以往所有的配置文件處理搞明白,然後將其升級到最新。舉個極端情況,如果已經升級過100次了, 第101次不得不把前面所有的版本實現複習一遍,找出不同,然後升級。我們想到了版本控制。仔細考慮這個問題我們會發現做升級的時候我們只需要循環實現將n-1次版本升級到下一個版本,直到最新。這樣一來每次只需要考慮一次升級。這裏還要考慮一個小問題:這種漸進式的升級並不是每次都能奏效,舉個簡單的例子: v1 -> v2 -> v3。 v1升級到v2會使信息丟失,這樣從v2到v3的升級就會導致丟失的信息無法復原。所以我們需要有一種機制做到v1->v3的升級。把這個分析用僞代碼寫出來應該是這樣的:

    Version Upgrade(Version currentVersion)

    {

           Version version = currentVersion;

           While( IsLatestVersion( version ) == false)

           {

                  Version nextVersion = version.Next;

                  while( IsStable( nextVersion) == false) nextVersion = nextVersion.Next;

                  version = nextVersion.UpgradeFrom( version );

           }


           return version;

    }

最外層的Upgrade函數從來漸進式的控制版本的升級。 IsLatestVersion用來判斷是否已經是最新的版本了,IsStable用來判斷該版本是否穩定(處理v1->v3這種情況)。每一個穩定版本有一個UpgradeFrom函數,這個函數纔是真正做升級用。

  回過頭來看這個升級過程,我們會發現實際上它和ConfigManager並沒有多大關係。兩者之間的紐帶只是那個配置文件中的版本號。所以,我們應該把版本控制的部分從配置類裏抽出來,單獨實現VersioinController類。這樣任何含有版本信息的邏輯都可以通過這個VersionController類統一升級。現在我們已經把版本控制從配置類中剝離出來了。我們接下來繼續研究如何實現這個版本控制類。

    如果只考慮升級過程,不考慮降級(downgrade),我們不難發現這是一個有向無環圖的操作,每一個圖結點可以有k個入邊, 但只有一個出邊。 即任意一個版本只能升級到一個新的版本,但是可以從k個之前的版本升級過來。如下圖所示。


       v1 -----> v2      v3 ----------> v4 ------> v5

                       |                            ^

                       |                            |

                      +---------------------+

現在我們又將問題從討論如何進行版本升級轉化爲: 處理圖結構 + 版本升級。將責任進一步的剝離。

如下圖所示:

VersionController保存了圖結點集合(graphNodes),每個圖結點的content域保存了Version信息。

Register函數用來生成這張有項無環圖。Upgrade函數用來升級Version。但是和上面實現有些不同,這裏遍歷圖結點的過程是基於Node,一旦到達某一個結點,會調用該結點的Visit來處理,在Visit內部調用Version的Upgrade。


   總結:通過關注分離,我們將原來配置文件裏一個bug的修復轉化爲實現一個版本控制類,在改類的實現中,我們又進一步劃分爲圖操作和版本升級。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章