java - 整个程序可以是不可变的吗?
问题描述
- 我熟悉不可变性并且可以设计不可变的类,但我主要是学术知识,缺乏实践经验
- 请参考上面的链接图片(不允许嵌入)
- 从下往上看
- 学生需要一个新地址
- 我们没有真正改变学生,而是创建一个包含新地址的新学生
- mutator 方法返回这个新对象
问题:假设 mutator 调用来自不可变对象,如何处理这个新对象?
- 新学生无法保存在 Lecture 中,因为 Lecture 也是不可变的
- 所以我们也需要一个新的 Lecture,它包含新的 Student
- 但是在哪里保存新的讲座?
- 当然是在新学期,但它在哪里结束呢?
- 至少可以通过使用组件外观模式来打破链,该模式处理所有新对象的创建,而无需通过整个链转发调用
问题:这在哪里停止?是否必须在某个地方有一个可变对象才能至少保存最顶层的实例?
解决方案
这就是函数式编程的思想。一切都是不可变的,任何函数调用都不允许有副作用。改变复杂对象的唯一方法,就像在你的例子中一样,是重新创建父对象。
现在的问题是如何改变程序状态。因此,我们首先考虑堆栈。它包含所有局部变量的值以及被调用函数的所有参数的值。我们可以通过调用新函数来创建新值。我们可以通过从函数返回来丢弃值。因此,我们可以通过调用函数来改变程序状态。但是,从函数返回时并不总是可以丢弃它的局部变量,因为我们可能只想丢弃一些局部变量,但需要保留其他变量的值以进行进一步的操作。在这种情况下,我们根本无法返回,但我们需要调用另一个函数并只将一些局部变量传递给它。现在,为了防止堆栈溢出,函数式语言有一个称为尾调用优化的特性,它能够从调用堆栈中删除不必要的条目。如果对关联函数剩下要做的唯一事情是返回其自身调用的函数的值,则调用堆栈的条目是不必要的。在这种情况下,保留调用堆栈条目是没有意义的。通过删除不必要的调用堆栈条目,其他未使用的局部变量的值将被丢弃。您可能想了解它在这里。此外,尾递归与此有关。
同样,这是像Haskell这样的纯函数式编程语言的想法。一切都是不可变的,这真是太好了,但是这些语言有它们唯一的问题和它们自己处理这些问题的方法。例如,在这些语言中可以使用 Monad(以及因此更高种类的类型),但在命令式/面向对象的编程语言中很少见。
我喜欢在我的程序内存的叶子上拥有不可变的值。但是,构成这些不可变值的代码(实际上形成了应用程序逻辑)确实包含可变状态。对我来说,这结合了两个世界的优点。然而,这似乎是一个偏好问题。
推荐阅读
- visual-studio - 查找并运行所有使用 step 的场景?
- c# - 放置在主窗口上时,用户控件缺少按钮
- php - 使用 php 提取 url 元标记
- android - 如何将edittext值从recyclerview适配器传递给android中的活动
- c# - 如何创建文件或将文件上传到 Azure Data Lake Storage Gen2
- php - windows文件解密
- java - 将枚举值列表与单个枚举进行比较
- sql - 如何在 SQL oracle 中连接 2 个表
- c++ - 由于模数运算符导致的编译代码问题
- javascript - 使用 where 子句 firestore 搜索时获取文档 ID