模型数据、所有数据以及仅数据 - 面向数据编程 v1.1

2024-06-04 08:52

本文主要是介绍模型数据、所有数据以及仅数据 - 面向数据编程 v1.1,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

面向数据编程 (DOP) 以尽可能紧密地对数据进行建模为中心,这一点并不令人意外,因此 DOP 的核心原则是对数据进行建模,即对整个数据进行建模,而不是只对数据进行建模。通过混合使用记录(records)和密封类型(sealed types)以及一些对面向对象开发人员来说可能很奇怪的编程实践,但可以最好地实现这一目标。

1.密封类型(Sealed Types)

举例来说,一旦我们创建了书籍、家具和电子物品记录,中心域数据就建模了。但还不完全,因为它们之间存在一种尚未捕获的关系:我们商店中的每件商品要么是一本书,要么是(一件)家具,要么是电子物品。为了表示这种关系,我们使用密封类型。

Java 17 中已最终确定了密封类型。类或接口通过关键字 sealed 标记为密封,然后只有 permits 子句中列出的类型可以从中继承 - 其他类型被禁止这样做,否则将导致编译错误。此机制非常适合建模替代方案。将这句“一个项目是一本书、一件家具或一个电子设备”变为:

sealed interface Item permits Book, Furniture, ElectronicItem {// ...
}

而在实际应用场景中,当系统不能在添加新实现后简单地工作时,密封类型特别有用。另一个 List 实现?没问题,它将无缝运行。另一个 Item 实现?在一些现实生活中可能会涉及到的场景,例如现在必须检查增值税率,必须调整专用视图(例如公寓规划器或目录显示),并且可能必须引入新的交付方法。

还有许多其他情况,仅仅添加接口实现是行不通的。例如,身份验证提供程序或支付方式:仅仅编写 CreditCardPayment 实现支付是不够的,因为至少还必须实现相关的支付系统,并且可能还需要一个机制,在代码中的正确位置收集付款并将其传送到合适的支付系统。我们将在即将发表的有关操作的文章中看到它如何与密封类型优雅地协同工作。

首先,密封类型的几个属性:

  • 允许的子类型必须与密封类型位于同一模块中,或者(如果代码未编译为模块)位于同一包中。
  • 如果密封类型和允许的子类型包含在同一源代码文件中,则可以省略 permits 子句。
  • 允许的子类型必须直接从密封类型继承。
  • 允许的子类型必须是 final、sealed 或显式非密封(Java 的第一个带连字符的关键字!)。

虽然密封类当然是可行的,有时也很有用,但处理密封接口在一个非常具体的方面要轻松得多,我们将在讨论操作时讨论这一点。这就是为什么我通常建议关注密封接口。

2.除了数据之外什么都不建模

记录使聚合数据变得容易,而密封类型使表达替代方案变得容易。结合起来,这两种机制非常强大,甚至可以很好地建模复杂的结构。

3.定制的聚合和替代方案

记录的简单定义让我们可以创建定制的、可能多种类型。具体比如用户不必获取街道、邮政编码、城市和国家/地区的组件,这些组件可能最好存储在地址记录中,然后用户就可以拥有该记录的一个实例。

如果地址是可选的,并且用户还可以选择存储电子邮件地址和电话号码,那么您可以为类型提供一个 List contacts 字段,其中密封接口 ContactInfo 允许 Address、Email、Phone。是否至少需要一个联系信息?有一个 ContactInfo primaryContact 字段并将列表重命名为 additionalContacts。

目标是使用这些功能来定制适合实际域数据的类型。这使得开发人员更容易理解代码,因为它与他们需要知道的数据非常相似,并且也使代码更易于维护,因为非法数据更容易被拒绝 - 当我们研究如何仅表示合法状态时会对此进行更多介绍。

4.和类型模式平等

数据建模的核心部分是相等性的定义。如记录文章中所述,它们带有使用所有组件的 equals(和 hashCode)实现。这在许多情况下都很好,但特别是在处理用户和项目的系统中,ID 无处不在,大多数具有 ID 的对象可能都应该使用它来确定相等性。这是通常重写 equals(和 hashCode)的众多原因之一。

在上述的例子中,根据 ISBN 定义 Book 的相等性是有意义的。我们可以借助一个稍后会变得更加重要的功能非常优雅地做到这一点:类型模式,在 Java 16 中标准化,在本例中为 instanceof。

record Book(String title, ISBN isbn, List<Author> authors) {@Overridepublic boolean equals (Object other) {return this == other|| other instanceof Book book&& Objects.equals(isbn, book.isbn);}@Overridepublic int hashCode() {return Objects.hash(isbn);}}

类型模式位于 Book book 的其他实例中。它完成三个任务:

  • 检查 other 是否是 Book 类型的实例
  • 定义一个新变量 Book book,该变量在测试返回 true 时可见(“在范围内”)
  • 分配 book = (Book) other

由于 book 变量在类型检查为正的地方可见,因此您可以在 && 之后直接使用它来比较所需的字段。

