跳转到内容

面向对象编程/"状态"是邪恶的!

来自维基教科书,开放世界中的开放书籍

"状态"是邪恶的!

[编辑 | 编辑源代码]

我们没有足够强调的一点是:状态(与变化相反)是邪恶的!或者,(也许说更准确)维护不必要的状态是所有(呃……许多)错误的根源。让我们看一个例子,现在在 Ruby 中

我们正在显示一个随时间推移的东西,所以我们正在处理像素和秒。根据缩放级别,它们之间存在关联:每秒像素数,或 PPS。

class Timeline
 {
  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
}

在这个例子中,我们正在维护不必要的 state - 也就是说,我们正在用秒和像素来保存位置。这很方便,对于这个类的用户来说也很重要,但我们在这里搞砸了封装。无论何时你设置一个单位的值,你都会破坏另一个单位的正确性。好吧,你说,这里有一个简单的解决方法

class Timeline
{
  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 类中的每个方法中保留每个计算,以保持一致性。可能会有数十个地方会出现这种情况(相信我)。那么当你添加其他单位时,比如英寸怎么办?你必须再次重复所有这些。可以推测,你已经看到了事情的发展方向:计算属性、逻辑属性、虚拟属性,无论你喜欢叫它们什么。让我们再看一遍这个例子

class 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 用户永远不会知道。

华夏公益教科书