我将使用一个不同的、经典的 OO 建模示例:银行账户。
这些在地球上几乎所有的 OO 课程中都会用到,而你通常最终得到的设计是这样的:
class Account(var balance: BigDecimal) {
def transfer(amount: BigDecimal, to: Account): Unit = {
balance -= amount
to.balance += amount
}
}
IOW:余额是数据,转账是操作。(另请注意,传输是涉及多个可变对象的复杂操作,但是应该是原子的,而不是复杂的......所以你需要锁定等)
然而,这是错误的。这不是银行系统的实际设计方式。事实上,现实世界(实体)银行业务的运作方式也并非如此。实际的实体银行和实际的银行系统是这样工作的:
class Account(implicit transactionLog: TransactionLog) {
def balance = transactionLog.reduceLeft(_ + _)
}
class TransactionSlip(from: Account, to: Account, amount: BigDecimal)
IOW:余额是操作,转账是数据。请注意,这里的一切都是不可变的。余额只是交易日志的左侧折叠。
另请注意,我们甚至没有将纯粹的功能性、不可变设计作为明确的设计目标。我们只是想正确地为银行系统建模,巧合的是,我们最终得到了一个纯粹的功能性、不可变的设计。(嗯,这实际上并非巧合。现实世界的银行业务以这种方式工作是有原因的,并且它具有与编程相同的好处:可变状态和副作用使系统变得复杂和混乱……而在银行业务中,这意味着钱不见了。)
这里的要点是,完全相同的问题可以用非常不同的方式建模,并且根据模型,您可能会提出一些简单的东西来使纯粹不可变或非常困难。