Interfaces
Ein Interface ist ein versionierter Vertrag, den Typen implementieren. Es benennt Attribute, Assoziationen und Methoden, die ein Typ bereitstellen muss, ohne die Position des Typs in der Vererbungshierarchie festzulegen. Abhängige Modelle können sich dann auf „alles, was Named-1 ist" verlassen statt auf einen konkreten Typ eines anderen Modells – so wird verhindert, dass sich eine Änderung dieses konkreten Typs in jedes abhängige Modell fortpflanzt.
Interfaces setzen ckLanguage: 2 voraus (siehe Übersicht).
Ein Interface deklarieren
Interfaces liegen in YAML-Dateien im Ordner interfaces/ des Modells (neben types/, attributes/, ...). Jede Datei hat den Wurzelschlüssel interfaces:. Der Compiler liest Interfaces ausschließlich aus diesem Ordner – ein interfaces:-Schlüssel in einer Datei eines anderen Ordners wird mit Warnung 110 ignoriert.
$schema: https://schemas.meshmakers.cloud/construction-kit-elements.schema.json
interfaces:
- interfaceId: Named-1
description: Anything with a human-readable name.
attributes:
- id: ${System}/Name
name: Name
- id: ${System}/Description
name: Description
isOptional: true
- interfaceId: Identified-1
description: A named thing with a serial number.
extends:
- ${this}/Named-1
attributes:
- id: ${this}/SerialNumber
name: SerialNumber
| Schlüssel | Erforderlich | Beschreibung |
|---|---|---|
interfaceId | ja | PascalCase-Name mit der Elementversion, z. B. Named-1. Die Version ist die Vertragsversion (siehe Versionierung). |
description | nein | Freitext. |
attributes | nein | Attribut-Member: id (Attributdefinition, wie bei Typen wiederverwendet), name (Zuweisungsname, den der implementierende Typ verwenden muss), isOptional. |
extends | nein | Interfaces, die dieses Interface erweitert, z. B. ${this}/Named-1. Mehrere Einträge sind erlaubt; Member werden geerbt. |
associations | nein | Assoziations-Member. |
methods | nein | Methoden-Member – dasselbe Schema wie Typ-Methoden. |
deprecated | nein | true markiert den Vertrag als veraltet; jede Referenz darauf erzeugt eine Compiler-Warnung (Meldung 124). |
visibility | nein | Public (Standard) oder Internal – siehe Zugriffsmodifikatoren. |
Ein Interface benötigt mindestens einen Member oder einen extends-Eintrag (Meldung 123). Ein Interface darf nicht denselben Namen wie ein Typ desselben Modells haben (Meldung 93), und jede Interface-Id darf nur einmal deklariert werden (Meldung 92) – Named-1 und Named-2 sind verschiedene Ids.
Innerhalb eines Interfaces darf jedes Attribut und jeder Member-Name nur einmal vorkommen (Meldung 127). Über ein Interface und die von ihm erweiterten Interfaces hinweg darf ein Name nicht für zwei Attribute stehen und ein Attribut nicht unter zwei Namen auftreten (Meldung 119); das identische Wiederholen eines geerbten Members ist erlaubt. extends muss bekannte Interfaces benennen, nicht das Interface selbst und keinen Zyklus bilden (Meldung 118).
Ein Interface implementieren
Ein Typ listet die Interfaces, die er implementiert, in implements auf:
types:
- typeId: Device
isAbstract: true
derivable: Any
derivedFromCkTypeId: ${System}/Entity
implements:
- ${this}/Identified-1
attributes:
- id: ${System}/Name
name: Name
- id: ${System}/Description
name: Description
isOptional: true
- id: ${this}/SerialNumber
name: SerialNumber
Der Compiler prüft jeden Typ, der ein Interface implementiert – einschließlich der Member aller Interfaces, die es erweitert:
| Regel | Meldung |
|---|---|
| Das Interface ist bekannt | 95 |
| Jeder erforderliche Attribut-Member wird mit derselben Attribut-Id durch den Typ oder einen seiner Basistypen zugewiesen | 96 |
Ein erforderlicher Member ist am Typ erforderlich (isOptional nicht true) | 97 |
| Die Zuweisung verwendet den Namen des Members | 98 |
Die Zuweisung ist nicht access: Hidden – Interface-Member müssen sichtbar sein | 99 |
| Jeder erforderliche Assoziations-Member wird bereitgestellt (siehe unten) | 121 |
| Ein Typ deklariert eine Interface-Methode nicht mit einem anderen Aufrufvertrag neu (Art, Parameter, Ergebnis, Fehlercodes) | 122 |
Optionale Member müssen nicht zugewiesen werden. Von einem Basistyp geerbte Member erfüllen das Interface, und implements wird an abgeleitete Typen vererbt: Sensor und Gateway im Beispiel implementieren Identified-1 und Named-1, weil Device es tut. Wer ein Interface implementiert, implementiert jedes Interface, das es erweitert.
Beispiel für einen fehlenden Member:
Error 96 types/types.yaml: Type 'Acme.Plant-1.0.0/Tag-1' implements 'Acme.Assets-1.0.0/Identified-1' but does not
assign required member 'Acme.Assets-1.0.0/SerialNumber-1' (name 'SerialNumber').
Assoziations-Member
Ein Assoziations-Member verlangt von implementierenden Typen eine ausgehende Assoziation mit einer bestimmten Rolle und einem kompatiblen Ziel:
interfaces:
- interfaceId: Monitoring-1
associations:
- id: ${this}/Monitors # association role
targetCkInterfaceId: ${this}/Identified-1 # or targetCkTypeId — exactly one of the two
multiplicity: N # One | ZeroOrOne | N; omitted = any
isOptional: false
- Genau eines von
targetCkTypeIdundtargetCkInterfaceIdist gesetzt; die Rolle und das Ziel müssen existieren (Meldung 120). - Ein Typ, der das Interface implementiert, muss eine eigene oder geerbte ausgehende Assoziation mit dieser Rolle besitzen, deren Ziel der Zieltyp des Members oder ein Untertyp davon ist – bei einem Interface-Ziel: ein Typ, der das Interface implementiert, oder eine auf dieses Interface (oder auf ein erweiterndes) eingeschränkte Assoziation. Ihre Multiplizität muss mindestens so streng sein wie die des Members (
Oneist strenger alsZeroOrOne, das strenger ist alsN). Andernfalls Meldung 121. Optionale Member werden nicht geprüft.
Eine Typ-Assoziation auf ein Interface einschränken
Eine Typ-Assoziation kann ihr Ziel mit targetCkInterfaceId auf Implementierer eines Interfaces einschränken:
types:
- typeId: Gateway
derivedFromCkTypeId: ${this}/Device
implements:
- ${this}/Monitoring-1
associations:
- id: ${this}/Monitors
targetCkTypeId: ${System}/Entity # still required
targetCkInterfaceId: ${this}/Identified-1 # target must also implement this interface
targetCkTypeId bleibt erforderlich: Das Interface schränkt das Ziel ein – ein gültiges Ziel leitet von targetCkTypeId ab und implementiert das Interface. Verwenden Sie ${System}/Entity als targetCkTypeId, um jeden Implementierer zu akzeptieren. Ein unbekanntes Interface ist Meldung 128.
Der Compiler nutzt die Einschränkung, um Interface-Assoziations-Member zu erfüllen (Regel 121), und das GraphQL-Navigationsfeld einer eingeschränkten Assoziation bietet nur die konkreten Zieltypen an, die das Interface implementieren. Beim Anlegen von Assoziationen zur Laufzeit wird das Ziel nur gegen targetCkTypeId geprüft.
Methoden-Member
Interfaces können Methodendefinitionen mit demselben Schema wie Typ-Methoden deklarieren (siehe Methodendefinitionen). Ein Typ, der das Interface implementiert, erbt sie (sie erscheinen unter den Methoden des Typs); eine Neudeklaration mit einem anderen Aufrufvertrag (kind, die Parameter nach Name mit ihrem Typ, isOptional und sensitive, das Ergebnis, die Menge der Fehlercodes) ist Meldung 122; Beschreibungen, Autorisierung und Ausführung dürfen abweichen, und es gelten die Autorisierung und Ausführung des neu deklarierenden Typs. Jede Änderung dieses Vertrags an einer Interface-Methode ist eine MAJOR-Versionsänderung des Modells des Interfaces, weil neu deklarierende Typen in anderen Modellen nicht mehr kompilieren würden. Interface-Methoden folgen denselben Regeln wie Typ-Methoden (Meldungen 100–104).
Ein Interface versionieren
Die Elementversion in interfaceId (Named-1) ist die Vertragsversion. Sie ist unabhängig von der Modellversion.
- Der Vertrag eines veröffentlichten Interfaces ändert sich nicht. Das Hinzufügen, Entfernen oder Ändern eines Members (Attribut, Assoziation oder Methode, einschließlich
isOptional) sowie das Hinzufügen oder Entfernen einesextends-Eintrags sind Major-Änderungen des Modells. - Veröffentlichen Sie stattdessen den geänderten Vertrag als neues Interface-Element neben dem alten –
Named-2nebenNamed-1, im selben Modell und derselben Modell-Major-Version. Ein neues Interface ist eine Minor-Änderung. - Typen können während einer Übergangszeit beide Versionen implementieren; Abhängige wechseln zu
Named-2, wenn sie es wählen. - Markieren Sie die alte Version mit
deprecated: true(Minor). Abhängige erhalten Warnung 124 für jedesimplements,extends, jedes Interface-Assoziationsziel und jedestargetCkInterfaceId, das sie noch referenziert. Entfernen Sie sie erst mit der nächsten Modell-Major-Version.
# interfaces/named.yaml
interfaces:
- interfaceId: Named-1
deprecated: true
attributes:
- id: ${System}/Name
name: Name
- interfaceId: Named-2
attributes:
- id: ${System}/Name
name: Name
- id: ${this}/ShortName
name: ShortName
# types/tag.yaml
types:
- typeId: Tag
derivedFromCkTypeId: ${System}/Entity
implements:
- ${this}/Named-1 # warning 124 (deprecated)
- ${this}/Named-2
attributes:
- id: ${System}/Name
name: Name
- id: ${this}/ShortName
name: ShortName
Interface-Ids tragen ihre Version immer mit – auch in kompilierten Modellen und Referenzen (Acme.Assets-1.0.0/Named-1, oder Acme.Assets@1/Named-1 mit Range Retention).
Die vollständige SemVer-Klassifikation steht in den Versionierungsregeln.
Interfaces aus anderen Modellen referenzieren
Ein Interface einer Abhängigkeit wird wie jedes andere Element referenziert: ${Acme.Assets}/Identified-1. Ein Modell kann es implementieren, erweitern und als Assoziationsziel verwenden – vorausgesetzt, das Interface ist nicht visibility: Internal (Meldung 112):
# Acme.Plant (ckLanguage: 2, depends on Acme.Assets-[1.0,2.0))
types:
- typeId: Pump
derivedFromCkTypeId: ${Acme.Assets}/Device # Device declares derivable: Any
implements:
- ${Acme.Assets}/Calibratable-1
In GraphQL und im Code
- Jedes CK-Interface wird zu einem GraphQL-Interface-Typ, den die implementierenden Entitätstypen implementieren (
Named-1→AcmeAssetsNamed,Named-2→AcmeAssetsNamed2);extendsentspricht der GraphQL-Interface-Vererbung – siehe GraphQL-Mapping. Das Abfragen von Entitäten nach Interface ist noch nicht verfügbar. - Der Source Generator erzeugt pro CK-Interface ein C#-Interface
IRt<Name>(siehe Generierter Code). - Die generierte Modelldokumentation listet pro Modell eine Seite
Interfaces.md(Member einschließlich geerbter,Extends, Assoziations- und Methodentabellen, eine „Deprecated"-Markierung) sowie pro Typ eine „Implements"-Zeile.