Modellierung und Produktkonfigurierung

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 Attributes. Es öffnet sich der folgende Dialog:

dialog attribut
Figure 1. Dialog zum Anlegen eines neuen Attributs

Die Felder haben folgende Bedeutung:

Table 1. Attribut Felder
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.

Attribute type/Typ des Attributs

Der Typ des Attributs.
* änderbar
Änderbare Eigenschaften, also solche mit Getter- und Setter- Methoden
* konstant
Konstante, nicht änderbare Eigenschaft
* abgeleitet (cached, Berechnung durch expliziten Methodenaufruf)
Abgeleitete Eigenschaften im UML Sinne. Die Eigenschaft wird durch einen expliziten Methodenaufruf berechnet und das Ergebnis ist danach über die Getter-Methode abfragbar. Zum Beispiel kann die Eigenschaft bruttobeitrag durch eine Methode berechneBeitrag berechnet und danach über getBruttobeitrag() abgerufen werden.
* abgeleitet (Berechnung bei jedem Aufruf der Gettermethode)
Abgeleitete Eigenschaften im UML Sinne. Die Eigenschaft wird bei jedem Aufruf der Getter-Methode berechnet. Zum Beispiel kann das Alter einer versicherten Person bei jedem Aufruf von getAlter() aus dem Geburtstag ermittelt werden.

[5] Entspricht der @override Annotation in Java 5.

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.

/**
 * Membervariable fuer zahlweise.
 *
 * @generated
 */
private Integer zahlweise = null;

/**
 * Erzeugt eine neue Instanz von HausratVertrag.
 *
 * @generated
 */
public HausratVertrag() {
	super();
	productConfiguration = new ProductConfiguration();
}

/**
 * Gibt den Wert des Attributs zahlweise zurueck.
 *
 * @generated
 */
@IpsAttribute(name = "zahlweise", kind = AttributeKind.CHANGEABLE, valueSetKind = ValueSetKind.Enum)
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 die Membervariable und für die Getter-Methode ist 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 eine Zeile 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.Enum)
public Integer getZahlweise() {
	System.out.println("getZahlweise");
	return zahlweise;
}
/**
 * Setzt den Wert des Attributs zahlweise.
 *
 * @generated NOT
 */
@IpsAttributeSetter("zahlweise")
public void setZahlweise(Integer newValue) {
	System.out.println("setZahlweise");
	this.zahlweise = newValue;
}

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 ProjectClean 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 [6].

[6] 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.

wertebereich zahlweise
Figure 2. Wertebereich für das Attribut 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.

problems view
Figure 3. Anzeige von Fehlern im Problems-View

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:

modell vertragsseite
Figure 4. Modell der Vertragsseite

Legen Sie zunächst die Klasse „HausratGrunddeckung“ analog zur Klasse „HausratVertrag“ an. Wechseln Sie zu der Klasse „HausratVertrag“. Starten Sie den Assistenten zur Anlage einer neuen Beziehung. Klicken Sie auf den Button Neu… im Abschnitt Beziehungen. [7]

[7] In Faktor-IPS wird in Übereinstimmung mit der UML der Begriff Association verwendet. In dem Tutorial verwenden wir den im Sprachgebrauch üblicheren Begriff Beziehung.

neue beziehung
Figure 5. Anlegen einer neuen 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.

eigenschaften beziehung
Figure 6. Rollennamen und Kardinalitäten einer Beziehung

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.

inverse beziehung
Figure 7. Neue inverse Beziehungs anlegen

Im nächsten Fenster geben Sie die Rollenbezeichnung der inversen Beziehung ein.

eigenschaften inverse beziehung
Figure 8. Eigenschaften der inversen Beziehung festlegen

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() [8] in der Klasse „HausratVertrag“:

public void setHausratGrunddeckung(HausratGrunddeckung newObject) {
	if (hausratGrunddeckung != null) {
		hausratGrunddeckung.setHausratVertragInternal(null);
	}
	if (newObject != null) {
		((HausratGrunddeckung) newObject).setHausratVertragInternal(this);
	}
	hausratGrunddeckung = (HausratGrunddeckung) newObject;
}

[8] Die generierten Javadocs zeigen wir von nun an nicht mehr im Tutorial, da die Methoden im Text erläutert werden.

Der Vertrag wird in der Deckung als der Vertrag gesetzt, zu dem die Deckung gehört (zweites if-Statement der Methode).