了解模型、类和关系

特定于域的语言(DSL)是由其 DSL 定义文件及您可能编写的任何自定义程序代码所定义的。 DSL 解决方案中的大多数程序代码都是从此文件生成的。

本主题介绍 DSL 定义的核心功能。

DSL 定义

打开 Dsl\DslDefinition.dsl时,Visual Studio 窗口如下图所示。

DSL 设计器

DSL 定义中最重要的信息显示在 DSL 定义关系图中。 其他信息(也是 DslDefinition.dsl 的一部分)显示在 DSL 资源管理器中,该资源管理器通常显示在关系图的一侧。 使用关系图处理最频繁的任务,使用 DSL 资源管理器进行更高级自定义。

DSL 定义关系图显示定义模型元素的域类,以及定义模型元素之间的链接的关系。 它还显示用于向用户显示模型元素的形状和连接线。

带泳道的 DSL 设计器

在 DSL 定义中选择项时,在关系图或 DSL 资源管理器中选择项时,“属性”窗口中会显示有关它的信息。 其他信息可能会在 DSL 详细信息窗口中显示。

模型是 DSL 的实例

模型是用户创建的 DSL 实例。 模型包含模型元素,它们是你定义的域类的实例,以及元素之间的链接,这些元素是你定义的域关系的实例。 模型还可以具有形状和连接线,这些形状和连接线在关系图上显示模型元素和链接。 DSL 定义包括形状类、连接线类和关系图的类。

DSL 定义也称为 域模型。 DSL 定义或域模型是特定于域的语言的设计时表示形式,而模型是特定于域的语言的运行时实例化。

域类定义模型元素

域类用于在域中创建各种元素,域关系是元素之间的链接。 它们是元素和链接的设计时表示形式,这些元素和链接将在创建模型时由设计特定语言的用户实例化。

此图显示了由音乐库 DSL 用户创建的模型。 音乐专辑由包含歌曲列表的框表示。 艺术家以圆角框表示,并连接到他们贡献的专辑。

生成的 DSL 的实例模型

DSL 定义分隔两个方面。 模型关系图上的模型元素的外观是使用形状类和连接线类定义的。 模型中携带的信息是使用域类和域关系定义的。

下图显示了音乐库的 DSL 定义中的域类和关系。

嵌入和引用关系

此图显示了四个域类:音乐、专辑、艺术家和歌曲。 域类定义域名、标题等域属性。 在实例模型中,其中一些属性的值显示在关系图上。

类之间有域关系:MusicHasAlbums、MusicHasArtists、AlbumbHasSongs 和 ArtistAppearedOnAlbums。 关系具有多重性,例如 1..1、0.*。 例如,每首歌曲都必须通过 AlbumHasSongs 关系与一张专辑建立唯一关联。 每个专辑可以有任意数量的歌曲。

重新排列 DSL 定义图

请注意,领域类可以在 DSL 定义图上出现多次,就像图片中的 Album 一样。 始终有一个主要视图,并且可能有一些 引用 视图。

若要重新排列 DSL 定义图,可以:

  • 使用 “在此处引入树 ”和 “拆分树 ”命令交换主视图和引用视图。 右键单击单个域类以查看这些命令。

  • 按 Ctrl+向上键和 Ctrl+向下对域类和形状类重新排序。

  • 使用每个形状右上角的图标折叠或展开类。

  • 单击域类底部的减号(-)折叠树的各个部分。

继承

可以使用继承定义域类。 若要创建继承派生,请单击“继承”工具,单击派生类,然后单击基类。 模型元素具有在其自己的域类上定义的所有属性,以及继承自基类的所有属性。 它还在关系中继承其角色。

还可以在关系、形状和连接线之间使用继承。 继承必须保留在同一组中。 形状无法继承自域类。

域关系

模型元素可以通过关系链接。 链接始终为二进制;它们完全链接两个元素。 但是,任何元素可以有多个指向其他对象的链接,并且同一对元素之间甚至可能有多个链接。

正如可以定义不同元素类一样,可以定义不同的链接类。 链接的类称为 域关系。 域关系指定其实例可以连接的元素类。 关系的每个结尾称为 角色,域关系定义两个角色的名称,以及关系本身的名称。

有两种类型的域关系:嵌入关系和引用关系。 在 DSL 定义关系图中,嵌入关系在每个角色上都有实线,引用关系具有虚线。

嵌入关系

模型中的每个元素(其根除外)都是一个嵌入链接的目标。 因此,整个模型构成了嵌入链接的单个树。 嵌入关系表示包含或所有权。 以这种方式相关的两个模型元素也称为父元素和子元素。 据说孩子嵌入在父母中。

