4.4 数据安全
你:关于结构性分享的事情,我有些不清楚。如果我们编写代码来修改在两个版本的数据之间共享的数据部分,会发生什么情况?更改是否会影响两个版本?
乔:你能不能写一段代码来说明你的问题?
你开始在笔记本电脑上键入内容,然后就会出现清单4.4中的代码片段,它说明了你的观点。
清单4.4 修改两个版本之间共享的数据的一段代码
var member = {
"email": "joe@me.com",
"password": "secret",
"isBlocked": true
}
var updatedMember = _.set(member, "password", "hidden");
member["isBlocked"] = false;
你:我的问题是:updatedMember中isBlocked的值是什么?
乔: 答案是:禁止通过本地哈希图设置器来改变数据。所有的数据操作都必须通过不可变的函数。
警告 所有的数据操作都必须通过不可变的函数完成。严禁使用本地Hash Map setter。
你:当你说禁止时,你的意思是由developer来确保它不会发生。对吗?
乔:没错。
你:有什么办法可以保护我们的系统不受开发者的错误影响?
乔: 是的,有一种方法可以在数据结构的层面上确保数据的不可更改性。这就是所谓的持久性数据结构。
你:持久性数据结构在内存和计算方面是否也很高效?
乔: 实际上,持久性数据结构内的数据组织方式使它们比不可变的函数更有效率。
TIP 持久性数据结构在数据层面是不可改变的。没有办法改变它们(即使是错误的操作)。
你:是否有提供持久性数据结构的库?
乔:当然有。例如,我们有JavaScript中的Immutable.js,Java中的 Paguro,C#中的Immutable Collections ,Python中的Pyrsistent,Ruby中的Hamster
您:那么为什么不使用持久数据结构而不是不可变函数呢?
乔:持久数据结构的缺点是它们不是本机的,这意味着使用它们需要从本机到持久以及从持久到本机的转换。
你:那你推荐哪种方式呢?
乔:如果你想玩一玩,那就从不可变函数开始吧。但是对于生产应用程序,我建议使用持久数据结构。
你:所以糟糕的原生数据结构不是持久的!
乔:这是我喜欢Clojure的原因之一:该语言的原生数据结构是不变的!
4.5 突变的提交阶段
到目前为止,我们已经了解了如何实现变异的计算阶段。计算阶段是无状态的,因为它不会对系统进行任何更改。现在,我们将了解如何在提交阶段更新系统状态。
再看一下清单4.5中Library.addMember()的代码,您会发现一些问题:该函数返回库的一个新状态,其中包含一个额外的成员,但它不影响库的当前状态!
清单4.5 变异的计算阶段不会对系统进行任何更改
Library.addMember = function(library, member) {
var currentUserManagement = _.get(library, "userManagement");
var nextUserManagement = UserManagement.addMember(currentUserManagement, member);
var nextLibrary = _.set(library, "userManagement", nextUserManagement);
return nextLibrary;
};
你:我看到Library.addMember()没有改变库的状态。那么,库状态是如何更新的呢?
乔:这是个很好的问题。AddMember()只处理数据计算,并且是无状态的。在提交阶段,通过向前移动系统状态引用的状态版本来更新状态。
你:什么意思?
乔:这是我们向系统添加会员时会发生的情况。计算阶段创建具有两个会员的状态版本。在提交阶段之前,系统状态是指具有一个会员的状态版本。提交阶段的职责是将系统状态向前移动,以便它引用具有两个会员的状态版本。
图4.5 提交阶段向前移动到系统状态TIP 提交阶段的职责是将系统状态前进到计算阶段返回的状态版本。
你:就代码而言,它看起来怎么样?
乔:代码由两个类组成:System,一个实现突变的单例有状态类。SystemState管理系统状态的单个有状态类。
你:在我看来,这听起来像是典型的面向对象。
乔:对。系统的这一部分是有状态的,非常类似于OO。
你:我很高兴看到你仍然在OO中发现了一些有用的东西。
乔:冥想教会了我,我们宇宙的每一块都有自己要扮演的角色。
你:好极了!你能给我看一些代码吗?
乔:当然可以。让我们从System类开始:清单4.6显示了addMember突变的实现。
清单4.6 系统类
class System {
addMember(member) {
var previous = SystemState.get();
var next = Library.addMember(previous, member);
SystemState.commit(previous, next);
}
}
你:SystemState是什么样子的?
乔:清单4.7显示了SystemState类的代码:它是一个有状态类!
清单4.7 SystemState类
class SystemState {
systemState;
get() {
return this.systemState;
}
commit(previous, next) {
this.systemState = next;
}
}
你:我不明白SystemState的意思。这是一个具有getter和Commit函数的简单类!
乔:稍后,我们将丰富SystemState.Commit()方法的代码,以便它提供数据验证和历史跟踪。目前,需要注意的重要一点是,计算阶段的代码是无状态的,并且它与提交阶段的有状态代码是分离的。
TIP 计算阶段是无状态的。提交阶段是有状态的。