<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://mletkin.net/index.php?action=history&amp;feed=atom&amp;title=Buildervererbung_%28Draft%29</id>
	<title>Buildervererbung (Draft) - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://mletkin.net/index.php?action=history&amp;feed=atom&amp;title=Buildervererbung_%28Draft%29"/>
	<link rel="alternate" type="text/html" href="https://mletkin.net/index.php?title=Buildervererbung_(Draft)&amp;action=history"/>
	<updated>2026-05-06T13:41:51Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in MimiPedia</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>https://mletkin.net/index.php?title=Buildervererbung_(Draft)&amp;diff=95&amp;oldid=prev</id>
		<title>Ullrich: Die Seite wurde neu angelegt: „==Das Problem der Builder-Vererbung== Wenn die Objekte einer Klasse {{java|A}} durch den Builder {{java|BuilderA}} erzeigt werden, ist es naheliegend, die Obje…“</title>
		<link rel="alternate" type="text/html" href="https://mletkin.net/index.php?title=Buildervererbung_(Draft)&amp;diff=95&amp;oldid=prev"/>
		<updated>2022-01-27T18:34:42Z</updated>

		<summary type="html">&lt;p&gt;Die Seite wurde neu angelegt: „==Das Problem der Builder-Vererbung== Wenn die Objekte einer Klasse {{java|A}} durch den Builder {{java|BuilderA}} erzeigt werden, ist es naheliegend, die Obje…“&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;==Das Problem der Builder-Vererbung==&lt;br /&gt;
