百萬行WPF項目代碼重構(gòu)記錄分析
此前帶領(lǐng)小組成員主導(dǎo)過一個百萬行代碼上位機(jī)項目的重構(gòu)工作,分析項目中存在的問題做了些針對性的優(yōu)化,整個重構(gòu)工作持續(xù)了一年半之久。
主要針對以下問題:
一 產(chǎn)品型號太多產(chǎn)生非常多重復(fù)性工作
產(chǎn)品型號太多導(dǎo)致代碼工程的分支太多,維護(hù)時會產(chǎn)生非常多的重復(fù)性的工作。
這是一個歷史遺留問題,公司成立之初的開發(fā)人員在開發(fā)時沒有考慮到后期其他機(jī)型的合并而留有余地,后面增加其他機(jī)型而導(dǎo)致的代碼差異,是直接通過創(chuàng)建工程的分支來進(jìn)行維護(hù)。兩個不同機(jī)型之間可能大部分的業(yè)務(wù)邏輯都相同而只有少部分的界面和業(yè)務(wù)存在差異性,當(dāng)修改一個共有的bug時,需要在所有分支上面都修改一遍。
同時有可能因?yàn)榉种Тa的改動,而無法直接進(jìn)行代碼合并,進(jìn)而導(dǎo)致重復(fù)工作量的增加。如果分支不多的時候倒還好,而當(dāng)機(jī)型越來越多,分支越來越多,這個重復(fù)性的工作會消耗相當(dāng)大的時間和精力。所以合并所有機(jī)型的代碼,實(shí)現(xiàn)上位機(jī)代碼在不同機(jī)型上的通用性勢在必行。
一個良好的軟件架構(gòu)應(yīng)當(dāng)是 面向接口編程,而不是面向?qū)崿F(xiàn)編程。對于不同機(jī)型中的業(yè)務(wù)和流程在主體上是一樣的,只是其中某些細(xì)節(jié)存在差異性的分支,所以我們需要將業(yè)務(wù)代碼提煉出主體流程和對應(yīng)的接口,通過這些業(yè)務(wù)接口來實(shí)現(xiàn)機(jī)型差異所帶來的的業(yè)務(wù)流程上的差異和分支,而非工程上的分支。
在軟件運(yùn)行時,可以通過相應(yīng)的配置來決定業(yè)務(wù)接口的具體實(shí)現(xiàn)是哪一個,同時由于不同機(jī)型的接口實(shí)現(xiàn)是分離的,修改一個機(jī)型不會影響到另一個機(jī)型的代碼。當(dāng)然,在修改主流程代碼時候就要小心了,需要考慮這一個改動對所有機(jī)型的影響。除非是非常有把握的情況下直接改動,否則還是先抽象出接口保證原主流程代碼不變,只修改你需要修改的實(shí)現(xiàn)。
二 配置零散,沒有統(tǒng)一的管理機(jī)制,不利于打包
軟件中的配置數(shù)據(jù)保存的地方太零散,有保存在數(shù)據(jù)庫的,有保存在txt文件的,有保存在注冊表的,有保存在app.config的。經(jīng)過不同開發(fā)人員的不斷迭代,積累了很多無用的配置數(shù)據(jù),并且沒有人敢刪除。而售后有時候?yàn)榱瞬檎倚薷哪硞€配置需要在各個地方查找,非常繁瑣。
同時也因?yàn)楣緳C(jī)型太多導(dǎo)致很多配置數(shù)據(jù)在不同機(jī)型上存在差異性,這些差異性有可能是來自硬件差異,也有可能是來自軟件功能上的差異。而每發(fā)布一個版本,就有可能需要同時打包多個機(jī)型的軟件包,每一個軟件包至少有1個G 的大小。而隨著機(jī)型的越來越多,一個版本不同軟件包也會越來越多,這對于打包人員來說是個不小的負(fù)擔(dān),同時也要求打包人員需要明確的知道每一個機(jī)型配置上的差異性來保證軟件包的正確性。
為了解決這個問題,我們花了數(shù)個月的時間,對所有機(jī)型保存在不同位置的配置做了一個整理。我們整理出了所有機(jī)型通用的配置,統(tǒng)一保存在數(shù)據(jù)庫表中,同時為每一個機(jī)型建立數(shù)據(jù)庫表用來保存存在差異的配置數(shù)據(jù)。為了方便打包人員和售后管理查看這些數(shù)據(jù)庫,我們開發(fā)了一個配置管理工具用來專門查看和修改配置。
另外,我們?yōu)榱私鉀Q多個軟件包的問題,把所有硬件相關(guān)的配置整合進(jìn)一個文件中,并開發(fā)出一個版本升級軟件。在這個版本升級軟件中,售后可以選擇機(jī)型對應(yīng)的硬件,升級程序可以通過所選硬件對應(yīng)的配置寫入到數(shù)據(jù)庫中來實(shí)現(xiàn)同一個軟件包不同機(jī)型的升級工作。
同時我們開發(fā)了通用的http接口給上層C#程序和下層C++程序使用,用于讀寫數(shù)據(jù)庫的配置數(shù)據(jù)。
由于我們的設(shè)備有多個PC,并且在醫(yī)院內(nèi)部無法連接外網(wǎng),此前軟件升級時每個PC都需要售后人員拷貝軟件包并手動調(diào)用升級腳本來完成升級。而現(xiàn)在,我們的升級程序可以通過遠(yuǎn)程調(diào)用的方式來同時完成多個PC的升級工作,做到了一鍵升級功能。
基于第一點(diǎn)的軟件代碼合并,在本次配置優(yōu)化之后,打包人員每次打包僅需要一個軟件包,即可實(shí)現(xiàn)不同機(jī)型的一鍵升級,省時又省力。
三 UI和業(yè)務(wù)邏輯混雜
項目以WPF為主,整體使用MVVM框架。項目中沒有使用開源的控件庫,其中含有非常多的高度自定義控件的開發(fā),這些控件的UI表現(xiàn)代碼和業(yè)務(wù)邏輯代碼夾雜在一起,耦合性太高非常不利于理解業(yè)務(wù)代碼和后期維護(hù)。
WPF的MVVM框架本身最大的優(yōu)勢就是為了分離業(yè)務(wù)和UI,降低耦合性,提高可重用性,所以這個項目并沒有發(fā)揮出MVVM 框架的優(yōu)勢。針對這個問題,我們分離UI和業(yè)務(wù),提煉出一個單純的UI庫,這個UI庫不包含任何的業(yè)務(wù)代碼,除了.Net Framework的依賴庫之外,不依賴項目中的任何其他庫。
每一個UI控件暴露自定義的依賴屬性,在業(yè)務(wù)邏輯調(diào)用時,通過綁定這些依賴屬性來改變UI的表現(xiàn)邏輯。這樣做的好處是完全分離了UI表現(xiàn)代碼和業(yè)務(wù)邏輯代碼,并且這個UI庫具有高重用性,可以交給其他項目使用,甚至可以直接開源出來。
而且在后期維護(hù)時,UI代碼的改動與業(yè)務(wù)代碼的改動互不干擾,也有利于Bug的排查。
四 開發(fā)人員的層次不一
由于前期開發(fā)人員的層次不一,并且沒有CodeReview機(jī)制來保證代碼質(zhì)量,導(dǎo)致代碼存在很多低級錯誤。
項目中存在大量重復(fù)代碼,明明可以提煉出一個簡單方便的方法,偏偏要在各個地方不斷的Copy相同的代碼。
明明只要增加一個參數(shù)就可以合并成一個方法,偏偏要寫上幾十個方法,諸如xxxx1,xxxx2....xxxx30,三十幾個方法執(zhí)行同樣的功能,只有一個參數(shù)上的差異。我們的業(yè)績考核又不是看代碼量,這種代碼看了讓人啼笑皆非。
沒有統(tǒng)一的命名規(guī)則,有些屬性首字母小寫,C#和C++風(fēng)格混用,有些命名直接使用縮寫,類似“ggr”這樣縮寫,除了作者誰能看懂這是什么意思?
一個類,一個方法代碼過多,幾千行一個類的文件不在少數(shù),一個類承擔(dān)的功能也過多也過余復(fù)雜。類和方法都應(yīng)該遵循職責(zé)單一原則,不過在實(shí)際開發(fā)過程中單一原則不太好把控,但應(yīng)該合理的控制代碼行數(shù)。
我們在項目重構(gòu)工作之前,制定了統(tǒng)一的命名風(fēng)格,并嚴(yán)格限制了每個類每個方法的代碼行數(shù)。一個文件,一個類,最多不超過一千行,一個方法最多不超過六十行。
六十行代碼一個屏幕很難放下,這個要求其實(shí)比較低了,最合理的應(yīng)該是三四十行,一個屏幕正好可以看完一個方法的所有代碼。
在項目重構(gòu)的過程中,我們規(guī)定所有人提交的代碼都必須提交給高級工程師CodeReview,高級工程師之間互相CodeReview。每個人的知識面都是有限的,但可以通過合作來達(dá)到無限的廣度和深度,我們應(yīng)該盡量去避免犯一些低級的、顯而易見的錯誤。
五 處理大量的圖片 內(nèi)存和CPU占用過大
5.軟件需要顯示處理大量的圖片,導(dǎo)致程序內(nèi)存和CPU占用過大。
由于我們的軟件需要處理顯示大量的Dicom文件,并且對這些展示的圖片都有較為復(fù)雜的操作,諸如旋轉(zhuǎn),放大、像素提取、銳化等,這些功能都集成在一個ImageControl中。然而由于我們的ImageControl寫的不合理,讀取一張500K的Dicom并顯示會至少占用2M的內(nèi)存。如果同時讀取上百個Dicom文件,程序內(nèi)存可以輕輕松松突破1個G,同時由于我們系統(tǒng)中不止一個程序需要處理這些圖片,所以對系統(tǒng)的內(nèi)存要求非常高。
在前期,我們只能不斷的累加硬件,把內(nèi)存擴(kuò)展到了32G,甚至是64G,然而這只是飲鴆止渴的錯誤方式,應(yīng)該從根本上解決ImageControl控件占用內(nèi)存過大的問題,同時應(yīng)該優(yōu)化程序顯示排列圖片的邏輯。
在考慮到項目人員的分配情況和項目計劃,我們決定優(yōu)化ImageControl這個思路暫緩,因?yàn)檫@個工作量會更大。我們決定先著手優(yōu)化程序展示圖片的邏輯。程序只加載用戶能夠看到的Dicom,當(dāng)用戶下拉進(jìn)度條時,再陸續(xù)加載可見的圖片,并且將其余不可見的ImageControl銷毀,優(yōu)化之后程序占用的內(nèi)存銳減60%-70%,可以接受。
以上就是一次百萬行WPF項目代碼的重構(gòu)記錄的詳細(xì)內(nèi)容,更多關(guān)于WPF項目代碼重構(gòu)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
XXencode 編碼,XX編碼介紹、XXencode編碼轉(zhuǎn)換原理與算法
這篇文章主要介紹了XXencode 編碼,XX編碼介紹、XXencode編碼轉(zhuǎn)換原理、算法,需要的朋友可以參考下2016-06-06Matlab使用Plot函數(shù)實(shí)現(xiàn)數(shù)據(jù)動態(tài)顯示方法總結(jié)
這篇文章主要介紹了Matlab使用Plot函數(shù)實(shí)現(xiàn)數(shù)據(jù)動態(tài)顯示方法總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02數(shù)據(jù)分析2020年全國各省高考成績分布情況
這篇文章主要介紹了數(shù)據(jù)分析2020年全國各省高考成績分布情況,順便可以用這個數(shù)據(jù)看每個省市的一本線劃分比率,還有其他相關(guān)的數(shù)據(jù),需要的朋友可以參考下2020-07-07