面向对象编程/状态之恶
外观
< 面向对象编程
我们没有足够强调的一点:状态(与变化相反)是邪恶的!或者,(也许更好的说法)维护不必要的状态是所有(呃……许多)错误的根源。让我们看一个例子,现在用 Ruby
- 我们正在随时间显示某些内容,因此我们正在处理
- 像素和秒。根据缩放级别,
- 它们之间存在相关性:每秒像素,
- 或 PPS。
类时间线
{ PPS; current_position_in_seconds; current_position_in_pixels; public:
GetPosInSeconds() {current_position_in_seconds;} SetPosInSeconds(s) {current_position_in_seconds = s;} # oops, we're out of sync
GetPosInPixels() {current_position_in_pixels;} SetPosInPixels(p) {current_position_in_pixels = p;} #oops, we're out of sync
} 在此示例中,我们正在维护不必要的状态——也就是说,我们同时以秒和像素来保存位置。这对使用此类的用户来说很方便,也很重要,但我们在封装方面搞砸了。每当您在一个单位中设置值时,都会破坏另一个单位的正确性。您说,这是一个简单的修复方法
- 我们正在随时间显示某些内容,因此我们正在处理
- 像素和秒。根据缩放级别,
- 它们之间存在相关性:每秒像素,
- 或 PPS。
类时间线 {
PPS; current_position_in_seconds; current_position_in_pixels;
public
GetPosInSeconds() {current_position_in_seconds;} SetPosInSeconds(s) { current_position_in_seconds = s; current_position_in_pixels = s*PPS; }
GetPosInPixels() {current_position_in_pixels;} SetPosInPixels(p) { current_position_in_pixels = p; current_position_in_seconds = p/PPS; }
} 这修复了前面示例中明显的错误,但您为自己制造了很多工作。现在您必须在 Timeline 类中的每个方法中保留所有计算的副本以保持一致性。这里可能还有几十个这样的情况(相信我)。那么,当您添加其他单位时,例如英寸?您必须再次复制所有内容。大概您会明白这将走向何方:计算属性、逻辑属性、虚拟属性,无论您喜欢怎么称呼它们。让我们再次看看这个例子
类时间线 {
PPS; # Pixels Per Second IPS; # Inches Per Second current_position_in_seconds;
public
GetPosInSeconds() {current_position_in_seconds;} SetPosInSeconds(s) {current_position_in_seconds = s;} GetPosInPixels() {current_position_in_seconds*PPS;} SetPosInPixels(p) {current_position_in_seconds = p/PPS; } GetPosInInches() {current_position_in_seconds*IPS;} SetPosInInches(i) {current_position_in_seconds = i/IPS; }
} 现在,我们假设 PPS 和 IPS 的转换因子设置正确,以方便起见,但除此之外,我们已经大大简化了问题。在我们现在编写的另外二十个函数中,我们只需要担心秒,没有一致性问题需要担心。此外,如果我们需要将基本单位从秒更改为像素,Timeline 用户永远不会知道。