Jan*_*ich 11 scala immutability
关于不可变状态的优点已经写了很多,但是在Scala中是否存在通常情况下更喜欢可变类?(这是来自使用可变类的"经典"OOP设计背景的人的Scala新手问题.)
对于像三维Point类这样微不足道的东西,我获得了不变性的优点.但是像Motor类这样的东西会暴露出各种控制变量和/或传感器读数呢?经验丰富的Scala开发人员通常会编写这样一个类是不可变的吗?在这种情况下,'speed'会在内部表示为'val'而不是'var',而'setSpeed'方法会返回该类的新实例吗?同样,来自描述电机内部状态的传感器的每个新读数是否会导致新的Motor实例被实例化?
使用类封装可变状态在Java或C#中执行OOP的"旧方法"似乎非常适合Motor示例.所以我很想知道,一旦你获得了使用不可变状态范例的经验,你甚至会设计像Motor这样的类是不可变的.
Jör*_*tag 12
我将使用另一个经典的OO建模示例:银行账户.
这些用于地球上几乎每个OO课程,你通常最终得到的设计是这样的:
class Account(var balance: BigDecimal) {
def transfer(amount: BigDecimal, to: Account): Unit = {
balance -= amount
to.balance += amount
}
}
Run Code Online (Sandbox Code Playgroud)
IOW:余额是数据,转移是一种操作.(另请注意,传输是一个涉及多个可变对象的复杂操作,但是它应该是原子的,而不是复杂的...所以你需要锁定等等)
但是,这是错误的.这不是银行系统实际设计的方式.事实上,这并不是真实世界(物理)银行的实际运作方式.实际的实体银行和实际银行系统的工作方式如下:
class Account(implicit transactionLog: TransactionLog) {
def balance = transactionLog.reduceLeft(_ + _)
}
class TransactionSlip(from: Account, to: Account, amount: BigDecimal)
Run Code Online (Sandbox Code Playgroud)
IOW:余额是一种操作,转移是数据.请注意,这里的一切都是不可变的.余额只是交易日志的左侧折叠.
另请注意,我们甚至没有将纯粹的功能性,不可变设计作为明确的设计目标.我们只是想正确地对银行系统进行建模,并最终得到一个纯粹的功能性,不可变的设计巧合.(嗯,实际上并非巧合.这就是为什么现实世界的银行业务以这种方式运作的原因,它具有与编程相同的好处:可变的状态和副作用使系统变得复杂和令人困惑......而在银行业这意味着钱消失了.)
这里的要点是,完全相同的问题可以用非常不同的方式建模,并且取决于模型,你可能会用一些微不足道的东西来制造纯粹的不可变或非常困难.
我认为最有可能的简短答案是:是的,不可变数据结构比您意识到的更有用和更高效。
您提出的问题有点模棱两可,因为答案不太取决于您所描述的电机,而是取决于您尚未描述的软件系统。在我看来,OOP 教学方式的最大错误是在考虑如何使用类之前建议对“领域”类进行自下而上的设计。也许您的系统甚至需要多个数据结构以不同的方式保存有关电机的相同信息。
在 Java 或 C# 中使用类封装可变状态进行 OOP 的“旧方法”似乎非常适合电机示例。
支持多线程系统的“新方法”(可以说)是将可变状态封装在actor中。代表电机当前状态的参与者是可变的。但是,如果您要拍摄电机状态的“快照”并将该信息传递给另一个参与者,则该消息需要是不可变的。
在这种[不可变]情况下,“speed”是否会在内部表示为“val”而不是“var”,并且“setSpeed”方法返回该类的新实例?
是的,但如果您使用案例类,则实际上不必编写该方法。假设您有一个定义为的类case class Motor(speed: Speed, rpm: Int, mass: Mass, color: Color)
。使用该copy
方法,您可以编写类似的内容motor2 = motor1.copy(rpm = 3500, speed = 88.mph)
。