爲用戶設計良好的接口

前言程序員

  做爲一名優秀的程序員,必須保證本身的代碼能提供正確的,完善的接口,如此方能和同事,甲方更好的溝通合做,也讓本身的代碼更加地容易維護。函數

  本文將介紹一些設計優秀接口的思路。編碼

思路一:導入新的類型spa

  下面仍是先看這個例子,我定義了一個存儲日期的 Date 類:設計

1 class Date
2 {
3 public:
4     Date(int month, int day, int year);
5     // ......
6 };

  可用如下方法定義一個 Date 對象:指針

1 Date d(30, 3, 1995);

  可有些用戶會犯很蠢的錯誤,好比:code

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 對象,可採用以下方式:blog

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的時候要調用的析構函數:

 1 std::tr1::shared_ptr<Investment> createInvestment()
 2 {
 3     // 指定智能指針類型及刪除器
 4     std::tr1::shared_ptr<Investment>retVal (static_cast<Investment *>(0), getRidOfInvestment);
 5     
 6     // retVal = ...
 7     // 令 retVal 指向正確的對象
 8 
 9     return retVal;
10 }

  上段代碼中的getRidOfInvestment是你本身指定的刪除器。

思路三:限制類型什麼事情能夠作什麼事情不能作

  使用 const,explicit等限制性關鍵字,屏蔽無用的拷貝構造函數等能夠作到這點。

  這些在之前的文章中均有講解。

思路四:使你的類儘可能表現得像內置類型

  要作到這點可不簡單,你須要以"當初語言設計者設計語言內置類型時"那般謹慎的思考class的設計,對設計出的class,咱們須要問本身如下幾個問題:

  1. 新的對象資源在什麼時候建立? 什麼時候銷燬?

    這部分一樣涉及到構造函數,析構函數的編寫。

  2. 對象的初始化和賦值應該有什麼樣的差異?

    這部分涉及到構造函數,拷貝構造函數,賦值運算符的編寫。不要混淆這兩個概念。

  3. 若是對象發生了值傳遞,意味着什麼?

    你得仔細考慮這期間發生的資源相關的一些問題。

  4. 哪些對象是合法範疇?

    對象的成員是否是合法,這點很重要。它影響到了你諸多成員函數的錯誤檢查工做,也影響到了拋出的異常。

  5. 新的類需不須要配合某個繼承圖系?

    若是這個類的子類要實現多態,那麼成員函數就得聲明爲虛函數;若是這個類繼承自其它類,那麼當你自定義拷貝構造函數或者重載賦值運算符的時候,也得對父類部分作出處理。

  6. 什麼樣的操做符和函數對這個新的類型來講是合理的?

    須要考慮這個類型應該對哪些運算符重載,還有哪些函數被當作成員函數,哪些用非成員函數實現。

    具體的選取規則,之後會有篇文章專門講。

  7. 什麼樣的標準函數應當駁回?

    將它聲明爲 private

  8. 新的類型成員將被哪些對象取用?

    這個涉及到變量private,protected,以及友元相關機制。

  9. 新的類型是否應當知足通常化的要求?

    若是你要定義的是一個類家族,那麼你須要的不止是一個類,而是一個類模板

小結

  1. 類的設計不要貪快。要儘可能知足,實現這些規則,貪快會致使開發後期事倍功半。

  2. 本文應當在實際項目中進行類設計的時候邊設計邊看,如此,方能有顯著的提升。

相關文章
相關標籤/搜索