嵌入链接通常不显式显示为关系图上的连接器。 相反,它们通常通过包含关系表示。 模型的根由关系图表示,嵌入其中的元素显示为关系图上的形状。

在此示例中,根类 Music 具有一个嵌入关系 MusicHasAlbums 指向 Album,该类又通过嵌入关系 AlbumHasSongs 指向 Song。 歌曲在每张专辑内的列表中显示为条目。 音乐还有一个嵌入 MusicHasArtists 到艺术家类,其实例也显示为图表上的形状。

默认情况下,嵌入元素在其父元素被删除时会自动删除。

当模型以 XML 格式保存到文件中时,嵌入的元素将嵌套在其父级内,除非你已自定义序列化。

注释

嵌入与继承不同。 嵌入关系中的子级不会继承父级的属性。 嵌入是模型元素之间的链接类型。 继承是类之间的关系,不会在模型元素之间创建链接。

嵌入规则

实例模型中的每个元素都必须是一个嵌入链接的目标,但模型的根节点除外。

因此,除根类以外的每个非抽象域类都必须是至少一个嵌入关系的目标,或者它必须从基类继承嵌入。 类可以是两个或多个嵌入的目标,但其实例模型元素一次只能有一个父元素。 从目标到源的多重性必须是 0..1 或 1..1。

浏览器显示嵌入树

DSL 定义还会创建一个浏览器,用户会在其模型图旁边看到它。

生成的 DSL 浏览器

浏览器显示模型中的所有元素,即便是那些未定义形状的元素。 它显示元素和嵌入关系,但不显示引用关系。

若要查看元素的域属性的值,用户在模型关系图或模型资源管理器中选择一个元素,并打开“属性”窗口。 它显示所有域属性,包括关系图上未显示的属性。 在此示例中,每个歌曲都有一个 Title 和一个流派,但关系图上只显示标题的值。

引用关系

引用关系表示不嵌入的任何类型的关系。

引用关系通常以形状之间的连接线的形式显示在关系图上。

在模型的 XML 表示形式中,使用 名字对象 表示两个元素之间的引用链接。也就是说,名字对象是唯一标识模型中每个元素的名称。 每个模型元素的 XML 节点都包含一个节点,该节点指定关系的名称和其他元素的名称。

角色

每个域关系都有两个角色,一个源角色和一个目标角色。

在下图中, Publisher 域类与 PublisherCatalog 域关系之间的行是源角色。 域关系和 Album 域类之间的行是目标角色。

角色和属性。

编写遍历模型的程序代码时,与关系关联的名称尤其重要。 例如,生成 DSL 解决方案时,生成的类 Publisher 有一个属性 Catalog,该属性目录是相册的集合。 类 Album 有一个属性 Publisher,它是类 Publisher 的一个实例。

在 DSL 定义中创建关系时,会提供属性和关系名称的默认值。 但是,可以更改它们。

多重性

多重性指定域关系中可以具有相同角色的元素数。 在此示例中,目录角色上的零对多(0..*)多重性设置指定发布者域类的任何实例可以具有任意数量的PublisherCatalog关系链接,具体数量可根据需要给予。

可以通过在关系图上键入或修改Multiplicity属性窗口中的属性来配置角色的多重性。 下表描述了此属性的设置。

多重性类型 Description
0..* (零到多) 域类的每个实例可以具有关系的多个实例,或者没有关系实例。
0..1 (零到 1) 域类的每个实例最多只能有一个关系实例,或者没有关系实例。
1..1 (一) 域类的每个实例可以有一个关系实例。 不能从角色类的任何实例创建此关系的多个实例。 如果启用了验证,则当角色类的任何实例没有关系实例时,将显示验证错误。
1..* (一对多) 在特定角色下具有此多重性的类的每个实例可以拥有多个关联实例,并且每个实例必须至少拥有一个关联实例。 如果启用了验证,则当角色类的任何实例没有关系实例时,将显示验证错误。

作为类的域关系

链接在 Store 中表示为 LinkElement 的实例,它是 ModelElement 的派生类。 可以在域模型关系图中定义这些属性。

你还可以将某个关系设置为其他关系的源或目标。 在域模型关系图中,右键单击域关系,然后单击“ 显示为类”。 将会出现一个额外的类框。 然后,你可以将其与关系连接起来。

可以部分通过继承来定义关系,就像使用域类一样。 选择派生关系并在“属性”窗口中设置 基关系

派生关系细化其基本关系。 它链接的域类应派生自或与基关系链接的类相同。 在模型中创建派生关系的链接时,它是派生关系和基关系的实例。 在程序代码中,可以使用基类或派生类生成的属性导航到链接的相反端。