0%

代码整洁之道 - 对象和数据结构

数据抽象 Data Abstraction

隐藏实现 并非 只是在变量之间放上一个函数层那么简单,隐藏实现 关乎 抽象

类不能简单地通过 Setter 和 Getter 把变量暴露出去,而应该暴露抽象接口,以便用户无需了解数据的的实现就能操作数据本体

如果把变量设为 private,然后通过 Setter 和 Getter 暴露,这跟设为 public 有什么区别

举两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Point {
public double x;
public double y;
}


public interface Point {
double getX();
double getY();
void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
1
2
3
4
5
6
7
8
9
public interface Vehicle {
double getFuelTankCapabilityInGallons();
double getGallonsOfGasoline();
}


public interface Vehicle {
double getPercentFuelRemaining();
}

即使用了接口,也不一定就隐藏实现,比如第一种实现

以上两段代码以后者为佳,不要暴露数据细节,要以抽象形态表述数据

在定义接口时,要严肃思考,不可随意乱加 setter 和 getter

数据、对象的反对称性 Data/Object Anti-Symmetry

对象和数据结构是不同的

对象:把数据隐藏于抽象之后,暴露操作数据的函数

数据结构:暴露其数据,没有提供有意义的函数

对象和数据结构之间的二分原理:

  • 过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数

  • 面向对象代码便于在不改动既有函数的前提下添加新类

换句话说:

  • 过程式代码难以添加新的数据结构,因为必须修改所有函数

  • 面向对象代码难以添加新函数,因为必须修改所有类

过程式代码难做的事,面向对象代码反而好做,反之亦然

所以,我们应该随机应变,不能武断地说一切都是对象,有时数据结构会更加合适

混杂 Hybrids

不要让一个类既扮演对象,又扮演数据结构,这种类中拥有执行操作的函数,也有公共变量或 setter/getter

这种混杂增加了添加新函数的难度,也增加了添加新数据结构的难度,两头不讨好,应避免创造这种结构

它们的出现,展示了一种乱七八糟的设计

数据传送对象

最为精炼的数据结构,是一个只有公共变量,没有函数的类

这种数据结构被称为 数据传送对象(DTO),常用在消息传递中

小结

  • 对象暴露行为,隐藏数据,便于添加新对象类型而无须修改既有行为,但是难以在既有对象中添加新行为

  • 数据结构暴露数据,没有明显的行为,便于向既有数据结构中添加新行为,但是难以向既有函数中添加新的数据结构

  • 所以在设计时,我们要看场景是什么,从而使用数据结构或对象

    • 希望灵活地添加新的数据类型:使用对象

    • 希望灵活地添加新的行为:使用数据结构

    • 优秀的软件开发者应了解这些情形,选择合适的手段