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

1.5当对象可变时,理解代码需要额外的思考

  在完成我们在上一节中介绍的系统级分析之后,您可能会有点累。

  让我们刷新一下,看看一些代码。

  请看一下清单1.1所示的代码:我们获得一个成员的阻塞状态,并将其显示两次。

  如果我告诉您,当我调用displayBlockedStatusTwice时,程序在第一次console.log()调用中显示为true,您能告诉我程序在第二次console.log()调用中显示了什么吗?

清单1.1非常简单的代码

class Member { 
	isBlocked = false;
	displayBlockedStatusTwice() {
		var isBlocked = this.isBlocked; 
		console.log(isBlocked); 
		console.log(isBlocked);
	}
}

var member = new Member(); 
member.displayBlockedStatusTwice();

“当然是True”,你告诉我。

你说得对。
  现在,请看一段稍有不同的伪代码,如清单1.2所示:在这里,我们在没有赋值变量的情况下显示两倍于成员的阻塞状态。
  和前面一样的问题:如果我告诉您,当我调用displayBlockedStatusTwice时,程序在第一次consol.log()调用中显示为true,您能告诉我程序在第二次consol.log()调用中显示了什么吗?

清单1.2明显简单的代码

class Member { 
	isBlocked = false;
	displayBlockedStatusTwice() { 
		console.log(this.isBlocked);
		console.log(this.isBlocked);
	}
}

var member = new Member(); 
member.displayBlockedStatusTwice();

  正确的答案是:在单线程环境中,它显示为真,而在多线程环境中,它是不可预测的。

  事实上,在多线程环境中,在两个console.log()调用之间,可能会有上下文切换,并且对象的状态可能会改变(例如,库管理员会解除成员的阻塞)。
  实际上,正如我们在第0章中所展示的,只要稍作修改,即使在单线程环境(如JavaScript)中,当通过异步代码修改数据时,也可能发生相同类型的代码不可预测性。
  这两个代码片段之间的区别在于:

  • 在第一个代码片段中,我们访问两次布尔值,这是一个原始值
  • 在第二个代码片段中,我们访问一个对象的成员两次

TIP 当数据可变时,代码是不可预测的。

  第二个代码段的这种不可预测的行为是令人恼火的后果之一,因为在OO中,与通常不可变的原始类型不同,对象成员是可变的。
  在OO中解决此问题的一种方法是使用互斥等并发安全机制保护敏感代码,但这本身会带来性能影响和死锁风险等问题。
  我们将在本书后面看到,Do以相同的方式处理每一条数据:原始类型和集合类型都是不可变值。这种“全民值类型待遇”,给开发商带来了不少心境的宁静。因此,DO开发人员的大脑中有更多的细胞可以处理他们构建的应用程序中有趣的部分。

TIP 数据的不变性给开发人员带来了许多宁静。

1.6 当数据作为成员锁定在对象中时,数据序列化并不简单

  现在,你真的累了,你在办公桌前睡着了…

  你有一个关于南希的梦,你的客户。

  在这个梦想中,南希要求你通过REST API,使用JSON作为传输层,使图书馆管理系统可以访问。

  你需要实现一个/搜索端点,接收JSON格式的查询并返回JSON格式的结果。

清单1.3中显示了一个/搜索端点的输入例子。

清单1.3 /搜索端的JSON输入

{
	"searchCriteria": "author", 
	"query": "albert"
}

清单1.4中显示了/搜索端的一个输出例子。

清单1.4 /搜索端的JSON输出

[
	{
		"title": "The world as I see it", 
		"authors": [
			{
				"fullName": "Albert Einstein"
			}
		]
	},
	{
		"title": "The Stranger", 
		"authors": [
			{
				"fullName": "Albert Camus"
			}
		]
	}
]

  你可能会通过创建三个类似于图1.14所示的类来实现/搜索端点(这并不奇怪:OO中的一切都必须用类来包装,对吗?):

  1. 负责处理查询的SearchController
  2. SearchQuery,将JSON查询字符串转换为数据
  3. SearchResult,将搜索结果数据转换为一个JSON字符串。

SearchController将有一个单一的处理方法,其流程如下:

  1. 从JSON查询字符串创建一个SearchQuery对象
  2. 从SearchQuery对象中获取searchCriteria和queryStr。
  3. 调用目录的搜索方法:用searchCriteria和queryStr编目,接收图书:List
  4. 创建包含书籍的SearchResult对象
  5. 将SearchResult对象转换为JSON字符串
图1.14 每个类都被拆分成代码实体和数据实体的类图

 
  其他端点又如何呢?例如,允许图书管理员通过。/add-book-item?

  那么,您必须重复完全相同的过程并创建3个类:

  1. 负责处理查询的AddBookItemController
  2. 将JSON查询字符串转换为数据的BookItemQuery
  3. 将搜索结果数据转换为JSON字符串的BookItemResult

  您之前在SearchQuery中编写的处理JSON反序列化的代码必须在BookItemQuery中重写。与您之前在SearchResult中编写的处理JSON序列化的代码相同:它必须在BookItemResult中重写
  坏消息是,您将不得不对系统的每个端点重复相同的过程。每次遇到新的JSON输入或输入时,都必须创建一个新类并编写代码。
  突然,您醒来并意识到Nancy从未要求过JSON。以上所有的一切都是一场梦,一个非常糟糕的梦…

TIP 在面向对象中,数据序列化是一场噩梦

  在OO中处理JSON序列化和反序列化需要添加如此多的类并一遍又一遍地编写如此多的代码,这非常令人沮丧!
  当您考虑序列化搜索查询、图书项目查询或任何查询都非常相似时,这种挫折感会变得更大。归根结底是:

  1. 查看数据字段
  2. 连接数据字段的名称和数据字段的值(用逗号隔开)。

  为什么这么简单的事情在OO中就这么难实现呢?
  问题是,在OO中,数据必须遵循严格的形状(在类中定义),这意味着数据锁定在成员中。一般情况下,无法访问数据。

TIP 在面向对象中,数据作为成员锁定在类中

  我们稍后将细化我们所说的对数据的泛型访问的含义,我们将看到DO如何提供一种泛型方法来处理JSON序列化和反序列化。在那之前,你将不得不继续受苦。但至少你意识到了这种痛苦,你知道这种痛苦是可以避免的。

警告 大多数面向对象编程语言都稍微减轻了从JSON到JSON转换的难度。它要么涉及反射(这绝对是一件复杂的事情),要么涉及代码冗长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值