條款18: 讓介面容易被正確使用,不易被誤用
(make inte***ces easy to use correctly and hard to use incorrectly.)
內容:假設現在的你需要提供一些介面給你的客戶去使用,而現在的你沒有任何這個方面的經驗,那麼你就要考慮
下面這些情況的發生:
(1)你沒有告訴你的客戶該如何使用該介面;
(2)你告訴使用者你怎麼使用這些介面的方法,但是當使用者使用的時候遺忘了某一步驟(甚至幾個步驟);
(3)客戶正確地按照你說的方法使用,但是卻沒有達到預期的效果.
對於以上的任何一種情況都會導致客戶得不到你最終想提供的功能,而作為介面的提供方你也承擔了不
可推卸的責任,為了避免或減少這種情況發生的概率,我們這裡提出了介面設計應當遵循的基本原則:要讓你提供
給別人使用的介面容易被正確使用,不易被誤用.而要開發出這樣的介面,首先你必須要了解客戶可能做出什麼樣
的錯誤,比如我們先來看書上舉的這個例子,假設你要設計乙個用來表現日期的類date:
class date;
//test.cpp
date oneday(29,3,1995); //暈,月日顛倒了.應該是date oneday(3,29,1995);
為了防止這樣的錯誤(客戶記錯了引數的型別意義)發生我用型別系統(type system)來進行保護,於是你構造
了三個型別:
struct day
int value_;
};struct month
int value_;
};struct year
int value_;
};class date;
date d(29,3,1995); //error,wrong type
date d(day(29),month(3),year(1995)); //error,wrong type
date d(month(3),day(29),year(1995)); //ok
date d(month(15),day(29),year(1995)); //wow,can't detected this
最後乙個沒有檢測出來,於是我們再次修改**,重新設計型別,增加對其值的限定,我們就可以對month作如
下修改:
class month
static month feb()
...static month dec()
...private:
explicit month(int m); //prvent create object from outward
...};
//test.cpp
date d(month::mar(),day(30),year(1995));
哦也,這樣的設計是不是比原始版本好多了,呵呵.
預防客戶錯誤的另乙個方法是,限制型別內什麼事可做,什麼事不能做.常見的限制是加上const.如果忘記
const用法的話,回過頭去看看條款3的講解,比如"以const修飾operator*的返回型別"可阻止客戶因"使用者自定義型別"而犯
錯:if(a*b = c)...//perfect,compile error
下面是另乙個一般性準則"讓types容易被正確使用,不容易被誤用"的表現形式:"除非有好理由,否則應該盡量令你
的types的行為與內建型別保持一致".客戶已經知道像int這樣的type有些什麼行為,所以你應該努力讓你的
types在合樣合理的前提下也有相同的表現.為了避免無端的與內建型別不相容,真正的理由是為了提供行為一
致的介面,這樣的"一致性"更能導致"介面容易被正確使用".stl容器的介面十分一致,這使得它們非常容易被使用,例
如每個stl容器都有乙個size的成員函式.
前面我們討論過的用管理類物件管理資源類的例子,現在我們再來看這個例子,我們來看那個工廠函式接
口:image* createimage();
這裡我們來討論一下客戶怎麼去使用這個介面?客戶應該先得到新物件的指標,然後開始進行對該物件進
行操作,用完以後要取保刪除掉它,如果客戶遺漏了這一步驟就會發生資源洩露.而這裡你"要求"客戶去確保該
物件的刪除操作被完成無疑是增加了風險,客戶很可能忘掉去做它.
image* pimage = createimage();
...//delete pimage;//這裡忘記呼叫delete pimage
...這裡你很不服氣,他(客戶)忘記呼叫是他的責任,怎麼能夠怪我的介面不好呢?他沒按照我的步驟來導致
得不到最終的效果,這能怪我嘛?可氣!呵呵,你說的貌似有道理,那麼我們這裡說明一點:導致使用者用錯或者遺
漏你強調的必須步驟,可能是你的介面讓客戶用起來複雜,不方便導致的.這可就是你的問題了,你要讓你的接
口更加簡單,更加適合客戶呼叫,這樣就會在一定程度上減少了客戶由於誤用該介面而失敗的概率.
我們先來解決上面的問題,我們這裡可以將返回值型別為智慧型指標型別,因為管理類物件將釋放資源操作
從客戶這邊轉移到你這邊了,減少了客戶的操作也就是減少了出錯的概率,你說是不是?
std::tr1::shared_ptrcreateimage();
條款14我們也談到了tr1::shared_ptr容許當智慧型指標被建立起來時指定乙個資源釋放函式繫結在智慧型指
針身上.假設這裡的釋放函式為getridofimage那麼我們就可以這樣實現函式體:
std::tr1::shared_ptrcreateimage()
請記住:
■ 好的介面很容易被正確使用,不容易被誤用.你應該在你的所有介面中努力達成這些性質.
■ "促進正確使用"的辦法包括介面的一致性,以及與內建型別的行為相容.
■ "阻止誤用"的辦法包括建立新型別、限制型別上的操作、束縛物件值以及消除客戶的資源管理責任.
■ tr1::shared_ptr支援定製型刪除器.這可防範dll問題,可被用來自動解除互斥鎖等等.
讓介面容易被正確使用,不易被誤用 條款18
欲開發乙個 容易被正確使用,不容易被誤用 的介面,首先必須考慮客戶可能做出什麼樣的錯誤。假設你為乙個用來表現日期的class設計建構函式 class date 乍見之下這個介面通情達理 至少在美國如此 但它的客戶很容易犯下至少兩個錯誤。第一,他們也許會以錯誤的次序傳遞引數 date 30,3,199...
18 讓介面容易被正確使用,不易被誤用
1 所謂軟體設計,就是 讓軟體做出你期望它做的事情 的步驟。首先是構想,考慮對外暴露的介面,然後實現。2 客戶沒有正確使用自己提供的介面,自己也要負擔一部分責任,思考自己的介面是不是簡單明瞭,容易理解。3 對於多個形參的介面,最好表明每個形參的型別和有效範圍。4 盡量限制哪些事能做,哪些事不能做,盡...
條款1 4 讓自己習慣C
define aspect ratio 1.653 巨集定義的方式 const double aspect patio 1.653 const常量的方式 define定義的名稱無法追蹤,不易除錯 define比const常量定義會產生更多的目標碼 編譯器處理原始碼之前,預處理器就已經將原始碼中的as...