Teil 1: Modellierung und Produktkonfiguration
Arbeiten mit Modell und Sourcecode
Im zweitem Schritt des Tutorials erweitern wir unser Modell und arbeiten mit dem generierten Sourcecode.
Als erstes erweitern wir die Klasse „HausratVertrag“
um ein Attribut zahlweise
. Wenn der
Editor mit der Vertragsklasse nicht mehr geöffnet ist, öffnen Sie diesen nun durch Doppelklick im Modell-Explorer. In dem Editor klicken Sie auf den Button Neu… im Abschnitt Attribute. Es öffnet sich der folgende Dialog:
Die Felder haben folgende Bedeutung:
Feld | Bedeutung |
---|---|
Name |
Der Name des Attributs |
Checkbox |
Indikator, ob dieses Attribut bereits in einer Superklasse definiert worden ist und in dieser Klasse lediglich Eigenschaften wie z.B. der Default Value überschrieben werden [5] |
Datatype/Datentyp |
Datentyp des Attributs |
Modifier/Sichtbarkeit |
Analog zum Modifier in Java. Der zusätzliche Modifier published bedeutet, dass die Eigenschaften ins published Interface aufgenommen wird. [6] |
Attribute type/Typ des Attributs |
Der Typ des Attributs. * änderbar |
5 Entspricht der @override Annotation in Java 5.
6 Hinweis: Die Aktivierung/Deaktivierung der Generierung der Published-Inferfaces erfolgt über das Kontextmenü > Eigenschaften > Faktor-IPS Code Generator des entsprechenden Projekts
Geben Sie als Namen zahlweise
und als Datentyp Integer ein. Wenn Sie auf den Browse Button neben dem Feld klicken, öffnet sich eine Liste mit den verfügbaren Datentypen. Alternativ dazu können Sie wie in Eclipse üblich auch mit STRG-Space eine Vervollständigung durchführen. Wenn Sie zum Beispiel „D“ eingeben und STRG-Space drücken, sehen Sie alle Datentypen, die mit „D“ beginnen. Die anderen Felder lassen Sie wie vorgegeben und drücken jetzt OK, danach speichern Sie die geänderte Vertragsklasse.
Der Codegenerator hat nun bereits die Java-Sourcefiles aktualisiert. Die Klasse „HausratVertrag“
enthält nun Zugriffsmethoden für das Attribut und speichert den Zustand in einer privaten Membervariable (Falls in Ihrer .ipsproject-Datei in der Einstellung "additionalAnnotations" @IpsGenerated
inkludiert ist, wird diese vor generierten Faktorips-Methoden zusätzlich gesetzt).
/**
* Diese Konstante enthaelt den Namen der Eigenschaft zahlweise.
*
* @generated
*/
public static final String PROPERTY_ZAHLWEISE = "zahlweise";
/**
* Die maximal erlaubten Werte fuer die Eigenschaft zahlweise.
*
* @generated
*/
@IpsAllowedValues("zahlweise")
public static final ValueSet<Integer> MAX_ALLOWED_VALUES_FOR_ZAHLWEISE = new UnrestrictedValueSet<>(true);
/**
* Der Vorgabewert des Attributs zahlweise.
*
* @generated
*/
@IpsDefaultValue("zahlweise")
public static final Integer DEFAULT_VALUE_FOR_ZAHLWEISE = null;
/**
* Membervariable fuer zahlweise.
*
* @generated
*/
private Integer zahlweise = DEFAULT_VALUE_FOR_ZAHLWEISE;
/**
* Erzeugt eine neue Instanz von HausratVertrag.
*
* @generated
*/
public HausratVertrag() {
super();
}
/**
* Gibt den erlaubten Wertebereich fuer das Attribut zahlweise zurueck.
*
* @generated
*/
@IpsAllowedValues("zahlweise")
public ValueSet<Integer> getAllowedValuesForZahlweise() {
return MAX_ALLOWED_VALUES_FOR_ZAHLWEISE;
}
/**
* Gibt den Wert des Attributs zahlweise zurueck.
*
* @generated
*/
@IpsAttribute(name = "zahlweise", kind = AttributeKind.CHANGEABLE, valueSetKind = ValueSetKind.AllValues)
public Integer getZahlweise() {
return zahlweise;
}
/**
* Setzt den Wert des Attributs zahlweise.
*
* @generated
*/
@IpsAttributeSetter("zahlweise")
public void setZahlweise(Integer newValue) {
this.zahlweise = newValue;
}
Das JavaDoc für einige Methoden sind mit einem @generated
markiert. Dies bedeutet, dass die Methode zu 100% generiert wird. Bei einer erneuten Generierung wird dieser Code genau so wieder erzeugt, unabhängig davon, ob er in der Datei gelöscht oder modifiziert worden ist. Änderungen seitens des Entwicklers werden also überschrieben. Möchten Sie die Methode modifzieren, so fügen Sie hinter die Annotation @generated
ein NOT
hinzu. Probieren wir das einmal aus. Fügen Sie jeweils die Zeile "System.out.println" (siehe folgende Codestelle) in die Getter- und Setter-Methode ein, und ergänzen bei der Methode setZahlweise()
mit NOT
hinter der Annotation:
/**
* Gibt den Wert des Attributs zahlweise zurueck.
*
* @generated
*/
@IpsAttribute(name = "zahlweise", kind = AttributeKind.CHANGEABLE, valueSetKind = ValueSetKind.AllValues)
public Integer getZahlweise() {
System.out.println("getZahlweise"); (1)
return zahlweise;
}
/**
* Setzt den Wert des Attributs zahlweise.
*
* @generated NOT
*/
@IpsAttributeSetter("zahlweise")
public void setZahlweise(Integer newValue) {
System.out.println("setZahlweise"); (1)
this.zahlweise = newValue;
}
1 | Handgeschriebener Code |
Generieren Sie jetzt den Sourcecode für die Klasse „HausratVertrag“
neu. Dies können Sie wie in Eclipse üblich auf zwei Arten erreichen:
-
Entweder bauen Sie mit Project ► Clean das gesamt Projekt neu, oder
-
Sie speichern die Modellbeschreibung der Klasse
„HausratVertrag“
erneut.
Nach dem Generieren ist das System.out.println(…)
-Statement aus der Getter-Methode entfernt worden, in der Setter-Methode ist es erhalten geblieben.
Methoden und Attribute, die neu hinzugefügt werden, bleiben nach der Generierung erhalten. Auf diese Weise kann der Sourcecode beliebig erweitert werden.
Nun erweitern wir noch die Modelldefinition der Zahlweise um die erlaubten Werte. Öffnen Sie dazu den Dialog zum Bearbeiten des Attributes und wechseln auf die zweite Tabseite. Bisher sind alle Werte des Datentyps als zulässige Werte für das Attribute erlaubt. Wir schränken dies nun auf 1, 2, 4, 12 für jährlich, halbjährlich, quartalsweise und monatlich ein. Ändern Sie hierzu den Typ auf „Aufzählung“ und geben Sie in die Tabelle die Werte 1, 2, 4 und 12 ein [7].
7 Faktor-IPS unterstützt auch die Definition von Enums. Auf dieses Feature wird an dieser Stelle aber verzichtet. Darüber hinaus können über einen Extension Point beliebige Java Klassen als Datentyp registriert werden. Diese Java-Klassen sollten dabei entsprechend des Musters ValueObject realisiert sein.
Setzen Sie nun einmal den Default Value auf 0. Faktor-IPS markiert den Default Value mit einer Warnung, da der Wert nicht in der im Modell erlaubten Wertemenge enthalten ist.
zahlweise
Es handelt sich also um einen möglichen Fehler im Modell. Lassen wir das aber für einen Augenblick so stehen. Das gibt uns die Gelegenheit die Fehlerbehandlung von Faktor-IPS zu erläutern. Schließen Sie dazu den Dialog und speichern die Vertragsklasse. Im Problems-View von Eclipse wird nun die gleiche Warnung wie im Dialog angezeigt. Faktor-IPS lässt Fehler und Inkonsistenzen im Modell zu und informiert den Benutzer darüber im Eclipse-Stil, also in den Editoren und als so genannte Problemmarker, die im Problems-View und in den Explorern sichtbar sind.
Löschen Sie die „0“ als Vorbelegungswert und speichern Sie die Vertragsklasse. Die Warnung wird damit wieder aus dem Problems-View entfernt.
Faktor-IPS generiert eine Warnung und keinen Fehler, da es durchaus sinnvoll sein kann, wenn der Defaultwert nicht im Wertebereich ist. Insbesondere gilt dies für den Defaultwert null. Wird zum Beispiel ein neuer Vertrag angelegt, so kann es gewolltes Verhalten sein, dass die Zahlweise nicht vorbelegt ist sondern noch null ist, um die Eingabe der Zahlweise durch den Benutzer zu erzwingen. Erst wenn der Vertrag vollständig erfasst ist, muss auch die Bedingung erfüllt sein, dass die Eigenschaft Zahlweise einen Wert aus dem Wertebereich enthält.
Das Kapitel schließen wir mit der Definition einer Klasse „HausratGrunddeckung“
und der Kompositionsbeziehung zwischen „HausratVertrag“
und „HausratGrunddeckung“
gemäß dem folgenden Diagramm:
Legen Sie zunächst die Klasse „HausratGrunddeckung“
analog zur Klasse „HausratVertrag“
an. Wechseln Sie anschließend zu der Klasse „HausratVertrag“
. Starten Sie den Assistenten zur Anlage einer neuen Beziehung. Klicken Sie auf den Button Neu… im Abschnitt Beziehungen. [8]
8 In Faktor-IPS wird in Übereinstimmung mit der UML-Begriff Association verwendet. In dem Tutorial verwenden wir den im Sprachgebrauch üblicheren Begriff Beziehung.
Als Target wählen Sie bitte die gerade angelegte Klasse „HausratGrunddeckung“ aus. Hier steht Ihnen wieder die Vervollständigung mit STRG-Space zur Verfügung. Bereich Überschreiben / Abgeleitete Vereinigung ignorieren Sie zunächst. Das Konzept wird im Tutorial zur Modellpartitionierung erläutert. Danach Button Next drücken.
Auf der nächsten Seite geben Sie als Minimale Kardinalität 1 und als Maximale Kardinalität 1 ein, als Rollennamen HausratGrunddeckung
bzw. HausratGrunddeckungen
. Die Mehrzahl wird verwandt, damit der Codegenerator verständlichen Sourcecode generieren kann.
Auf der nächsten Seite können Sie auswählen, ob es auch eine Rückwärtsbeziehung (Inverse Beziehung) von „HausratGrunddeckung“
zu „HausratVertrag“
geben soll. Beziehungen in Faktor-IPS sind immer gerichtet, so ist es auch möglich die Navigation nur in eine Richtung zuzulassen. Hier wählen Sie bitte Neue inverse Beziehung und gehen zur nächsten Seite.
Im nächsten Fenster geben Sie die Rollenbezeichnung der inversen Beziehung ein.
Mit Finish legen Sie die beiden Beziehungen (vorwärts und rückwärts) an und speichern danach noch die Klasse „HausratVertrag“
. Wenn Sie sich jetzt die Klasse „HausratGrunddeckung“
ansehen, ist dort die Rückwärtsbeziehung eingetragen.
Zum Abschluss werfen wir noch einen kurzen Blick in den generierten Sourcecode. In die Klasse „HausratVertrag“
wurden Methoden generiert, die Grunddeckung dem „HausratVertrag“
hinzuzufügen. In der Klasse „HausratGrunddeckung“
gibt es eine Methode, um zum „HausratVertrag“
zu navigieren. Wenn sowohl die Vorwärts- als auch die Rückwärtsbeziehung im Modell definiert ist, werden hierbei beide Richtungen berücksichtigt. Das heißt nach dem Aufruf von setHausratGrunddeckung(HausratGrunddeckung d)
auf einer Instanz v
von „HausratVertrag“
liefert d.getHausratVertrag()
wieder v
zurück. Dies zeigt ein kurzer Blick in die Implementierung der Methode setHausratGrunddeckung()
in der Klasse „HausratVertrag“
:
@IpsAssociationAdder(association = "HausratGrunddeckung")
public void setHausratGrunddeckung(HausratGrunddeckung newObject) {
if (hausratGrunddeckung != null) {
hausratGrunddeckung.setHausratVertragInternal(null);
}
if (newObject != null) {
newObject.setHausratVertragInternal(this);
}
hausratGrunddeckung = newObject;
}
Der Vertrag wird in der Deckung als der Vertrag gesetzt, zu dem die Deckung gehört (zweites if
-Statement der Methode).