为什么几乎所有观察者模式的实现代码都是用副作用实现的?

查看 32|回复 3
作者:netabare   
感觉很难理解,因为几乎所有的教程/教材,样例代码或者实际代码里面,提到观察者模式的时候,都是清一色的用返回 void 的方法来 visit 和 accept ,然后依赖副作用和全局变量来返回结果。
感觉这样的代码很难懂而且很绕,也可能因为我比起命令式或 OOP 代码更容易理解纯函数式代码吧。
就,比如说我试图理解 visitor pattern 的话,我会把它当成一个「在命令式语言里通过动态分派实现模式匹配」的技巧。自然而然的就会想出这样的代码,比如说在 Java 里的话:
public ISomeVisitor {
  public T visit(DerivedDataTypeA data);
  public T visit(DerivedDataTypeB data);
    ...
}
public IDataType {
  public T accept(ISomeVisitor visitor);
}
然后在 visitor 的具体实现里面,就只需要去重写然后使用 DerivedDataType 里面的访问方法去处理它,然后返回一个转换后的?T 类型。同时,由于每个 visit 分支都返回相同的类型,它们可以被组合起来,看起来就跟比如 Scala 或者 ML 语言里的模式匹配是同样的方式。
嗯,这个是理想情况。
但我记得在大学里学习 OOP 然后第一次按照这个路子写 visitor 后,就被其他人纠正说我实现的方法不对,因为我在 visit 函数里实现了具体逻辑。嗯,我一直没能搞懂为什么我实现错了,其实现在我也没搞懂。
但是我其实对返回 void 的方法和没有参数化泛型的类感觉更难理解,大概有这么几个原因吧:
  • 首先是这样实现的模式完全依赖于副作用,而不是显式的返回一个值。给人想起了 C 语言里的通过参数返回函数结果的代码风格,至少对我来说,感觉这样的代码更难懂,因为没有类型安全,而且靠副作用来操纵数据变换的时候,基本上算是做啥都行也没有任何机制可以保证返回结果能够被妥善的使用并且只使用一次。感觉像是典型的约定大于类型。
  • 其次是这样的代码里基本上可以不管什么都往 visitor 的基类里面塞,感觉就很自然地倒向了 god class 的反模式。而且这也是经常在使用了 visitor 的工程代码里看到的,而且每次看到这样的几百行甚至上千行的 visitor 都特别头疼。
  • 同样的道理,我可以有一个public Result returnedResult;在第 19 行,然后我的几十个分支里面都去 mutate 这个Result来返回一次访问的结果,就像刚才说的,没有任何机制可以让我不去随意修改这个returnedResult来破坏 visitor pattern 的模式。但同时,一个字段可以在第 19 行,第 199 行和第 1099 行被修改,这也破坏了「就近原则」吧?而且这里也可以看到一个潜在的 NPE 热点。

    所以觉得这样的代码既难以理解又难以维护。
    当然也许有人会说顺序很重要,但是一般来说 visitor 在业务代码例如 web 应用里面都是用在树状数据结构上,这种使用场景应该没差?
    所以我感觉困惑的大概就是,我这个 visitor 的实现思路错在了哪里?为什么几乎清一色的所有 visitor 的代码实现都是返回 void 方法并且通过副作用修改全局变量来储存返回计算结果的?这样做是为了什么呢?
    然后这个草稿写完之后又读了一本叫 A little Java, A few Patterns 的书,就感觉更困惑了。因为这本书里的 visitor 不但也是不依赖副作用而是返回值的,它的 visit 函数甚至还可以接受多个参数,看起来更不符合 visitor 的一般定义。
    所以我该怎么理解这个 visitor pattern 呢?
  • donaldturinglee   
    https://refactoring.guru/design-patterns/visitor 你可以参考一下这个,或者读一下 C++那本设计模式的书(很经典,但很难读)
    netabare
    OP
      
    @donaldturinglee 我了解这个网站,而且这个网站也属于我提到的「用副作用实现观察者模式」的例子。
    w568w   
    > 当然也许有人会说顺序很重要,但是一般来说 visitor 在业务代码例如 web 应用里面都是用在树状数据结构上,这种使用场景应该没差?
    你也想到了,如果 visitor 的结果依赖于访问状态(例如访问的顺序或访问的次数),你提出的这种设计就失效了。换句话说,你的设计要求 visitor 必须是无状态的(或者通俗地说,纯的),这就限制了使用场景。
    因此,不如在设计上退一步。无论是否需要访问状态,都要求访问者自己维护。
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部