2019 年 1 月

第 34 卷,第 1 期

[孜孜不倦的程序员]

裸编码

作者 Ted Neward | 2019 年 1 月

Ted Neward错过上一期专栏的读者可能会惊讶地发现我还没有谈论 MEAN(Mongo、Express、Angular 和 Node)堆栈;这是因为我上次总结了这个系列,现在是时候把注意力转向其他内容了。

在 MEAN 系列中,我系统地解构了整个堆栈,并详细检查了每个组成部分。现在我将介绍一个堆栈(好吧,技术上是真正的单一),它旨在将所有部分组合成一个单一且无缝的整体,一个旨在用于隐藏大部分所需的低级别的细节工作。换言之,有时开发人员“只想使用对象”,而不必担心生成 UI、数据库或中间件。在很多方面,这是 Alan Kay 在发明对象时所想到的最终表现形式,早在 Smalltalk(这是 Kay 发明的面向原始对象的语言)时期,它是领域驱动设计 (DDD) 概念的自然逻辑扩展(或者更确切地说,是相反的扩展)。

Smalltalk 在运行时总是在称为浏览器的更大环境中执行。从本质上讲,浏览器是一个运行时执行环境,IDE 和 UI 主机都包装进一个环境。(相比之下,HTML 浏览器通常只是一个 UI 主机,尽管包括 Microsoft 在内的几家供应商都疯狂地想要将 HTML 浏览器同时用于 IDE 和执行环境。) 当开发人员在 Smalltalk 中定义新类时,浏览器知道如何围绕给定对象生成一个通用 UI,并且(根据具体供应商的 Smalltalk)知道如何将其持久保存到关系存储或其自己的对象数据库。开发人员不需要定义“视图”或“控制器”,并且“模型”也不是本质上只定义数据库架构的仅数据类。从许多方面来看,这就是原本面向对象的方式:开发人员与客户交互,查找对象,定义这些对象的属性(以及它们之间的关系),定义这些对象的行为并交付项目。正是出于这个原因,Kay 曾经说过,“我发明了面向对象这个术语,但我可以告诉你,C++ 并不是我想要的结果。” 除了以 C++ 语言为代价的所有笑话之外,他还反对用户和被操纵的实际域对象之间存在所有数千个对象。(好了,暂且不管这个事和通过指针直接操纵内存这件事,让我们集中精神。) 这意味着,我可以冒昧地认为他对 C# 和 Java 都不满意。(还有 Node.js,要启动。)

当然,开发人员一直致力于重新创建对象编程的“圣杯”,我将研究其中一个尝试,其名称为裸对象。(不,我是认真的,这就是它的名字,这源于开发人员应只关注业务领域,而用户应能够直接“使用无额外修饰”的对象,如果你愿意的话。或者,换句话说,用户应以其自然的“裸”状态来使用对象。)

从本质上讲,这种方法中正在发生的情况即引人入胜又令人生畏:基于在运行时从对象收集的元数据(通常通过 Reflection 调用),你将生成一个知道如何在对象上显示和编辑属性的 UI,根据对象上指定的其他元数据验证编辑内容(通常通过自定义属性),并在必要时拒绝那些不符合验证的更改。可以从那里进行查询并将对象存储到数据库(通常通过对象/关系映射层),以及可能需要更新的任何其他对象,例如通过所有权或一些其他关系链接的对象。

当然,使用裸对象框架的部分吸引力在于一语双关。准备好抽丝剥茧进行深入研究吗?

获取裸对象

启动浏览器,将其指向 nakedobjects.org,注意,该网站是一个简单的重定向站点,可让你选择访问位置,具体取决于所感兴趣的平台:有一种 .NET 风格(这将是我的重点)和 Java 风格,也称为 Apache Isis。(同样,不是开玩笑 - 负责 Isis 的开发人员正在考虑更改名称,但公平地说,在中东人民使用这个名字的很久之前他们就选择了这个名字。)

当重定向到 .NET 风格时,最终会进入 NakedObjects 框架项目的 GitHub 项目页,在撰写本文时,该项目的版本为版本 9。项目页的 README 主页上有两个值得注意的链接:一个是开发人员指南,这是使用框架时必须具备的(一个撰写良好文档的范例),另一个是包含模板解决方案的 .ZIP 文件,用作起点。虽然 NakedObjects 框架(缩写为 NOF)程序集可通过 NuGet 获得,但通常使用 .ZIP 模板来开始新的 NOF 项目会更加轻松,在后面原因将变得更加明显。现在,获取 .ZIP 模板,将其分解为代码的子目录,并在你喜欢的 Visual Studio 实例中打开解决方案文件。

当 Visual Studio 完成加载后,你会注意到该解决方案由几个不同的项目组成,准确地说是五个项目。在大多数情况下,它们的名字一目了然:Template.Client 是 Web 客户端,Template.Server 是 Web 服务器,Template.DataBase 和 Template.SeedData 表示与数据库通信的层。(实质上,最后两个是非常简单的实体框架项目,因此如果你已经了解 EF,那么你也就已经获得了 NOF 的持久性部分。)

