音樂可視化specinker重構開發日誌

 重構前後對比:

 

由於期末臨近,同學哀求老師給時間複習,就推遲到考試結束一星期之後,這也給了充足的時間,對軟件進行了一次徹徹底底的重構!!

有多徹底?我重構之前這個項目的代碼只界面部分就已經七千多行了,這次重構小到基本控件,大到整個界面,引擎,都重新寫了,唯一留下的,還是那個顏色選擇控件。

爲什麼要重構?有三方面原因:

1、性能跟不上,內存佔用太多:起初頻譜引擎的響應速度爲25ms刷新一次,也就是說25ms之內要分析波形,根據波形,繪製軌道,得到軌道後,又要根據屬性,繪製圖形。而我在代碼設計之初並未考慮效率問題,導致軟件做好之後頻譜有輕微的卡頓,起初我以爲是音頻數據的問題,因此還特意寫了一個緩衝算法,進行平滑,勉強能夠用得下去。而內存就更誇張了,每創建一個頻譜,內存要多幾十MB,而刪除之後還不會減少,我明明有把對象析構啊!

2、界面粗糙,操作困難:界面醜不醜大家各有各的看法=.=,操作困難倒是真的,那個時候還沒寫手勢拖動的功能,要移動頻譜,只能在那個微調框那裏,輸入數據或拖動滑動條,手都給弄酸了

3、代碼混亂,擴展困難:寫代碼的時候只管當下想要什麼,就寫什麼,而沒有把目標放長遠,爲以後的擴展給預留位置。

我是如何解決性能和內存問題的:

按照常規的界面,我都是在窗口的繪圖事件之中創建畫筆,然後進行繪圖,這樣做常規的界面通過事件繪圖,看不出又什麼區別,但是我這裏25ms刷新一次,每次畫筆都要重新初始化,那麼就特別耗時,因此,我直接將畫筆做爲成員,將調整控件的值與畫筆的屬性進行綁定,那麼在繪圖的時候就不需要再初始化畫筆了,窗口只管繪圖就行。而內存方面,是由於Qt的控件刪除問題,控件在刪除前,必須將其父控件設爲null,再調用remove,之後還需調用delete才能刪除。

界面問題:

由於這個團隊就我一人,且代碼工程量不小,既要做UI,也要搞引擎,還得做ppt,初次的設計界面是隨手畫的,比較普普通通的常規設計軟件,一個縮率窗口,然後窗口旁邊一些屬性,這樣做也還行,後來我突然這樣想到,既然我做的是桌面頻譜,我爲什麼還要費事費力的搞一個縮率窗口來進行設計,直接在桌面設計,所見即所得不行嗎?因爲這個東西,我考慮到肯定要花費很多時間,所以歌單管理界面寫了一半,乾脆直接不要播放器了,直接採集系統音頻,況且做播放器真的太沒意思了(主要原因還是我找到了採集系統聲音的方法)

代碼混亂問題:

初次寫的項目裏就十多個文件的樣子,有些cpp中代碼量甚至超過了千行,可讀性太差,擴展就更不用說了,根本擴展不了,另外就是控件的效率問題,那個時候的控件都是自行繪製的(對,就是通過鼠標鍵盤事件自己繪製這些控件,這樣做的好處就是控件比較個性化,好看!),隨之而來的就是效率問題,比如我拖動頻譜,那麼控件的值也應該進行修改,試想拖動一個控件肯定要觸發很多次的控件設值事件,這些事件如果不能快速處理,就會導致我拖動的時候,會有卡頓。因此,對其重構,將控件與頻譜進行抽象,以便擴展

重構時使用設計模式解決了以下問題:

問題1:在不同環境下使用這個軟件,比如窗口尺寸不同,軟件如何自適應?

使用享元模式和單例模式,創建一個Config類,使用餓漢式單例。在類的構建時,動態的去讀取這些信息,而軟件設計中是通過Config類來進行配置,因此可以使得軟件能夠適應不同環境。

問題2:如何構建出一套完整的顏色選擇控件,使得其中不同的控件可以共同協作調整同一個色值?

使用中介者和觀察者模式,構造一個顏色中心(color center),所有的顏色控件都是對它內部的顏色進行修改,當一個顏色控件捕獲到一個顏色後,首先修改顏色中心的color值,然後顏色中心發送一個刷新信號,各個控件再刷新其內部的顏色。

問題3:一個被嵌套了很多層的窗口對象,已經“埋”得很深了,這時如果一個外部類使用它,難道一層一層的通過參數傳遞?

使用單例模式,深刻體會到單例模式的用處,單例模式的巧妙之處不在於“單例”,更爲有用的是提供了一個全局的公共訪問點,其中很多窗口類都用到了單例,顏色控件中的取色器,顏色板,顏色選擇器,顯示窗口,以及屬性窗口,由於軟件中除了主線程之外,還有一個引擎的波形分析線程,並沒有涉及到界面操作,因此無需考慮線程安全,又由於所有Qt窗口對象創建之前,必須先構造一個QApplication對象,所以無法使用餓漢單例在類的構建時就創建單例對象。因此,這裏用到的所有單例大都是懶漢式。

問題4:很多時候會需要創建“相似”的動畫生成器,應該如何處理?

使用原型模式,對動畫模式進行深拷貝,動畫類存在一個抽象的clone函數,主要是拷貝原型的屬性值,以及顏色值。

問題5:每一個微調控件都有各自的值修改信號,如果每一個控件都按各自的信號,那麼處理的時候就需要對它們一個一個的進行處理,這無疑是一個非常繁瑣的事情,且很容易出錯,也不容易排查。

使用了適配器模式,一些控件是自己實現的,一些是控件是使用自帶控件。爲了讓所有的微調控件能夠被統一的處理,因此我創建了一個adjuster類,內部提供一個valueChange信號。各個控件只需繼承自adjuster,並且連接valueChange信號,即可。

問題6:有時候選擇一個動畫的屬性之後,可能還會需要對這個屬性進行細微的設計,比如我選擇“點”類型頻譜,那麼應該可以修改它的“點大小”,如果修改爲“線”類型頻譜,那麼就應該可以修改它的“線寬”。也就是說,可能存在一些動態的屬性,那麼該怎麼解決這個問題?

使用組合模式,使得控件能“包含”控件,當父控件的值變動時,切換相應的子控件。

問題7:只提供一個動畫的設計是否使得軟件過於“乏力”,能不能再進一步使得整個軟件能繪製其他的自定義動畫?

的確,單純的繪製一個類型的頻譜動畫,最多也就是將這些動畫組成不同的形狀,用戶要不了多久就會失去興趣。因此我是這樣思考動畫的擴展:一個動畫無非就是連續顯示多張圖片。所以,要實現一個可定製的動畫,需要的就是一個屬性調節面板(動畫屬性+顏色屬性)加一個根據這些屬性來繪製動畫的“圖片生成器”。綜上,我對項目進行了重構,且將動畫向上抽象,構建了一個抽象的動畫類。只需提供一個動畫的生成函數,以及屬性面板,就可擴展額外動畫。

重構後的代碼框架(頭文件)

頻譜動畫類UML圖(只需按這個抽象類實現動畫,可以進行任意擴展)

 

發佈了374 篇原創文章 · 獲贊 147 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章