接口进阶:继承、关系与 Action
继承叠出更宽的形状;接口关系规定实现方必须有哪条线;接口上的 Action 是怎么设计的、AIDC 今天怎么做。

本课目标
读完这一课,你将能够
- 用继承把接口叠起来,说清实现子接口意味着什么
- 给接口声明关系,让实现方映射到自己的链接端
- 说清接口上的 Action 是怎么设计的,以及 AIDC 今天怎么做
继承:一层加一点
上一节的 Schedulable 是一个平的接口。接口还可以继承别的接口,拿到它全部的属性和关系,再加上自己的。示例工厂里,「有人负责」是比「可排期」更宽的形状:先有 Owned(一个 ownerId),Schedulable 继承它。假设三种订单都有 ownerId,由 assign-owner 维护。
[
{ "kind": "sharedPropertyType", "apiName": "ownerId", "title": "Owner", "schema": { "type": "string" } },
{
"kind": "interfaceType",
"apiName": "Owned",
"title": "Owned",
"schema": { "properties": { "ownerId": { "required": true } } }
},
{
"kind": "interfaceType",
"apiName": "Schedulable",
"title": "Schedulable",
"schema": {
"extendsInterfaces": ["Owned"],
"properties": { "dueAt": { "required": true }, "status": { "required": true } }
}
}
]
- 实现
Schedulable的类型,同时也实现了Owned。按Owned查,三种订单一样会出来。 - 继承来的必填属性也要映射;名字相同的可以省略,这里的
ownerId就是。 - 一个接口最多继承 10 个接口,一个对象类型最多直接实现 20 个接口,继承不能成环。
- 改父接口会波及所有子接口的实现方,所以父接口要小而稳。去掉一层继承,或给父接口加必填属性,都会被标为破坏性变更。
不要做深继承链。建议组合,而不是层层继承:工单将来还要「可检验」,就让它同时实现 Schedulable 和 Inspectable,不要发明组合类型。
SchedulableWorkOrder、InspectableWorkOrder……每多一种能力,就多一个组合类型。
workOrder 同时实现 Schedulable 和 Inspectable;新能力只需要再加一个接口。
接口关系:规定必须有哪条线
接口也能声明关系,这叫关系约束(link type constraint)。约束写清四件事:目标(一个对象类型,或另一个接口)、基数(ONE 或 MANY)、是否必填,以及在代码里叫什么。实现方用自己已有的链接端去满足它。
例子:销售订单的对方是客户,采购订单的对方是供应商。两个目标类型不同,所以目标要写成接口。先建 Party(有 name),让 customer 和 supplier 各自实现它,再给 Schedulable 加一个指向 Party 的 counterparty 关系。
[
{
"kind": "interfaceType",
"apiName": "Party",
"title": "Party",
"schema": { "localProperties": { "name": { "type": "string", "required": true } } }
},
{
"kind": "interfaceType",
"apiName": "Schedulable",
"title": "Schedulable",
"schema": {
"extendsInterfaces": ["Owned"],
"properties": { "dueAt": { "required": true }, "status": { "required": true } },
"links": {
"counterparty": {
"title": "Counterparty",
"linkedEntity": { "interfaceType": "Party" },
"cardinality": "ONE",
"required": false
}
}
}
}
]
实现方在 implements 里用 links,把接口关系映射到自己的链接端。可选的关系可以不映射:工单没有对方,就不写。
{
"salesOrder": [{ "interface": "Schedulable", "properties": { "dueAt": "promisedDate" }, "links": { "counterparty": "customer" } }],
"purchaseOrder": [{ "interface": "Schedulable", "properties": { "dueAt": "expectedAt" }, "links": { "counterparty": "supplier" } }],
"workOrder": [{ "interface": "Schedulable", "properties": { "dueAt": "dueDate" } }]
}
把 required 改成 true,工单就必须补上,否则定义被拒:workOrder: must implement required interface link Schedulable.counterparty。所以新加的接口关系先写成可选,等实现方都到位再收紧。
接口上的 Action
接口上的 Action 有两层设计。接口 Action 规则:创建、修改、删除、建关系,作用在「实现某个接口的对象」上,所以只能改接口共有的属性,比如给所有订单推迟到期日。接口的 Action 类型约束(这一层还在早期阶段):接口只规定实现方要有一个什么样的 Action,包括名字和参数的形状,逻辑仍由各类型自己的 Action 去写。
接口 Action 规则要小心几点:提交条件对所有实现类型一视同仁,所以权限要按最宽的情况去想;修改规则不能改主键;创建规则要求接口和规则里有一个能当主键的属性。另外,它目前不写 Action 日志,也不能配合函数使用。
| 它做什么 | AIDC 今天 | |
|---|---|---|
| 对接口写创建、修改、删除规则 | 对任一实现类型的对象操作,只能改接口共有的属性 | 目前还没有:规则的 objectType 必须是具体的对象类型 |
| 接口的 Action 类型约束 | 规定实现方要有什么样的 Action:名字和参数的形状 | 目前还没有 |
| 引用任一实现类型的对象当参数 | 接口引用参数:可以选到实现了接口的任意对象 | 有:interfaceObject 参数,按对象 RID 传入;改数据的规则仍要写具体类型 |
所以今天的做法是:为每种类型各写一个 Action,参数名、提交条件和日志保持同一个形状。比如「推迟到期日」:postpone-sales-order、postpone-work-order、postpone-purchase-order,都带 newDueDate 和 reason,都开 actionLog。这是先搭好接口、暂时按类型重复:不比没有接口更费事,将来合并也有路。
下一节回到判断力:这些构件都会用了,什么该建成对象、属性、关系,还是接口?
要点
- 子接口继承父接口的属性和关系;实现子接口,就同时实现了它继承的接口。
- 组合优于深继承:一个类型实现几个小接口,不发明组合类型。
- 接口关系规定实现方必须有哪条链接,目标可以是对象类型,也可以是另一个接口;新约束先写成可选。
- 接口上的 Action 分规则和类型约束两层;AIDC 目前还没有,今天为每种类型各写一个同形状的 Action。
练一练
给接口加继承、关系和动作
接着上一节的 ontology/ 目录做。
把 Owned 加进定义,让 Schedulable 继承它。三种订单的 implements 各要多映射哪个属性?哪些可以省略?
写出 Party 和 counterparty 的完整定义,让 customer 和 supplier 实现 Party。counterparty 该不该设成必填?想想工单。
assign-owner 对所有有 ownerId 的类型都一样。按接口 Action 的设计,它该是接口 Action 吗?在 AIDC 里今天怎么落地?写下你的方案。
小测
选一个答案,马上看解析。
Q1workOrder 实现了 Schedulable,而 Schedulable 继承 Owned。按 Owned 查询,会包含工单吗?
继承来的接口自动算数。工单仍然要给继承来的必填属性做映射,但不用再单独声明实现 Owned。
Q2Schedulable.counterparty 是可选的接口关系,工单没有对方。会怎样?
只有必填的接口关系才强制实现方映射。想强制,就把 required 设成 true,并接受工单必须补上。
Q3今天能不能对 Schedulable 直接写一个 modifyObject 规则,一次改所有订单的到期日?
interfaceObject 只能用来引用对象,改数据的规则仍然指向具体的对象类型。接口 Action 规则目前还没有。