百萬行WPF項目代碼重構記錄分析

此前帶領小組成員主導過一個百萬行代碼上位機項目的重構工作,分析項目中存在的問題做瞭些針對性的優化,整個重構工作持續瞭一年半之久。

主要針對以下問題:

1.產品型號太多導致代碼工程的分支太多,維護時會產生非常多的重復性的工作。

這是一個歷史遺留問題,公司成立之初的開發人員在開發時沒有考慮到後期其他機型的合並而留有餘地,後面增加其他機型而導致的代碼差異,是直接通過創建工程的分支來進行維護。兩個不同機型之間可能大部分的業務邏輯都相同而隻有少部分的界面和業務存在差異性,當修改一個共有的bug時,需要在所有分支上面都修改一遍。

同時有可能因為分支代碼的改動,而無法直接進行代碼合並,進而導致重復工作量的增加。如果分支不多的時候倒還好,而當機型越來越多,分支越來越多,這個重復性的工作會消耗相當大的時間和精力。所以合並所有機型的代碼,實現上位機代碼在不同機型上的通用性勢在必行。

一個良好的軟件架構應當是 面向接口編程,而不是面向實現編程。對於不同機型中的業務和流程在主體上是一樣的,隻是其中某些細節存在差異性的分支,所以我們需要將業務代碼提煉出主體流程和對應的接口,通過這些業務接口來實現機型差異所帶來的的業務流程上的差異和分支,而非工程上的分支。

在軟件運行時,可以通過相應的配置來決定業務接口的具體實現是哪一個,同時由於不同機型的接口實現是分離的,修改一個機型不會影響到另一個機型的代碼。當然,在修改主流程代碼時候就要小心瞭,需要考慮這一個改動對所有機型的影響。除非是非常有把握的情況下直接改動,否則還是先抽象出接口保證原主流程代碼不變,隻修改你需要修改的實現。

2.配置零散,沒有統一的管理機制,不利於打包。

軟件中的配置數據保存的地方太零散,有保存在數據庫的,有保存在txt文件的,有保存在註冊表的,有保存在app.config的。經過不同開發人員的不斷迭代,積累瞭很多無用的配置數據,並且沒有人敢刪除。而售後有時候為瞭查找修改某個配置需要在各個地方查找,非常繁瑣。

同時也因為公司機型太多導致很多配置數據在不同機型上存在差異性,這些差異性有可能是來自硬件差異,也有可能是來自軟件功能上的差異。而每發佈一個版本,就有可能需要同時打包多個機型的軟件包,每一個軟件包至少有1個G 的大小。而隨著機型的越來越多,一個版本不同軟件包也會越來越多,這對於打包人員來說是個不小的負擔,同時也要求打包人員需要明確的知道每一個機型配置上的差異性來保證軟件包的正確性。

為瞭解決這個問題,我們花瞭數個月的時間,對所有機型保存在不同位置的配置做瞭一個整理。我們整理出瞭所有機型通用的配置,統一保存在數據庫表中,同時為每一個機型建立數據庫表用來保存存在差異的配置數據。為瞭方便打包人員和售後管理查看這些數據庫,我們開發瞭一個配置管理工具用來專門查看和修改配置。

另外,我們為瞭解決多個軟件包的問題,把所有硬件相關的配置整合進一個文件中,並開發出一個版本升級軟件。在這個版本升級軟件中,售後可以選擇機型對應的硬件,升級程序可以通過所選硬件對應的配置寫入到數據庫中來實現同一個軟件包不同機型的升級工作。

同時我們開發瞭通用的http接口給上層C#程序和下層C++程序使用,用於讀寫數據庫的配置數據。

由於我們的設備有多個PC,並且在醫院內部無法連接外網,此前軟件升級時每個PC都需要售後人員拷貝軟件包並手動調用升級腳本來完成升級。而現在,我們的升級程序可以通過遠程調用的方式來同時完成多個PC的升級工作,做到瞭一鍵升級功能。

基於第一點的軟件代碼合並,在本次配置優化之後,打包人員每次打包僅需要一個軟件包,即可實現不同機型的一鍵升級,省時又省力。

3.UI和業務邏輯混雜

