Teil 2: Verwendung von Tabellen und Formeln
Verwendung von Formeln
Unser bisheriges Hausratmodell bietet der Fachabteilung wenig Flexibilität. Ein Produkt kann genau eine Grunddeckung haben, der Beitrag wird über die Tariftabelle festgelegt. Jetzt wollen wir es der Fachabteilung erlauben, flexibel Zusatzdeckungen zu definieren, ohne dass das Modell oder der Programmcode (durch die Anwendungsentwicklung) geändert werden muss. Für die Berechnung des Beitrags werden wir hierzu die Formelsprache von Faktor-IPS verwenden.
Zusatzdeckungen gegen Fahrraddiebstahl und Überspannungsschäden dienen uns als Beispiel:
Fahrraddiebstahl | Überspannung | |
---|---|---|
Versicherungssumme der Zusatzdeckung |
1% der im Vertrag vereinbarten Summe, maximal 3000 Euro. |
5% der im Vertrag vereinbarten Summe. Keine Deckelung. |
Jahresbasisbeitrag |
10% der Versicherungssumme der Fahrraddiebstahldeckung |
10 Euro + 3% der Versicherungssumme der Überspannungsdeckung |
Die Zusatzdeckungen haben eine eigene Versicherungssumme, die von der im Vertrag vereinbarten Versicherungssumme abhängt. Der Jahresbasisbeitrag hängt wiederum von der Versicherungssumme der Deckung ab. Um solche Zusatzdeckungen abbilden zu können, erweitern wir das Modell nun wie im folgenden Klassendiagramm abgebildet:
Ein HausratVertrag kann beliebig viele solcher Zusatzdeckungen enthalten. Die Konfigurationsklasse zur HausratZusatzdeckung nennen wir HausratZusatzdeckungstyp. Diese hat die Eigenschaften „versSummenFaktor“ und „maximaleVersSumme“. Die Versicherungssumme der Zusatzdeckung ergibt sich durch Multiplikation der Versicherungssumme des Vertrags mit dem Faktor und wird durch die maximale Versicherungssumme gedeckelt.
Unsere beiden Beispieldeckungen sind Instanzen der Klasse HausratZusatzdeckungstyp. Dies zeigt das folgende Diagramm.
Bevor wir zur Beitragsberechnung kommen, legen wir zunächst die beiden neuen Klassen HausratZusatzdeckung und HausratZusatzdeckungstyp an. Mit dem Assistenten zum Anlegen einer neuen Vertragsklasse kann man auch direkt die zugehörige Konfigurationsklasse mit erzeugen. Starten Sie nun den Assistenten und geben auf der ersten Seite die Daten wie im folgenden Bild zu sehen ein
Auf der zweiten Seite geben Sie noch den Klassennamen HausratZusatzdeckungstyp ein und klicken Finish. Faktor-IPS legt nun beide Klassen an und richtet auch die Referenzen aufeinander ein.
Nun müssen wir noch die Beziehung zwischen HausratVertrag und HausratZusatzdeckung und entsprechend zwischen HausratProdukt und HausratZusatzdeckungstyp anlegen. Hierzu verwenden Sie den Assistenten zum Anlegen einer neuen Beziehung im Vertragsklasseneditor. Dieser erlaubt es Ihnen gleichzeitig auch die Beziehung auf der Produktseite mit anzulegen.
Nachdem die Beziehungen definiert sind, legen Sie an der Klasse HausratZusatzdeckung das abgeleitete (Getter) Attribut versSumme (Money) an und an der Klasse HausratZusatzdeckungstyp die Attribute versSummenFaktor (Decimal), maximaleVersSumme (Money), und bezeichnung (String). Nachdem dies geschehen ist, können wir auch direkt die Ermittlung der Versicherungssumme implementieren. Hierzu implementieren wir in der Klasse HausratZusatzdeckung
die Methode getVersSumme()
wie folgt:
/**
* Gibt den Wert des Attributs versSumme zurueck.
*
* @restrainedmodifiable
*/
@IpsAttribute(name = "versSumme", kind = AttributeKind.DERIVED_ON_THE_FLY, valueSetKind = ValueSetKind.AllValues)
public Money getVersSumme() {
// begin-user-code
HausratZusatzdeckungstyp typ = getHausratZusatzdeckungstyp();
if (typ == null) {
return Money.NULL;
}
Decimal faktor = typ.getVersSummenFaktor();
Money vsVertrag = getHausratVertrag().getVersSumme();
Money vs = vsVertrag.multiply(faktor, RoundingMode.HALF_UP);
if (vs.isNull()) {
return vs;
}
Money maxVs = typ.getMaximaleVersSumme();
if (vs.greaterThan(maxVs)) {
return maxVs;
}
return vs;
// end-user-code
}
Nun legen wir die beiden Deckungstypen für die Versicherung von Fahrraddiebstählen bzw. Überspannungsschäden an. Wechseln Sie hierzu wieder in die Produktdefinitionsperspektive und markieren im Produktdefinitionsexplorer im Projekt Hausratprodukte das Paket „deckungen“. Nun legen Sie zwei Produktbausteine mit den Namen HRD-Fahrraddiebstahl 2019-07 und HRD-Überspannung 2019-07 basierend auf der Klasse HausratZusatzdeckungstyp an und geben im Editor deren Eigenschaften ein.
HRD-Fahrraddiebstahl 2019-07 | HRD-Überspannung 2019-07 | |
---|---|---|
Bezeichnung |
Fahrraddiebstahl |
Überspannungsschutz |
VersSummenFaktor |
0.01 |
0.05 |
Maximale VersSumme |
3000 EUR |
<null> |
Nun müssen die neuen Deckungen noch den Produkten zugeordnet werden. Dies erfolgt analog zur Zuordnung der Grunddeckungen. Bei Verträgen, die auf Basis des Produktes HR-Optimal (das im Ordner „produkte“ liegt) abgeschlossen werden, sollen die Deckungen immer enthalten sein (Art der Beziehung ist Obligatorisch), beim Produkt HR-Kompakt sollen Sie optional dazu gewählt werden können (Art der Beziehung Optional). Sie können dies über die Art der Beziehung im Bausteineditor einstellen.
Beitragsberechnung für die Zusatzdeckungen
Die Berechnung des Jahresbasisbeitrags soll durch die Fachabteilung durch eine Formel festgelegt werden. Der Beitrag für eine Zusatzdeckung wird i.d.R. von der Versicherungssumme und evtl.weiteren risikorelevanten Merkmalen abhängen [5]. In der Formel muss man also auf diese Eigenschaften zugreifen können. Hierzu sind prinzipiell zwei Wege denkbar:
-
die Formelsprache erlaubt eine beliebige Navigation durch den Objektgraphen
-
die in der Formel verwendbaren Parameter werden explizit festgelegt.
In Faktor-IPS wird die zweite Alternative verwendet. Hierfür gibt es zwei Gründe:
-
Die Syntax für die Navigation durch den Objektgraphen kann schnell komplex werden. Wie sieht zum Beispiel eine für die Fachabteilung leicht verständliche Syntax zur Ermittlung der Deckung mit der höchsten Versicherungssumme aus?
-
Aktualität von abgeleiteten Attributen
5 In der Hausratversicherung neben der Tarifzone zum Beispiel die Art des Hauses (Ein-/Mehrfamilienhaus) oder die Bauweise.
Der zweite Aspekt lässt sich am besten an einem Beispiel erläutern. Für die Berechnung des Beitrags einer Zusatzdeckung wird die Versicherungssumme benötigt. Diese ist selbst ein abgeleitetes Attribut. Wenn es sich dabei um ein gecachtes Attribut handelt, muss vor dem Aufruf der Formel zur Beitragsberechnung sichergestellt werden, dass die Versicherungssumme berechnet wurde. Erlaubt man nun eine beliebige Navigation durch den Objektgraphen muss man für alle erreichbaren Objekte sicherstellen, dass die abgeleiteten Attribute berechnete Werte enthalten. Da dies leicht zu Fehlern führt und auch bzgl. der Performance ungünstig ist, werden in Faktor-IPS die in einer Formel verwendbaren Parameter explizit festgelegt.
Als Formelparameter können sowohl einfache Parameter wie z. B. die Versicherungssumme definiert werden als auch ganze Objekte wie z.B. der Vertrag. Letzteres hat den Vorteil, dass die Parameterliste nicht erweitert werden muss, wenn die Fachabteilung auf bisher nicht genutzte Merkmale zugreift. Für die Berechnung des Jahresbasisbeitrags der Hausratzusatzdeckung verwenden wir die Zusatzdeckung selbst und den Hausratvertrag (zu dem die Deckung gehört) als Parameter.
Um in Zusatzdeckungen die Formel für die Beitragsberechnung hinterlegen zu können, muss zunächst in der Klasse HausratZusatzdeckungstyp die Formelsignatur mit den Parametern definiert werden. Öffnen Sie nun den Editor für die Klasse HausratZusatzdeckungstyp. Klicken Sie auf der zweiten Seite im Abschnitt Methods and Formula Signatures (Methoden und Formelsignaturen) auf den New Button, um eine Formelsignatur anzulegen und tragen Sie die Daten wie im folgenden Screenshot zu sehen ein.
Schließen Sie den Dialog und speichern. In der Klasse HausratZusatzdeckungstyp
befindet sich nun die Methode berechneJahresbasisbeitrag
(…) zur Berechnung des Basisbeitrags.
Öffnen wir nun die Fahrraddiebstahldeckung, um die Formel für die Beitragsberechnung festzulegen. Beim Öffnen erscheint zunächst ein Dialog, in dem angezeigt wird, dass es im Modell eine neue Formel gibt, die es bisher nicht in der Produktdefinition gab.
Bestätigen Sie mit Beheben, dass diese hinzugefügt werden soll. In dem Abschnitt Tables & Formulas (Tabellen und Berechungsvorschriften) wird nun die noch leere Formel für den Beitragssatz angezeigt. Klicken Sie auf den Button neben dem Formelfeld, um die Formel zu editieren. Es öffnet sich der folgende Dialog, in dem Sie die Formel bearbeiten können und in dem auch die verfügbaren Parameter angezeigt werden:
Der Beitrag für die Fahrraddiebstahlversicherung soll 10% der Versicherungssumme der Zusatzdeckung betragen. Drücken Sie Strg-Space in der mittleren Eingabebox und Sie sehen die Parameter und Funktionen, die Sie zur Verfügung haben. Wählen Sie den Parameter „deckung“ aus und geben danach noch einen Punkt ein. Sie bekommen die Eigenschaften der Deckung zur Auswahl angeboten. Wählen Sie die „versSumme“. Nun multiplizieren Sie die Versicherungssumme noch mit 0.1.
Schließen Sie den Dialog und speichern den Baustein. Analog definieren Sie nun, dass der Beitrag für die Überspannungsdeckung 10Euro + 3% der Versicherungssumme beträgt (Formel: 10EUR + deckung.versSumme * 0.03).
Faktor-IPS hat nun die Subklassen für die beiden Produktbausteine mit der in Java Sourcecode übersetzten Formel generiert. Sie finden beide Klassen im Java-Sourcefolder „src/main/resources“ im Package org.faktorips.tutorial.produktdaten.internal.deckungen
[6]. Der folgende Sourcecode enthält die generierte Methode berechneJahresbasisbeitrag
(…) für die Fahrraddiebstahldeckung.
6 Da der Name von Produktbausteinen auch Blanks und Bindestriche enthalten kann, diese aber nicht in Java- Klassennamen erlaubt sind, wurden diese durch Unterstriche ersetzt. Konfigurieren können Sie die Ersetzung in der „.ipsproject“ Datei im Abschnitt ProductCmptNamingStrategy.
public Money berechneJahresbasisbeitrag(final HausratVertrag vertrag, final HausratZusatzdeckung deckung) throws FormulaExecutionException {
try {
return deckung.getVersSumme().multiply(Decimal.valueOf("0.1"), RoundingMode.HALF_UP);
} catch (Exception e) {
StringBuffer parameterValues = new StringBuffer();
parameterValues.append("vertrag=");
parameterValues.append(vertrag == null ? "null" : vertrag.toString());
parameterValues.append(", ");
parameterValues.append("deckung=");
parameterValues.append(deckung == null ? "null" : deckung.toString());
throw new FormulaExecutionException(toString(), "deckung.versSumme*0.1", parameterValues.toString(), e);
}
}
Tritt beim Ausführen der in Java übersetzten Formel ein Fehler auf, wird eine RuntimeException geworfen, die den Formeltext sowie die String-Repräsentation der übergebenen Parameter enthält.
Nun müssen wir noch dafür sorgen, dass die Formel im Rahmen der Beitragsberechnung auch aufgerufen wird, indem wir in der Klasse HausratZusatzdeckung
die Methode berechneJahresbasisbeitrag
() implementieren. Dies geht nun einfach, indem wir an die Berechnungsmethode im Zusatzdeckungstyp delegieren und als Parameter die Zusatzdeckung (this) und den Vetrag, zu dem die Deckung gehört, übergeben. Legen Sie in der Modellklasse HausratZusatzdeckung die Methode berechneJahresbasisbeitrag mit dem Rückgabewert Money, der Sichtbarkeit published und ohne Parameter an und speichern.
Öffnen Sie nun die Java-Klasse HausratZusatzdeckung
und implementieren Sie die Methode wie folgt.
public Money berechneJahresbasisbeitrag() {
return getHausratZusatzdeckungstyp().berechneJahresbasisbeitrag(getHausratVertrag(), this);
}
Damit der Beitrag der Zusatzdeckungen auch dem Gesamtbeitrag des Hausratvertrags zugeschlagen wird, erweitern wir noch die Beitragsberechnung in der Klasse Hausratvertrag:
private void berechneJahresbasisbeitrag() {
jahresbasisbeitrag = Money.euro(0, 0);
HausratGrunddeckung hausratGrunddeckung = getHausratGrunddeckung();
hausratGrunddeckung.berechneJahresbasisbeitrag();
jahresbasisbeitrag = jahresbasisbeitrag.add(hausratGrunddeckung.getJahresbasisbeitrag());
/*
* Über Zusatzdeckungen iterieren und jeweils den Beitrag dem Gesamtbeitrag
* hinzuaddieren:
*/
List<? extends HausratZusatzdeckung> zusatzdeckungen = getHausratZusatzdeckungen();
for (int i = 0; i < zusatzdeckungen.size(); i++) {
HausratZusatzdeckung hausratZusatzdeckung = zusatzdeckungen.get(i);
jahresbasisbeitrag = jahresbasisbeitrag.add(hausratZusatzdeckung.berechneJahresbasisbeitrag());
}
}
Zum Abschluss des Kapitels testen wir die neue Funktionalität wieder durch die Erweiterung des JUnit-Tests wie folgt.
@Test
public void testBerechneJahresbasisbeitragFahrraddiebstahl() {
// Erzeugen eines hausratvertrags mit der Factorymethode des Produktes
HausratVertrag vertrag = kompaktProdukt.createHausratVertrag();
// Vertragsattribute setzen
vertrag.setVersSumme(Money.euro(60_000));
/*
* Zusatzdeckungstyp Fahrraddiebstahl holen. Der Einfachheit halber nehmen wir
* hier an, der erste Zusatzdeckungstyp ist Fahrraddiebstahl
*/
HausratZusatzdeckungstyp deckungstyp = kompaktProdukt.getHausratZusatzdeckungstyp(0);
// Zusatzdeckung erzeugen
HausratZusatzdeckung deckung = vertrag.newHausratZusatzdeckung(deckungstyp);
// Jahresbasisbeitrag berechnen und testen
deckung.berechneJahresbasisbeitrag();
/*
* Versicherungssumme der Deckung = 1% von 60.000, max 5.000 => 600 Beitrag =
* 10% von 600 = 60
*/
assertEquals(Money.euro(60, 0), deckung.berechneJahresbasisbeitrag());
}
In diesem zweiten Teil des Tutorials haben wir gesehen, wie Tabellen in Faktor-IPS genutzt werden, haben die Beitragsberechnung implementiert und getestet und am Beispiel der Zusatzdeckungen gesehen, wie man ein Modell so gestaltet, dass es flexibel erweitert werden kann.
Ein weiteres Tutorial zeigt, wie man mit den hier erstellten Modellklassen in einer operativen Anwendung arbeitet (Tutorial Hausrat Angebotssystem).
Im Tutorial zur Modellpartitionierung wird gezeigt, wie man komplexe Modelle in sinnvolle Teile zerlegt und damit umgeht. Insbesondere die Trennung in einzelne Sparten und die Trennung von spartenspezifischen und spartenübergreifenden Aspekten wird dort beispielhaft gezeigt.
Wie man beim Testen von Faktor-IPS unterstützt wird, zeigt das Tutorial Softwaretests mit Faktor-IPS.