面向数据编程 Data-Oriented Programming [20]

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()只处理数据计算,并且是无状态的。在提交阶段,通过向前移动系统状态引用的状态版本来更新状态。

你:什么意思?

乔:这是我们向系统添加会员时会发生的情况。计算阶段创建具有两个会员的状态版本。在提交阶段之前,系统状态是指具有一个会员的状态版本。提交阶段的职责是将系统状态向前移动,以便它引用具有两个会员的状态版本。

TIP 提交阶段的职责是将系统状态前进到计算阶段返回的状态版本。

面向数据编程 Data-Oriented Programming [20] 图4.5 提交阶段向前移动到系统状态

 

你:就代码而言,它看起来怎么样?

乔:代码由两个类组成: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 计算阶段是无状态的。提交阶段是有状态的。

上一篇:Java中禁止浏览器开启缓存的方法命令


下一篇:面向数据编程 Data-Oriented Programming [4]