項目以WPF為主,整體使用MVVM框架。項目中沒有使用開源的控件庫,其中含有非常多的高度自定義控件的開發,這些控件的UI表現代碼和業務邏輯代碼夾雜在一起,耦合性太高非常不利於理解業務代碼和後期維護。

WPF的MVVM框架本身最大的優勢就是為瞭分離業務和UI,降低耦合性,提高可重用性,所以這個項目並沒有發揮出MVVM 框架的優勢。針對這個問題,我們分離UI和業務,提煉出一個單純的UI庫,這個UI庫不包含任何的業務代碼,除瞭.Net Framework的依賴庫之外,不依賴項目中的任何其他庫。

每一個UI控件暴露自定義的依賴屬性,在業務邏輯調用時,通過綁定這些依賴屬性來改變UI的表現邏輯。這樣做的好處是完全分離瞭UI表現代碼和業務邏輯代碼,並且這個UI庫具有高重用性,可以交給其他項目使用,甚至可以直接開源出來。

而且在後期維護時,UI代碼的改動與業務代碼的改動互不幹擾,也有利於Bug的排查。

4.由於前期開發人員的層次不一,並且沒有CodeReview機制來保證代碼質量,導致代碼存在很多低級錯誤。

項目中存在大量重復代碼,明明可以提煉出一個簡單方便的方法,偏偏要在各個地方不斷的Copy相同的代碼。

明明隻要增加一個參數就可以合並成一個方法,偏偏要寫上幾十個方法,諸如xxxx1,xxxx2….xxxx30,三十幾個方法執行同樣的功能,隻有一個參數上的差異。我們的業績考核又不是看代碼量,這種代碼看瞭讓人啼笑皆非。

沒有統一的命名規則,有些屬性首字母小寫,C#和C++風格混用,有些命名直接使用縮寫,類似“ggr”這樣縮寫,除瞭作者誰能看懂這是什麼意思?

一個類,一個方法代碼過多,幾千行一個類的文件不在少數,一個類承擔的功能也過多也過餘復雜。類和方法都應該遵循職責單一原則,不過在實際開發過程中單一原則不太好把控,但應該合理的控制代碼行數。

我們在項目重構工作之前,制定瞭統一的命名風格,並嚴格限制瞭每個類每個方法的代碼行數。一個文件,一個類,最多不超過一千行,一個方法最多不超過六十行。

六十行代碼一個屏幕很難放下,這個要求其實比較低瞭,最合理的應該是三四十行,一個屏幕正好可以看完一個方法的所有代碼。

在項目重構的過程中,我們規定所有人提交的代碼都必須提交給高級工程師CodeReview,高級工程師之間互相CodeReview。每個人的知識面都是有限的,但可以通過合作來達到無限的廣度和深度,我們應該盡量去避免犯一些低級的、顯而易見的錯誤。

5.軟件需要顯示處理大量的圖片,導致程序內存和CPU占用過大。

由於我們的軟件需要處理顯示大量的Dicom文件,並且對這些展示的圖片都有較為復雜的操作,諸如旋轉,放大、像素提取、銳化等,這些功能都集成在一個ImageControl中。然而由於我們的ImageControl寫的不合理,讀取一張500K的Dicom並顯示會至少占用2M的內存。如果同時讀取上百個Dicom文件,程序內存可以輕輕松松突破1個G,同時由於我們系統中不止一個程序需要處理這些圖片,所以對系統的內存要求非常高。

在前期,我們隻能不斷的累加硬件,把內存擴展到瞭32G,甚至是64G,然而這隻是飲鴆止渴的錯誤方式,應該從根本上解決ImageControl控件占用內存過大的問題,同時應該優化程序顯示排列圖片的邏輯。

在考慮到項目人員的分配情況和項目計劃,我們決定優化ImageControl這個思路暫緩,因為這個工作量會更大。我們決定先著手優化程序展示圖片的邏輯。程序隻加載用戶能夠看到的Dicom,當用戶下拉進度條時,再陸續加載可見的圖片,並且將其餘不可見的ImageControl銷毀,優化之後程序占用的內存銳減60%-70%,可以接受。

以上就是一次百萬行WPF項目代碼的重構記錄的詳細內容,更多關於WPF項目代碼重構的資料請關註WalkonNet其它相關文章!

推薦閱讀: