有時,重復來自設計中的錯誤。
讓我們看一個來自配送行業的例子。假定我們的分析揭示,一輛卡車有車型、牌照號、司機及其他一些屬性。與此類似,發運路線的屬性包括路線、卡車和司機。基于這一理解,我們編寫了一些類。
但如果Sally打電話請病假、我們必須改換司機,事情又會怎樣呢?Truck和DeliverRoute都包含有司機。我們改變哪一個?顯然這樣
的重復很糟糕。根據底層的商業模型對其進行規范化(normalize)--卡車的底層屬性集真的應包含司機?路線呢?又或許我們需要第三種對象,把司
機、卡車及路線結合在一起。不管最終的解決方案是什么,我們都應避免這種不規范的數據。
當我們擁有多個互相依賴的數據元素時,會出現一種不那么顯而易見的不規范數據。讓我們看一個表示線段的類:
- class Line {
- public:
- Point start;
- Point end;
- double length;
- };
第一眼看上去,這個類似乎是合理的。線段顯然有起點和終點,并總是有長度(即使長度為零)。但這里有重復。長度是由起點和終點決定的:改變其中一個,長度就會變化。最好是讓長度成為計算字段:
- class Line {
- public:
- Point start;
- Point end;
- double length() { return start.distanceTo(end); }
- };
在以后的開發過程中,你可以因為性能原因而選擇違反DRY原則。這經常會發生在你需要緩存數據,以避免重復昂貴的操作時。其訣竅是使影響局部化。對DRY原則的違反沒有暴露給外界:只有類中的方法需要注意"保持行為良好"。
- class Line {
- private:
- bool changed;
- double length;
- Point start;
- Point end;
-
- public:
- void setStart(Point p) { start = p; changed = true; }
- void setEnd(Point p) { end = p; changed = true; }
-
- Point getStart(void) { return start; }
- Point getEnd(void) { return end; }
-
- double getLength() {
- if (changed) {
- length = start.distanceTo(end);
- changed = false;
- }
- return length;
- }
- };
這個例子還說明了像Java和C++這樣的面向對象語言的一個重要問題。在可能的情況下,應該總是用訪問器(accessor)函數讀寫對象的屬性 。這將使未來增加功能(比如緩存)變得更容易。
Uniform Access原則:模塊提供的所有服務都應能通過統一的表示法使用,該表示法不能泄漏它們是通過存儲、還是通過計算實現的。