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

3.6 处理不同类型的record

  我们已经了解了DO如何使我们能够将record作为一等公民对待,可以使用泛型函数以灵活的方式对其进行操作。但是,如果record只不过是字段的聚合,我们如何知道记录的类型呢?

DO对这个问题有一个令人惊讶的答案。

你:我有一个问题。如果一条record只不过是一张Map,你怎么知道记录的类型呢?

乔:这是一个很好的问题,答案也很令人惊讶。

你:我很好奇。

乔:大多数时候,没有必要知道record的类型。

你:什么意思?

乔:我的意思是,最重要的是字段的价值。例如,查看清单3.15中的Catalog.AuthNames()源代码。它对图书记录进行操作,但唯一重要的是AuthIds字段的值。

令人怀疑的是,您可以查看Catalog.AuthNames的源代码。

清单3.16 计算一本书的作者姓名

function authorNames(catalogData, book) {
	var authorIds = _.get(book, "authorIds");
	var names = _.map(authorIds, function(authorId) { //1
		return _.get(catalogData, ["authorsById", authorId, "name"]);
	});
	return names;
}
  1. 可以使用.forEach()而不是.map()来完成

你:如何区分不同的用户类型,比如会员和图书馆管理员?我是说,他们都有电子邮件和加密密码。您如何知道记录代表的是会员还是图书管理员?

乔:您可以检查是否在Catalog的LibrariansByEmail索引或MembersByEmail索引中找到该记录。

你:你能说得更具体些吗?

乔:当然可以。假设我们有一名图书管理员和一名成员,让我写下我们的小型图书馆的用户管理数据可能是什么样子。为了简单起见,我使用朴素的base-64编码来加密密码。

清单3.17一条UserManagement record

var userManagementData = {
	"librarians": {
		"franck@gmail.com": {
			"email": "franck@gmail.com",
			"encryptedPassword": "bXlwYXNzd29yZA==" // 1
		}
	},
	"members": {
		"samantha@gmail.com": {
			"email": "samantha@gmail.com",
			"encryptedPassword": "c2VjcmV0", // 2
			"isBlocked": false,
			"bookLendings": [{
				"bookItemId": "book-item-1",
				"bookIsbn": "978-1779501127",
				"lendingDate": "2020-04-23"
			}]
		}
	}
}
  1. “mypassword”的Base-64编码
  2. “机密”的Base-64编码

TIP 大多数情况下,不需要知道record的类型。

你:我记得在第二章中,你告诉我将在第三章中展示UserManagement.isLibrarian()函数的代码。

乔:我们现在是在第三章,我要兑现我的诺言:

清单3.18 检查用户是否为图书馆管理员

function isLibrarian(userManagement, email) {
	return _.has(_.get(userManagement, "librariansByEmail"), email);
}

你:好的。你只需检查LibrariansByEmail Map是否包含电子邮件字段。

乔:是的。

你:你会用同样的模式来检查会员是超级会员还是VIP会员吗?

乔:我们确实可以有SuperMembersByEmail和VIPMembersByEmail索引。
不过,还有更好的办法。

你:怎么做?

乔:当一个会员是VIP会员时,我们会将值为true的字段isVIP添加到它的record中。
要检查会员是否为VIP会员,请检查成员记录中的isVIP字段是否设置为true:

清单3.19 检查会员是否为VIP会员

function isVIPMember(userManagement, email) {
	return _.get(userManagement, ["membersByEmail", email, "isVIP"]) == true;
}

你:我看到您通过isVIP字段的信息路径访问它:[“MembersByEmail”,email,“isVIP”]。

乔:是的。我想这能让代码清晰明了。

你:同意。我猜当会员是超级会员时,我们也可以这样做,并将isSuper字段设置为true?

乔:是的。就像这样:

清单3.20 UserManagement模块的代码

class UserManagement {
	isVIPMember(userManagement, email) {
		return _.get(userManagement, ["membersByEmail", email, "isVIP"]) == true;
	}

	isSuperMember(userManagement, email) {
		return _.get(userManagement, ["membersByEmail", email, "isSuper"]) == true;
	}
}

你查看了UserManagement模块代码几秒钟,然后突然有了一个想法,…

您:为什么不在会员记录中有一个类型字段,其值可以是VIP或Super?

乔:我想,根据产品要求,会员既可以是贵宾,也可以是超级会员。

你:嗯…。我们可以有一个类型字段,它将是VIP或Super的集合。

乔:在某些情况下,使用Types字段会很有帮助,但我发现对于record支持的每个特性都使用Boolean字段会更简单。

你:像isVIP和isSuper这样的字段有名字吗?

乔:我管它们叫特征字段。

TIP 使用特征字段,而不是维护有关记录的类型信息。(例如,isVIP)。

您:我们可以使用特征字段来区分图书馆员和会员吗?

乔:你是说有一个 isLibrarian 和一个 isMember字段?

你:是的,对图书馆员和会员都有一个通用的用户记录类型。

乔:我们可以,但我认为对图书馆员和会员有不同的record类型会更简单:图书馆员是图书馆员,会员是会员。

你:为什么?

乔:因为在数据方面,图书馆员和会员之间有明显的区别。例如,会员有图书外借,但图书管理员没有。

你:我同意。现在,我们需要提到实体图中的两个成员功能字段:

图3.6图书馆管理数据模型,成员特征字段为isVIP和isSuper。

 

乔:你喜欢我们一起设计的数据模型吗?

你:我觉得这很简单明了。

乔:这是DO的主要目标。

你:此外,令我惊喜的是,在代码和数据模型方面,适应不断变化的需求是如此容易。

乔:我想你也很高兴摆脱了复杂的类层次结构图。

你:当然!处理功能字段感觉比处理类继承简单得多。

乔:避免record中的继承使我们的数据模型变得简单。

你:用Map表示records还有其他好处吗?

乔:是的。我们可以很容易地处理高级数据检查内容。关于这一点,我将在第5章告诉你更多。

你:为什么不在下一章呢?

乔:因为我有一些更基本的事情要告诉你。

你:那是什么?

乔:如何在不改变数据的情况下管理状态。

3.7 总结

  在本章中,我们探讨了使用字符串映射表示record的好处。

  我们系统的数据部分是灵活的,每条信息都可以通过它的信息路径访问。我们使用泛型函数操作数据,泛型函数要么由语言本身提供,要么由第三方库(如Lodash)提供。例如,您可以免费获得JSON序列化。

  一方面,我们已经失去了通过编译时定义的成员访问record字段的安全性。另一方面,我们将数据从类和对象的限制中解放出来。数据表示为数据!

  当数据不是由对象表示时,我们可以自由地可视化系统的每个部分。

  我们不维护有关record的类型信息,而是使用特征字段。

  在DO系统中,代码和数据之间的依赖性很弱。这都是关于记录字段名称的。弱依赖性使其更容易适应不断变化的需求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值