我想「資料結構」這個術語對每個程式設計師都不陌生,像什麼「棧」、「樹」、「fifo」、「list」、「map」、「雜湊表」之類的概念我們在學生時代就已掌握,我想應該沒有哪個有經驗的程式設計師沒有應用過上面提到的那些資料結構吧!然而,通常我們應用這些資料結構,是把它當作存放待處理資料的容器,有多少人會把它和程式架構聯絡在一起呢?
很多時候,我們在設計程式時,考慮的是它的模組間的邏輯呼叫關係,比如,乙個程式要呼叫哪些子模組?乙個使用者操作或輸入檔案會觸發哪些功能被呼叫?等等……這種設計思路對於我們設計乙個小規模的應用程式時是比較方便的(因為比較符合我們的習慣),然而如果我們要設計乙個更複雜的程式,那麼這種設計思路就會遇到一些問題,比如,當我們要加入乙個新功能時,我們不得不修改大面積的**,從而引入一些很難察覺的bug;或者我們要修正乙個bug,而這個修正會改變函式的呼叫順序時,這些修改也是極其容易引入新bug的;或者隨著修改次數的增加,使得我們的**越來越難讀懂,越來越難維護,最後不得不使用一些work-around方法,而這些work-around反過來又進一步使得我們的程式更加難以掌控。
以上種種問題都是我們在實際開發中經常遇到的問題,而且往往這些問題才是困擾專案的最大問題,那麼有沒有什麼辦法可以改進這些問題呢?方法還是有的,但首先我們要改變我們的思維習慣,特別是在設計程式框架時,我們要放棄以邏輯(即呼叫關係)為主導的思維習慣,而要以資料為主導去思考我們的系統。這裡的資料即包括我們需要處理的純資料,也包括我們的處理函式,即把函式當作資料看待(在《c++語言中的元類程式設計》一文中,我們討論「閉包」概念時,也提到過這一思想)。這樣我們在做架構設計時,就只需要關注各種資料間的組織結構和處理演算法(即資料結構)了。
上面的建議聽起來似乎有些道理,但也頗令人費解,我們要如何把函式當作資料來看待呢?這樣做真的能改進我們的程式質量嗎?那就讓我們來看乙個實際的例子,當它的處理函式被資料化後能帶來的好處吧。
很多時候,我們的程式都需要處理一組命令列的引數,而通常我們的處理邏輯看起來會像這樣:
const char * option_names = }}
}}上面**中的省略部分就是命令列引數名的定義和呼叫相關的處理函式,該**的效率且不說,單考慮增加乙個命令列引數及其處理函式,我們就需要修改兩個不同地方,如果我們忘記修改任何一處地方,就會產生bug。另外,隨著parseoptions函式的多次修改,它也變得越來越長,給閱讀帶來不便。然而,如果我們考慮將命令列引數名的定義和它的處理函式放在一起,並將它們組織成乙個map,那麼parseoptions函式就只需要考慮如何訪問這個map,並且一次實現無需修改,而我們需要修改的僅僅是往這個map中增加乙個命令值處理函式。如下例:
struct ioptionparser
virtual void parsevalue(const char * valuebegin, const char * valueend) = 0;
// 如果我們將命令列引數的內容放在這個介面的實現類中,我們只需要再增加一組操作來獲取它的內容即可
typedef std::mapoption_parser_map;
void parseoptions(const char * szoptions, option_parser_map & parsermap)
} class cstropt: public ioptionparser
}; class cintopt: public ioptionparser
...regoptionparsers(option_parser_map & out_parsermap)
通過上面的簡單例子,我們不難體會,將函式「資料化」後,能使得程式的核心函式「一次實現,無需修改」,而且可以單獨進行優化(和原始函式比較一下),擴充套件更容易,程式可讀性和可維護性也提高了。
資料結構與程式架構(三)
在上篇文章中,我們看到了硬體工程師在設計程式 即邏輯電路 時是很直觀的,直觀就意味著容易理解和檢錯 同時,我們也能看到這種方法的強大,通過將基本處理單元連線成特定的拓撲結構,就可以實現某種特定功能的計算 而且,這也體現了這種方法的靈活性,當面對新需求時,我們只要設計出能處理這一新需求的拓撲結構即可 ...
資料結構與演算法(一)常用資料結構
什麼是資料 資料元素 資料項 資料物件 資料型別?資料 萬物都是資料,資料就是能輸入計算機和被程式處理的符號 資料元素 資料元素是資料的基本單位 是具體的資料 每乙個學生的資訊就是乙個資料元素 資料項 乙個資料元素由若干個資料項構成 學生的姓名 學號等都是學生資訊資料元素的乙個資料項 資料物件 具有...
資料結構與演算法(一)
物件導向程式設計方式 1.使用自定義類封裝陣列 2.新增類方法來實現資料操作。無序陣列 增 刪 改 查 更 顯 public class myarray public myarray int maxsize 新增資料 public void insert long value 顯示資料 public...