Generierte Builder für Vertrags- und Produktteilklassen
Policy- und ProductBuilder ermöglichen dem User schnell und unkompliziert neue Instanzen von Policen und Produkten zu erstellen. Damit kann ausgehend von einem Faktor-IPS Modell die gesamte Vertrags- und Produktstruktur instanziiert werden. Dies vereinfacht Setup-Code sowohl in Produktivsystemen als auch beim Testen erheblich.
Beispiel
// Police erstellen
IHausratVertrag hausrateVertrag = IHausratVertrag.NEW.builder()
.zahlweise(12).plz("123456") //
.add() // Referenzierte Objekte hinzufügen
.deckung(IHausratGrunddeckung.NEW.builder() // (1)
.wohnfläche(120) //
.add()
.zuNa()) // ZuNa an neue Deckung hinzufügen, die Klammer von (1) wird erst hier geschlossen!
.add() // weiteres Objekt hinzufügen
.deckung(IFahrraddiebstahlDeckung.NEW.builder());
Getting started
Installation
Dieses Projekt ist direkt in faktorips.base
integriert. Die Generierung von Builder-Klassen kann in Eclipse an und ausgeschalten werden:
-
Rechtsklick auf das Projekt in Package Explorer
-
Wähle Eigenschaften (Properties)
-
Wähle links Faktor-IPS Code Generator
-
Den Wert von Generate Builder Classes ändern (Optionen: Policies only, Products only, All und None)
Features
Policy- und ProductBuilder generierieren zu jeder Vertrags-/Produktklasse ModellObjekt eine Builderklasse ModellObjektBuilder. Diese stellt alle Methoden bereit, die notwendig sind, um eine Instanz der entsprechenden Modellklasse, sowie deren Assoziationen/Kompositionen, zu erstellen und zu bearbeiten. Die zugehörige Builderklasse für eine Modellklasse Vertrag wird VertragBuilder genannt.
PolicyBuilder werden verwendet, um Vertragsinstanzen zu erstellen, oder zu bearbeiten. Dies ist sowohl für Produktivsysteme als auch für Tests sinnvoll. ProductBuilder werden verwendet um Produktmodelle zu erstellen. Hierfür wird ein InMemoryRuntimeRepository verwendet, das alle Produktdaten im Speicher hält. Dadurch, dass Modelle programmatisch erstellt werden können, ergeben sich mehrere Vorteile:
-
Es wird kein spezielles Test-Produkt mit entsprechendem Projekt-Setup und Sourcecode Verwaltung benötigt
-
Modelle können flexibel pro Anwendungsfall/Testfall erstellt werden
ProductBuilder sind daher speziell für Tests sinnvoll.
Der Großteil der Features existiert sowohl für Policy- als auch für ProductBuilder. Im Folgenden werden primär die Features von PolicyBuilder vorgestellt. Spezifische Funktionen für ProductBuilder werden im jeweiligen Abschnitt angemerkt.
Neue Instanzen erstellen
IHausratVertrag hausratVertrag = IHausratVertrag.NEW.builder().getResult();
-
Eine Factory für die entsprechende Builder-Klasse wird als Konstante (
NEW
) im Published Interface hinterlegt -
Die Method
builder()
erstellt einen Builder mit einer neuen Vertragsinstanz -
getResult()
gibt die gebaute Instanz zurück
Falls keine Published Interfaces generiert werden, können neue Instanzen folgendermaßen erstellt werden:
HausratVertrag hausratVertrag = HausratVertrag.builder().getResult();
Ebenso können auch konfigurierte Vertragsinstanzen mit Hilfe eines existierenden Produktbausteins erzeugt werden.
IFahrradDiebstahlDeckung fahrradDiebstahlDeckung = IFahrradDiebstahlDeckung.NEW.builder(runtimeRepository, "beispiel.Fahrrad 2015-06").getResult();
-
Mit
builder(runtimeRepository)
kann zusätzlich einIRuntimeRepository
gespeichert werden, in dem Produktbausteine liegen -
Für konfigurierte Klassen werden
builder(productCmpt)
undbuilder(runtimeRepository, productCmptId)
angeboten
Für Produktklassen muss ein Repository angegeben werden. Es gibt 3 Möglichkeiten, eine neue Produktinstanz zu erstellen:
IHausratDeckungstyp hausratDeckungstyp = IHausratDeckungstyp.NEW
.builder(repo, "beispiel.", "Basic", "2016-05").getResult();
IHausratDeckungstyp hausratDeckungstyp = IHausratDeckungstyp.NEW
.builder(repo, "beispiel.", "Basic", "2016-05",
new DateTime(2015, 7, 1)).getResult();
IHausratDeckungstyp hausratDeckungstyp = IHausratDeckungstyp.NEW
.builder(repo, "beispiel.Fahrrad 2015-07").getResult();
-
builder(inMemoryRuntimeRepository, id, kindId, versionId)
undbuilder(inMemoryRuntimeRepository, id, kindId, versionId)
erstellen einen Builder mit einer neuen Produktinstanz mit den gegebenen IDs. -
builder(inMemoryRuntimeRepository, productCmptId)
erstellt einen Builder mit einer neuen Instanz von einem Produkt, wovon ein Produktbaustein bereits im Repository existiert.
Bestehende Instanzen bearbeiten
hausratVertrag.modify()…
hausratVertrag.modify(repository)…
-
Existierende Instanzen können mit der Objektmethode
modify()
bearbeitet werden. Diese Methode gibt einen Builder von dem Vertrag zurück. -
Auch hier kann ein
IRuntimeRepository
explizit angegeben werden.
Attribute setzen und bearbeiten
hausratVertrag.modify().plz("12345") //
.zahlweise(12); //
//oder
IHausratVertrag hausratVertrag = IHausratVertrag.NEW.builder()//
.plz("12345") //
.zahlweise(12) //
.getResult();
-
Attribute können mit einer gleichnamigen Methode gesetzt werden
-
Setter für Attribute geben wieder den Builder zurück, damit weitere Attribute gesetzt, oder die Struktur weitergebaut werden kann
Assoziationen/Kompositionen hinzufügen
PolicyBuilder bietet hierfür die Methode add()
an. Dadurch wird ein AssociationsBuilder zurück gegeben an dem für alle Beziehungen Methoden mit den entsprechenden Rollennamen vorhanden sind.
// Hinzufügen einer Deckung,nur möglich, wenn das Ziel nicht abstrakt ist
hausratVertrag.modify().add().deckung();
// Eine spezielle Deckungsklasse verwenden
hausratVertrag.modify().add().deckung(IHausratGrunddeckung.NEW.builder().wohnflaeche(120));
hausratVertrag.modify().add().deckung(IFahrradDiebstahlDeckung.NEW.builder(runtimeRepository, "beispiel.Fahrrad 2015-06"));
// Die neue Instanz gleich weiter bearbeiten (der Deckung eine ZuNa hinzufügen)
hausratVertrag.modify().add().deckung(IHausratGrunddeckung.NEW.builder().add().zuNa());
// ProductCmptID benutzen
hausratVertrag.modify(runtimeRepository).add().deckung("beispiel.Fahrrad 2015-06");
-
Mit
add()
wird damit begonnen, eine verknüpfte Instanz zu erstellen -
Setter für Assoziationen haben den gleichen Namen wie der Rollenname (im Singular)
-
Die Rollen-Methode (z.B.
deckung()
) erstellt eine neue Instanz der Zielklasse. Es wird der ursprüngliche Builder (also z.B. HausratVertragBuilder) zurück gegeben. -
Mit den Rollen-Methode (
rolle(targetBuilder)
) kann man eine Instanz einer Subklasse verknüpfen -
Wenn das neue, verknüpfte Objekt weiter eingestellt werden soll, kann der targetBuilder weiter verwendet werden (die Beziehungs-Methode bekommt also ggf. ein mehrzeiliges Argument)
-
Falls der Builder ein RuntimeRepository kennt, kann die Zielinstanz auch mit Hilfe des ProductCmptIDs erstellt werden (
rolle(productCmptID)
)
Anpassungsstufen
ProductBuilder generiert bei der Erstellung einer neuen Produktinstanz automatisch eine neue Anpassungsstufe. Diese Anpassungsstufe wird implizit für Attribute und Assoziationen verwendet, die sich über die Zeit ändern. Es kann aber auch jederzeit neue Anpassungstufen erstellt, und in Bearbeitung gesetzt werden:
hausratDeckungstyp.modify().anpStufe(2015, 3, 2).add().zuNaTyp("beispiel.", "ZuNa", "2015-03");
Nun wird die Assoziation zu der Anpassungstufe mit der Gültigkeitsdatum 2015-03-02 hinzugefügt.
ProductBuilder bietet auch Getter-Methoden an, die Anpassungsstufen zurückgeben:
hausratDeckungstyp.modify().anpStufe(2013,4,2).getCurrentGeneration();
hausratDeckungstyp.modify().getLatestAnpStufe();
-
getCurrentGeneration()
gibt die Anpassungsstufe zurück, die sich gerade in Bearbeitung befindet. -
getLatestAnpStufe()
gibt die zeitlich aktuellste Anpassungsstufe zurück.
Die Methodennamen sind abhängig von der Faktor IPS-Einstellung zur Namensgebung von Anpassungsstufen/Generationen:
-
getLatestAnpStufe()
heißt analoggetLatestGen()
-
anpStufe()
heißt analoggen()
Cheat Sheet
Ziel | Methode | Anwendungsbeispiel | Ausgangsklasse | Rückgabewert |
---|---|---|---|---|
Neue Instanz erstellen |
|
|
Published Interface |
Builder |
|
|
Published Interface |
Builder |
|
|
|
Published Interface |
Builder |
|
|
|
Published Interface |
Builder |
|
|
Published Interface |
Builder |
||
Neue Instanz erstellen (ohne Published Interfaces) |
|
|
Vertragsklasse |
Builder |
|
|
Vertragsklasse |
Builder |
|
|
|
Vertragsklasse |
Builder |
|
|
|
Vertragsklasse |
Builder |
|
|
Vertragsklasse |
Builder |
||
Gebaute Instanz speichern |
|
|
Builder |
Vertrag |
|
|
Builder |
Anpassungsstufe |
|
|
|
Builder |
Anpassungsstufe |
|
Instanz bearbeiten |
|
|
Vertrag |
Builder |
|
|
Vertrag |
Builder |
|
Attribute bearbeiten |
|
|
Builder |
Builder |
Eine Assoziation hinzufügen |
|
|
Builder |
AssociationsBuilder |
|
|
AssociationsBuilder |
Builder (Quelle, z.B. VertragBuilder) |
|
|
|
AssociationsBuilder |
Builder (Quelle, z.B. VertragBuilder) |
|
Ziel der Assoziation weiter bauen |
|
|
AssociationsBuilder |
Builder (Quelle, z.B. VertragBuilder) |
Ziel mit Produktbaustein konfigurieren |
|
|
AssociationsBuilder |
Builder (Quelle, z.B. VertragBuilder) |
Zu bearbeitende Anpassungsstufe ändern |
|
|
Builder |
Builder |
Vorbelegung für produktkonfiguriertes Vertragsattribut ändern |
|
|
Produkt |
Builder |
Wertebereich für produktkonfiguriertes Vertragsattribut ändern |
|
|
Produkt |
Builder |
Tipp
-
In Eclipse kann ein Zeilenumbruch erzwungen werden, indem man „//“ (leerer Kommentar) ans Ende der Zeile schreibt, um so mehrere Änderungen durch einen Builder zu strukturieren. Alternativ können in den Formatter-Einstellungen für Zeilenumbrüche festgelegt werden, dass manuell umgebrochene Zeilen niemals zusammengefügt werden („Never join already wrapped lines“).