【问题标题】:What to call OOP's equivalent of "referential transparency"?什么叫 OOP 的“参照透明”?
【发布时间】:2011-03-11 20:19:38
【问题描述】:

我的理解是“referential transparency”这个词实际上只能应用于函数式代码。但是,面向对象代码中对对象的方法调用可以具有类似的属性,即方法的返回值,而方法调用后对象的状态只取决于调用前对象的状态,以及方法的参数。

即功能参照透明:

i = foo(n, m);
// return value depends only on n, m

OO“参照透明度”:

i = obj.foo(n, m);
// return value, and subsequent state of obj, depends 
// only on initial state of obj, n, m

这个属性有名字吗?

如果obj的状态在调用foo()的过程中没有改变,那么如果function overloading被支持,那么“面向对象”风格就等价于函数形式,因为它可以被重写为:

i = foo(obj, n, m);
// return value depends only on obj, n, m

但是,obj 的状态在方法调用中发生变化是很常见的,所以我不确定这是否有助于分析...

【问题讨论】:

    标签: oop functional-programming referential-transparency


    【解决方案1】:

    您的错误是认为 FP 和 OO 在某种程度上是根本不同的。参照透明的“OO 版本”就是参照透明。

    表达式e 是引用透明的当且仅当e 可以替换为其评估结果而不影响程序的行为。

    因此,如果您有一个表达式o.foo(a),那么如果您可以修改代码以将其替换为调用结果,那么它是引用透明的,而无需更改程序的行为方式。显然,如果 o.foo 是无效的,你就不能这样做。如果它修改了o 的内部状态,则同上。因此,o.foo(a) 引用透明的唯一方法是其结果 oa 的函数。

    在我看来,“功能代码”是“引用透明代码”的同义词。

    【讨论】:

    • 另外,如果a)你的程序依赖于o.foo(a)结果的对象标识,或者b)o.foo(a)有一些副作用比如I/O,你不能替换打电话。
    • 我想可以公平地说,使用 OO 代码可以实现完全的引用透明性。 (标准的 getter 可能会有这个属性。)但我的主要问题是,在方法调用之后,是否有一个较弱但仍然有用的属性的名称,o 本身的返回值和最终状态仅取决于o 的初始状态和方法的参数。
    • 标准吸气剂明显在引用上是透明的。引用透明性意味着您的结果取决于传入的参数。 getter 没有 没有传入参数(除非您想开始谈论隐含参数,例如 this/self 在这种情况下您错过了“透明”部分的重点)和它们的返回值取决于调用中涉及的对象的状态,该对象是函数外部的实体。参考透明度基本上是“你得到你所看到的,仅此而已”。如果有看不见的部分,那是不透明的。
    • 如果你可以用它的值替换对“getter”的调用,那么它是RT,否则不是。 “仅取决于初始状态”非常弱。初始状态可能取决于整个宇宙。
    • 你有时知道状态。例如,如果您刚刚在程序的前一行创建了该对象。
    【解决方案2】:

    我不认为您在 OO 场景中描述的属性为您提供了与函数式编程中引用透明度所做的类似的任何东西。您描述了一个属性,其中foo 方法在以下调用中仅修改obj 对象的状态:

    i = obj.foo(n, m); 
    

    但是,如果您有另一个引用 obj 的对象,那么对 foo 的调用也会修改另一个对象的行为。由于对象之间的引用在 OO 中是必不可少的(这意味着这是一个您无法轻易避免的问题),这意味着您描述的属性并没有告诉您太多关于代码的信息。例如:

    a = new Other(obj);
    i = obj.foo(n, m);  // changes state of 'obj' and 'a'
    

    如果foo 方法是引用透明(没有修改任何状态 - 只是返回了一些结果),那么这将是一个有趣的属性 - 因为它不会修改a.

    【讨论】:

    • 我很欣赏你的观点,如果 obj 的状态发生变化,那么理论上在调用 foo() 时会发生任何事情,因为 obj 可能与系统中的任何其他对象有连接,我没有考虑这在提出问题时。但是,如果对 obj 的更改仅限于其自己的原始类型或标量类型,我认为 OOP“引用透明度”仍然是一个有意义且有用的概念。例如,此属性可以更轻松地测试和推理代码。
    • "那么理论上,当 foo()" 一针见血时,任何事情都可能发生。由于这是真的,因此很难确定程序的行为,包括对 foo() 的调用。实际上,程序员必须做更多的工作来确保这种对象的模块化和安全性。此外,这通常是一个全球性问题:您必须在您的确定性前面加上“假设该类按预期使用......”
    【解决方案3】:

    如您所说,功能术语是引用透明度。我会谦虚地建议您在此处描述的形式以及取决于方法的参数加上对象的状态的结果被称为 reference opacity

    【讨论】:

      【解决方案4】:

      方法调用后对象的状态仅取决于调用前对象的状态和方法的参数。

      我想你可以说方法没有外部依赖

      然而,与参照透明性不同,我不确定这会给您带来什么。我想这意味着该方法易于测试。

      【讨论】:

      • 是的,我在考虑这个属性如何帮助测试,以及它如何帮助人们推理代码。
      • 不确定我是否同意最后一条语句 - 如果其中一个依赖项是对象的起始状态,那么测试非常很难,因为枚举了任何可能的状态非平凡的对象使得选择合适的测试用例变得非常困难(我同意具有外部依赖性会使输入空间更大,达到组合爆炸的程度)
      • @Paolo 同意了。但是你会同意它至少在原则上是可测试的,不像调用DateTime.Now的方法
      猜你喜欢
      • 1970-01-01
      • 2011-06-18
      • 2011-03-17
      • 1970-01-01
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多