解决方案中的最后一个项目 Template.Model 是大多数(如果不是全部)开发人员执行操作的地方。这是表示项目的域模型的类的集合,因此,开发人员需要执行的大部分工作应该并且通常将在这里完成。幸运的是,NOF 模板中已经包含一些示例代码 - 一个 Student 类型,代表了那些喜欢学习的人们,那么,让我们启动该模板并了解它的运行方式吧。确保将 Template.Server 设置为启动项目(它应该已经存在),然后按 F5 并松开。

运行裸对象

首先,Visual Studio 将启动服务器组件,并且由于 EF 的默认配置,将花费一些时间来从无到有生成要开始进行操作的数据库。开始几秒后,一些 JSON 将出现在浏览器窗口中 - 这是因为 Template.Server 实际上是一个 RESTful 服务器,这意味着它不仅可以通过 HTTP 运行,还可以回送描述整个选项集合的 JSON,用户可以根据需要利用这些选项。请注意,JSON 由外观基本上与超链接类似的以下项组成:“rel”表示“关系”,“href”表示要使用的 URL,“type”表示预期内容等等。这样,不想使用通用 UI(接下来我将进行介绍)的开发人员可以创建自己的知道如何使用回传的 JSON 的 UI。

让我们看看 NOF 所生成的 UI。在新的浏览器选项卡中,导航到 http://localhost:5001。返回的结果是......无任何修饰。它显然构建得不是非常美观,对于不熟悉的人来说,它看起来似乎没有实际的起点。但是,请记住 REST(正如 Fielding 最初所期望的)和 Smalltalk 具有类似的目标:一个通用 UI。因此无论域名如何,用户都知道如何操作它。本质上,NOF 通过许多静态类方法来共同构建 UI,并且这些方法将显示在该主页的顶级菜单中。单击它会显示三个选项:“所有学生”,“创建新学生”和“按姓名查找学生”。 很明显,这些是简单的 CRUD 方法(好吧,反正是 C 和 R),因此,让我们看看系统中都有谁。单击“所有学生”,将显示三个学生(在 Template.SeedData 项目启动时提供给 EF 的三个学生),并在返回列表的右上角显示一个奇怪的图标。单击此图标会将结果显示为一个表而不仅仅是列表,但因为学生只有 FullName 属性而没有其他内容,所以这似乎并不那么有趣。

从该列表中选择一个学生将占用整个页面。我们假设“James Java”已经改变了主意,并希望进行合法的姓名更改,所以从列表中选择他,当他的姓名出现时,选择“编辑”。请注意,UI 将更改以使其姓名可编辑,因此,让我们将其更改为“James Clr”。 单击“保存”,然后返回到只读 UI。让我们回过头再看一下学生列表,选择主页图标(左下角的房屋图标),然后再次返回到开始菜单。可以通过选择“菜单 | 所有学生”再次查找 James,但剪贴板图标将显示你最近使用的所有对象的列表。选中它,果然,“James Clr”就在那里。(“Student”包含对象类型,正好 James 就是对象类型。这在只有一种对象的演示中并不重要,但随着系统的增长,名为 James 的对象可能同时是 Student 和 RegistrationHistory。)

学生和学习

Student 模型似乎太过单一,因为学生不仅仅是一个名字而已!他们也可以属于一个学科,因此,让我们将它添加到模型中。停止 Visual Studio,打开 Template.Model 项目中的 Student.cs 文件。Student 当前类似于图 1 中所示的代码。

图 1 基本 Student 类型

using NakedObjects;
namespace Template.Model
{
  public class Student
  {
    // All persisted properties on a domain object must be 'virtual'
    NakedObjectsIgnore] // Indicates that this property
                        // will never be seen in the UI
    public virtual int Id { get; set; }
    [Title]// This property will be used for the object's title at
           // the top of the view and in a link
    public virtual string FullName { get; set; }
  }
}

向 Student 添加一个新属性,例如“Subject”,同时也是一个字符串,还可以是公共的或虚拟的,然后再次按 F5。由于 EF 中的开发默认设置,James Java 将失去他的姓名更改,但请注意所获得的内容:在整个 UI 和数据库中,现在就有了具有学科的学生。所有这些学科目前都是空的,但请注意,很多大学生也不知道他们正在学习什么。更重要的是,使用该单个属性,可以获得完整的 UI 支持以及数据库支持,而无需编写任何代码来创建 UI、验证输入或保留数据。

总结

这里显然还包含很多内容,但我只是跳过而没有详细介绍,为了对读者负责,我将进行进一步探索,所以我会通过一些文章来检查 NOF 并确定它的局限性。重要的是,特别是对于不面向消费者的应用程序,并非所有应用程序都必须采用手工精心制作的 UI 和数据库;有时候,应用程序的生成速度的重要性远远超过它的外观。在这种情况下,NOF 和其他 DDD 激发的工具类型可以完全像医生处方中规定的那样,甚至还有一些空间可以通过生成更加具有自定义性的 Angular(或其他 SPA 框架)前端来使应用程序更“漂亮”。下次我将探讨 NOF 中域类的一些选项和功能,我们将看到 NOF 如何处理常见的 UI 问题。在此期间......祝编码快乐!


Ted Neward 是本部位于西雅图的 Polytechnology 公司的顾问、讲师和导师。他写过大量文章,独自撰写并与人合著过十几本书,并在世界各地发表演讲。可通过 ted@tedneward.com 与他联系,也可阅读他的博客 blogs.tedneward.com