前言
作為一名優秀的程式設計師,必須保證自己的程式碼能提供正確的,完善的介面,如此方能和同事,甲方更好的溝通合作,也讓自己的程式碼更加地容易維護。
本文將介紹一些設計優秀介面的思路。
思路一:匯入新的型別
下面還是先看這個例子,我定義了一個儲存日期的 Date 類:
1 class Date 2 { 3 public: 4 Date(int month, int day, int year); 5 // ...... 6 };
可用以下方法定義一個 Date 物件:
1 Date d(30, 3, 1995);
可有些使用者會犯很蠢的錯誤,比如:
1 Date d(30, 3, 1995);
顯然,他使用者將介面的引數輸錯位了。然而,優秀的介面應當能夠友好反饋錯誤資訊給使用者,這種情況下,最好的策略就是定義新的型別,請參考下面這個 Date 類的設計:
1 class Day 2 { 3 public: 4 explicit Day(int d) 5 :val(d) {} 6 // ...... 7 private: 8 int val; 9 }; 10 11 class Month 12 { 13 public: 14 static Month Jan() { 15 return Month(1); 16 } 17 static Month Feb() { 18 return Month(2); 19 } 20 // ...... 21 22 private: 23 explicit Month(int m) { 24 val = m; 25 } 26 // ...... 27 28 int val; 29 // ...... 30 }; 31 32 class Year 33 { 34 public: 35 explicit Year(int y) 36 :val(y) {} 37 private: 38 int val; 39 };
而定義一個 Date 物件,可採用如下方式:
1 Date d(Month::Feb(), Day(30), Year(1995));
在日,月,年各個類中,還可以實現更高階的封裝。
思路二:引導使用者進行正確編碼
這裡繼續上一篇文章中提到的智慧指標的一個例子,這裡要說明的是,當時給出的那個工廠函式:
1 class Investment 2 { 3 // ...... 4 }; 5 6 Investment * createInvestment();
並不是很好的一種設計。
為啥?因為使用者可能忘了使用智慧指標把 Investment * 接過去。而使用下面的工廠函式介面設計可以有效的避免這個問題:
1 std::tr1::shared_ptr<Investment> createInvestment();
這樣就讓使用者你不用智慧指標都不行了,哈哈。
甚至你還可以更過分,指定智慧指標在資源被指數為0的時候要呼叫的解構函式:
std::tr1::shared_ptr<Investment> createInvestment() { // 指定智慧指標型別及刪除器 std::tr1::shared_ptr<Investment>retVal (static_cast<Investment *>(0), getRidOfInvestment); // retVal = ... // 令 retVal 指向正確的物件 return retVal; }
上段程式碼中的getRidOfInvestment是你自己指定的刪除器。
思路三:限制型別什麼事情可以做什麼事情不能做
使用 const,explicit等限制性關鍵字,遮蔽無用的拷貝建構函式等可以做到這點。
這些在以前的文章中均有講解。
思路四:使你的類儘量表現得像內建型別
要做到這點可不簡單,你需要以"當初語言設計者設計語言內建型別時"那般謹慎的思考class的設計,對設計出的class,我們需要問自己以下幾個問題:
1. 新的物件資源在何時建立? 何時銷燬?
這部分同樣涉及到建構函式,解構函式的編寫。
2. 物件的初始化和賦值應該有什麼樣的差別?
這部分涉及到建構函式,拷貝建構函式,賦值運算子的編寫。不要混淆這兩個概念。
3. 如果物件發生了值傳遞,意味著什麼?
你得仔細考慮這期間發生的資源相關的一些問題。
4. 哪些物件是合法範疇?
物件的成員是不是合法,這點很重要。它影響到了你諸多成員函式的錯誤檢查工作,也影響到了丟擲的異常。
5. 新的類需不需要配合某個繼承圖系?
如果這個類的子類要實現多型,那麼成員函式就得宣告為虛擬函式;如果這個類繼承自其它類,那麼當你自定義拷貝建構函式或者過載賦值運算子的時候,也得對父類部分做出處理。
6. 什麼樣的操作符和函式對這個新的型別來說是合理的?
需要考慮這個型別應該對哪些運算子過載,還有哪些函式被當做成員函式,哪些用非成員函式實現。
具體的選取規則,以後會有篇文章專門講。
7. 什麼樣的標準函式應當駁回?
將它宣告為 private
8. 新的型別成員將被哪些物件取用?
這個涉及到變數private,protected,以及友元相關機制。
9. 新的型別是否應當滿足一般化的要求?
如果你要定義的是一個類家族,那麼你需要的不止是一個類,而是一個類别範本
小結
1. 類的設計不要貪快。要儘量滿足,實現這些規則,貪快會導致開發後期事倍功半。
2. 本文應當在實際專案中進行類設計的時候邊設計邊看,如此,方能有顯著的提高。