有时,重复来自设计中的错误。
让我们看一个来自配送行业的例子。假定我们的分析揭示,一辆卡车有车型、牌照号、司机及其他一些属性。与此类似,发运路线的属性包括路线、卡车和司机。基于这一理解,我们编写了一些类。
但如果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原则:模块提供的所有服务都应能通过统一的表示法使用,该表示法不能泄漏它们是通过存储、还是通过计算实现的。