注意:用 instanceof 实现 equals 并不总是正确的,但这里没有问题,因为 Book 是常量。

5.方法

您可以在记录上实现任意方法,但作为数据的透明载体,它们更喜欢某些方法而不是其他方法:

  • 没有参数的方法是最好的,因为它们除了返回记录的数据之外什么都做不了(除非它们引用全局变量,但这很少是一个好主意)。例如,email.tld() 可以识别并返回电子邮件地址的顶级域,或者 book.byline() 可以将书名和作者组合成一个字符串。
  • 接受类型本身作为唯一参数的方法也是受欢迎的。例如,如果您实现 Comparable,这可能是 compareTo,或者 Book 可以有一个方法 commonAuthors(Book),它返回参与这两本书的作者列表。
  • 接受其他记录(最好是那些已经用作组件类型的记录)的方法通常也可以:因为它们也应该是不可变的数据载体,所以可以假设没有状态被改变,所有结果都通过返回值传达。然而,在这种情况下,避免实现非平凡的域逻辑变得很重要。根据将操作与数据分开的原则,此类操作应保留给外部系统。
  • 具有任意参数的方法,特别是可变参数的方法,很有可能将正在作为操作的一部分处理的数据记录转变为这些操作的执行器,这通常应该避免。

请注意,这些并不是硬性规定,而是指导方针,如果情况需要,可以暂停执行,但你应该有这样做的充分理由。

6.接口合约

如果在面向数据的编程中,记录主要只是提供对数据的访问,而几乎没有其他操作,那么您可能会问自己如何在这样的设计中使用接口 - 毕竟,我们主要使用它们来为行为建模契约。事实上,这个角色在这里并不那么重要。记录实现的(密封)接口主要不是定义类型的功能,而是定义类型是什么:

  • 书籍、电子设备和家具属于物品。
  • 地址、电子邮件和电话号码属于联系信息。

从这些示例中可以看出,接口下统一的类型通常很少重叠。虽然项目可能至少都有项目编号,但不同的联系信息却完全不同。因此,像 ContactInformation 这样的接口可能最终没有单一方法。这是不寻常的,而且“看起来不对劲”,但这只是一个熟悉的问题。这里定义的契约不是描述行为(这不是数据有意义的类别),而是描述分组(在接口上下文中哪些数据是彼此的替代品),并且不需要任何方法。

6.总结

使用记录将数据聚合为有意义的定制类型,并使用密封接口来表达这些类型之间的替代方案。由于数据不附带行为,因此此类记录通常声明很少或根本不声明方法,而这些方法不仅仅是以不同的形式返回数据。因此,它们实现的密封接口可能声明很少或根本不声明方法,这可能是新颖的,但这是意料之中的,因为它们描述的契约是关于数据是什么(而不是它做什么)。

这篇关于模型数据、所有数据以及仅数据 - 面向数据编程 v1.1的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1029623

相关文章

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

详解如何使用Python从零开始构建文本统计模型

《详解如何使用Python从零开始构建文本统计模型》在自然语言处理领域,词汇表构建是文本预处理的关键环节,本文通过Python代码实践,演示如何从原始文本中提取多尺度特征,并通过动态调整机制构建更精确... 目录一、项目背景与核心思想二、核心代码解析1. 数据加载与预处理2. 多尺度字符统计3. 统计结果可

解决mysql插入数据锁等待超时报错:Lock wait timeout exceeded;try restarting transaction

《解决mysql插入数据锁等待超时报错:Lockwaittimeoutexceeded;tryrestartingtransaction》:本文主要介绍解决mysql插入数据锁等待超时报... 目录报错信息解决办法1、数据库中执行如下sql2、再到 INNODB_TRX 事务表中查看总结报错信息Lock

使用C#删除Excel表格中的重复行数据的代码详解

《使用C#删除Excel表格中的重复行数据的代码详解》重复行是指在Excel表格中完全相同的多行数据,删除这些重复行至关重要,因为它们不仅会干扰数据分析,还可能导致错误的决策和结论,所以本文给大家介绍... 目录简介使用工具C# 删除Excel工作表中的重复行语法工作原理实现代码C# 删除指定Excel单元

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创

SpringBoot整合Sa-Token实现RBAC权限模型的过程解析

《SpringBoot整合Sa-Token实现RBAC权限模型的过程解析》:本文主要介绍SpringBoot整合Sa-Token实现RBAC权限模型的过程解析,本文给大家介绍的非常详细,对大家的学... 目录前言一、基础概念1.1 RBAC模型核心概念1.2 Sa-Token核心功能1.3 环境准备二、表结

Nacos日志与Raft的数据清理指南

《Nacos日志与Raft的数据清理指南》随着运行时间的增长,Nacos的日志文件(logs/)和Raft持久化数据(data/protocol/raft/)可能会占用大量磁盘空间,影响系统稳定性,本... 目录引言1. Nacos 日志文件(logs/ 目录)清理1.1 日志文件的作用1.2 是否可以删除

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3