Wenn die Objekte einer Klasse {{java|A}} durch den Builder {{java|BuilderA}} erzeigt werden,&lt;br /&gt;
ist es naheliegend, die Objekte der abgeleiteten Klasse {{java|B}} durch den von {{java|BuilderA}}&lt;br /&gt;
abgeleiteten Builder {{java|BuilderB}} zu erzeugen -- warum?&lt;br /&gt;
Betrachten wir die Klassen&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class Foo {&lt;br /&gt;
    int x;&lt;br /&gt;
}&lt;br /&gt;
public class Bar extends Foo {&lt;br /&gt;
    int y;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Jeder Builder einer der beiden Klassen wird einen Modifier für das Feld {{java|x}} benötigen.&lt;br /&gt;
Es liegt also nahe, einen Builder {{java|FooBuilder}} zu definieren von dem der Builder {{java|BarBuilder}}&lt;br /&gt;
ableitet. Der erste Entwurf dafür sähe so aus:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class FooBuilder {&lt;br /&gt;
    Foo obj;&lt;br /&gt;
    FooBuilder withX(int x) {&lt;br /&gt;
        obj.x = x;&lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
    FooBuilder get() {&lt;br /&gt;
        return obj;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Den Getter in {{java|BarBuilder}} zu überschreiben stellt kein Problem dar, da {{java|get}} stets die&lt;br /&gt;
letzte Methode im Builder-Aufruf ist. Was aber geschieht, wenn wir die Methode {{java|withX}} auf den&lt;br /&gt;
gedachten {{java|BarBuilder}} aufrufen wir erhalten eine {{java|FooBuilder}}-Instanz als Ergebnis und &lt;br /&gt;
können keine {{java|BarBuilder}}-Methoden mehr aufrufen. Das führt und zum ersten Entwurf:&lt;br /&gt;
===Erster Entwurf===&lt;br /&gt;
Wir müssen also dafür sorgen, daß die Modifier-Methoden&lt;br /&gt;
des abgeleiteten Builder die richtigen Objekte liefern. Dafür parametrisieren wir den {{java|FooBuilder}} und&lt;br /&gt;
geben ihm als Typ-Parameter den entsprechenden -- abgeleiteten -- Builder mit:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class FooBuilder&amp;lt;B extends FooBuilder&amp;lt;?&amp;gt;&amp;gt; {&lt;br /&gt;
    Foo obj;&lt;br /&gt;
    B withX(int x) {&lt;br /&gt;
        obj.x = x;&lt;br /&gt;
        return (B)this;&lt;br /&gt;
    }&lt;br /&gt;
    Foo get() {&lt;br /&gt;
        return obj;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Damit {{java|withX}} den richtigen Builder-Typ liefert, muß der return-Wert gecastet werden.&lt;br /&gt;
Unsere Lösung hat einen kleinen Haken. Der Compiler wird sich über den Cast {{java|(B)this}} beschweren,&lt;br /&gt;
weil hier in ungeprüfter Cast stattfindet. Wir werden uns das später anschauen.&lt;br /&gt;
&lt;br /&gt;
Der erste Entwurf des {{java|BarBuilder}} sieht -- noch ziemlich ungelenk -- sieht nun wie folgt aus.&lt;br /&gt;
Das Generat wird hier der Bequemlichkeit halber in einem initialisierungs-Block erzeugt. Die Generat-Erzeugung&lt;br /&gt;
wird noch zu diskutieren sein.&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class BarBuilder extends FooBuilder&amp;lt;BarBuilder&amp;gt; {&lt;br /&gt;
    {&lt;br /&gt;
        obj = new Bar();&lt;br /&gt;
    }&lt;br /&gt;
    BarBuilder withY(int y) {&lt;br /&gt;
        ((Bar)obj).y = y;&lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
    @Override&lt;br /&gt;
    Bar get() {&lt;br /&gt;
        return (Bar)obj;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Die Häßlichkeit entspringt der Tatsache, daß wir das Genrat nicht parametrisiert haben.&lt;br /&gt;
Der Builder wird zwar funktionieren, aber &amp;#039;&amp;#039;schön&amp;#039;&amp;#039; ist was anderes. Da das {{java|foo}}-Feld im&lt;br /&gt;
{{java|FooBuilder}} als {{java|Foo}}-Typ deklaiert wurde, können wir zwar {{java|Bar}}-Objekte hineinfüllen,&lt;br /&gt;
müssen sie aber bei jeder Gelegenheit casten -- igitt!&lt;br /&gt;
&lt;br /&gt;
===Zweiter Entwurf===&lt;br /&gt;
Die Lösung führt über die Parametrisierung des Generat-Typs:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class FooBuilder&amp;lt;T extends Foo, B extends FooBuilder&amp;lt;T, B&amp;gt;&amp;gt; {&lt;br /&gt;
    T obj;&lt;br /&gt;
    B withX(int x) {&lt;br /&gt;
        obj.x = x;&lt;br /&gt;
        return (B) this;&lt;br /&gt;
    }&lt;br /&gt;
    T get() {&lt;br /&gt;
        return obj;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Im ersten Entwurf mußte der {{java|BarBuilder}} die {{java|get}}-Methode überschreiben um sicherzustellen,&lt;br /&gt;
daß der Builder auch ein {{java|Bar}}-Objekt liefert. Zur Laufzeit tat er das natürlich immer, tatsächlich&lt;br /&gt;
geht es darum, auch zur Compile-Zeit sicherzustellen daß der Compiler mit dem richtigen Typ arbeiten kann.&lt;br /&gt;
Der {{java|BarBuilder}} sieht nun so aus:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class BarBuilder extends FooBuilder&amp;lt;Bar, BarBuilder&amp;gt; {&lt;br /&gt;
    {&lt;br /&gt;
        obj = new Bar();&lt;br /&gt;
    }&lt;br /&gt;
    BarBuilder withY(int y) {&lt;br /&gt;
        obj.y = y;&lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Das Feld {{java|obj}} hat nun für den Compiler in jedem Fall en richtigen Typ und auch der Builder-Typ&lt;br /&gt;
wird in jedem Falle korrekt bestimmt.&lt;br /&gt;
&lt;br /&gt;
===Der Fall des unerwünschten B-Cast===&lt;br /&gt;
Warum beschwert sich der Compiler nun über den Cast in der Modifier-Methode:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public class FooBuilder&amp;lt;T extends Foo, B extends FooBuilder&amp;lt;T, B&amp;gt;&amp;gt; {&lt;br /&gt;
    ...&lt;br /&gt;
    B withX(int x) {&lt;br /&gt;
        obj.x = x;&lt;br /&gt;
        return (B) this;&lt;br /&gt;
    }&lt;br /&gt;
    ...&lt;br /&gt;
}}&lt;br /&gt;
Die Klasse {{java|FooBuilder}} vertraut hier darauf, daß die abgeleitete Klasse tatsächlich&lt;br /&gt;
vom Typ {{java|B}} ist. Kann das garantiert werden? Nein. Wenn wir einen dritten Builder bauen:&lt;br /&gt;
{{java|code=public class BadBuilder extends FooBuilder&amp;lt;Bar, BarBuilder&amp;gt;}}&lt;br /&gt;
Dann wird die Methode {{java|withX}} versuchen {{java|this}} -- das vom Typ {{java|BadBuilder}} ist --&lt;br /&gt;
nach {{java|BarBuilder}} zu casten und kläglich scheitern...&lt;br /&gt;
&lt;br /&gt;
Der Designer muß selbst entscheiden, ob das tatsächlich ein Problem ist. Wie sinnvoll ist es für&lt;br /&gt;
einen Builder eine Ableitung zu machen wie oben gezeigt? Es bliebe zu zeigen, ob man damit tatsächlich&lt;br /&gt;
eine sinnvolle implementierung konstruieren kann.&lt;br /&gt;
&lt;br /&gt;
Wenn es nur um die drohende Class Cast Exception geht, kann man dem entgegenhalten: Man geht mit dem&lt;br /&gt;
Cast das Risiko ein und kann es zur Compile-Zeit nicht verhindern -- na und? Spätestens bei der ersten&lt;br /&gt;
Ausführung -- die natürlich im Unit-Tests stattfindet -- hat man die Exception und kann sie fixen.&lt;br /&gt;
In  aller Regel wird es sich ja wohl um einen Tippfehler handeln.&lt;br /&gt;
&lt;br /&gt;
Wenn das nun tatsächlich absolut kein Zustand ist, kann man natürlich was dagegen tun. Man fügt dem&lt;br /&gt;
{{java|FooBuilder}} einfach eine abstrakte Methode hinzu:&lt;br /&gt;
{{java|code=&lt;br /&gt;
public abstract class FooBuilder&amp;lt;T extends Foo, B extends FooBuilder&amp;lt;T, B&amp;gt;&amp;gt; {&lt;br /&gt;
    T obj;&lt;br /&gt;
    B withX(int x) {&lt;br /&gt;
        obj.x = x;&lt;br /&gt;
        return getThis();&lt;br /&gt;
    }&lt;br /&gt;
    T get() {&lt;br /&gt;
        return obj;&lt;br /&gt;
    }&lt;br /&gt;
    abstract B getThis();&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Jetzt kann nichts mehr schief gehen. Jede Klasse, die den Builder ableitet muß {{java|getThis}} so überschreiben,&lt;br /&gt;
daß die Methode Objekte des richtigen Typs liefern muß.&lt;br /&gt;
===Generat-Erzeugung===&lt;br /&gt;
Was in den vorangegangenen Beispielen noch ausgesprochen krude aussieht, ist die Erzeugung des Generats,&lt;br /&gt;
warum haben wir es in einem Initialisierungs-Block erzeugt und warum nicht in der {{java|FooBuilder}}-Klasse.&lt;br /&gt;
&lt;br /&gt;
Wie in vorangegangenen Abschnitten beschrieben, kann man das Generat-Objekt zu jeder beliebigen Zeit erzeugen.&lt;br /&gt;
In der Regel erzeiugt man es direkt bei Erzeugung des Builders oder bei Aufruf der Generat-Methode am Ende.&lt;br /&gt;
Aber im Prinzip kann man das Generat zu jedem Zeitpunkt erzeugen während der Lebenszeit des Builders.&lt;br /&gt;
&lt;br /&gt;
Man kann die Erzeugung in der Superklasse durchführen, also im {{java|FooBuilder}}. Da es aber nicht möglich ist,&lt;br /&gt;
aus dem generischen Typ ein reales Objekt zu erzeugen, muß man auch hier wieder auf eine abstrakte Methode ausweichen:&lt;br /&gt;
{{java|code=abstract T create();}}&lt;br /&gt;
Damit kann man im {{java|FooBuilder}} das Objekt zu jeder beliebigen Zeit erzeugen. Daß man sich damit für alle&lt;br /&gt;
abgeleiteten Builder festlegt, ist dabei in der Regel kein Problem. Das verzögerte Erzeugen erfordert die Pufferung&lt;br /&gt;
der Eingaben, das läßt sich in der Regel nicht in de abgeleiteten Buildern nachträglich einführen.&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungs-Block in den Beispielen dient und nur als Platzhalter. Wir können jedes beliebige Konzept&lt;br /&gt;
einsetzen das wir für sinnvoll halten, Generat-Erzeugung&lt;br /&gt;
*Im Initialisierungsblock&lt;br /&gt;
*Im Konstruktor&lt;br /&gt;
*In statischen Factory-Methoden&lt;br /&gt;
*In der Generat-Funktion&lt;br /&gt;
*An jeder anderen Stelle&lt;br /&gt;
Es empfiehlt sich in jedem Falle &amp;#039;&amp;#039;ein&amp;#039;&amp;#039; Konzept durch die gesamte Builder-Hierarchie zu verwenden.&lt;br /&gt;
In der Regel wird man nur die &amp;quot;Blatt-Builer&amp;quot; verwenden, also Builder von denen keine anderen Builder&lt;br /&gt;
abgeleitet sind. Es empfiehlt sich, die anderen Klassen -- btw. ihre Konstruktoren -- gegen ungewollte&lt;br /&gt;
oder unerwünschte Benutzung abzusichern, indem man sie protected deklariert.&lt;br /&gt;
[[Kategorie:Java]]&lt;br /&gt;
[[Kategorie:Builder]]&lt;/div&gt;</summary>
		<author><name>Ullrich</name></author>
	</entry>
</feed>