Ich habe eine Frage zur “Aufwertung” von Rückgabetypen geerbter Methoden.
Ich habe eine Klasse XmlBuilder
, mit der man XML-Dokumente erzeugen kann, bei denen sich der Builder die richtige Einrückebene merkt. Ein Beispiel:
String xml = new XmlBuilder()
.appendXmlHeader()
.appendOpeningTag("zeug")
.appendInTag("foo", "Wichtiger foo-Inhalt")
.appendInTag("bar", "Wichtiger bar-Inhalt")
.appendOpeningTag("baz")
.appendInTag("baz-foo", "baz-foo-blubb",
new NamedXmlParameter("foo", "foo-Inhalt"))
.appendInTag("baz-bar", "baz-bar-blubb",
new NamedXmlParameter("foo", "foo-Inhalt"),
new NamedXmlParameter("bar", "bar-Inhalt"),
new NamedXmlParameter("baz", "baz-Inhalt"))
.appendClosingTag("baz")
.appendOpeningTagWithParameters("open-mit-parameter",
new NamedXmlParameter("option", "bla bla"))
.appendClosingTag("open-mit-parameter")
.appendClosingTag("zeug")
.toString();
Erzeugt
<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
<zeug>
<foo>Wichtiger foo-Inhalt</foo>
<bar>Wichtiger bar-Inhalt</bar>
<baz>
<baz-foo foo="foo-Inhalt">baz-foo-blubb</baz-foo>
<baz-bar foo="foo-Inhalt" bar="bar-Inhalt" baz="baz-Inhalt">baz-bar-blubb</baz-bar>
</baz>
<open-mit-parameter option="bla bla">
</open-mit-parameter>
</zeug>
Natürlich lässt sich das auch wie folgt erreichen:
XmlBuilder builder = new XmlBuilder();
builder.appendXmlHeader();
builder.appendOpeningTag("zeug");
builder.appendInTag("foo", "Wichtiger foo-Inhalt");
builder.appendInTag("bar", "Wichtiger bar-Inhalt");
builder.appendOpeningTag("baz");
builder.appendInTag("baz-foo", "baz-foo-blubb",
new NamedXmlParameter("foo", "foo-Inhalt"));
builder.appendInTag("baz-bar", "baz-bar-blubb",
new NamedXmlParameter("foo", "foo-Inhalt"),
new NamedXmlParameter("bar", "bar-Inhalt"),
new NamedXmlParameter("baz", "baz-Inhalt"));
builder.appendClosingTag("baz");
builder.appendOpeningTagWithParameters("open-mit-parameter",
new NamedXmlParameter("option", "bla bla"));
builder.appendClosingTag("open-mit-parameter");
builder.appendClosingTag("zeug");
String xml = builder.toString();
Man sieht schon, die erste Version ist deutlich netter zu schreiben und übersichtlicher, weil nicht die ganzen Wiederholungen des Builders und die Semikoli den Blick auf das Wesentliche erschweren. Dafür gibt jede Methode im XmlBuilder
this
zurück.
So weit, so gut. Nun gibt es auch einen HtmlBuilder
, der auf analoge Weise funktioniert. Und da HTML nicht viel mehr als ein Spezialfall von Xml ist, erbt der HtmlBuilder
vom XmlBuilder
und verwendet viele von den Methoden des XmlBuilder
s wieder, was gut ist, da es dem DRY-Principle (don’t repeat yourself) entspricht. Leider enthält der HtmlBuilder
nun viele Methoden der Form
@Override
public HtmlBuilder appendOpeningTag(String tag) {
super.appendOpeningTag(tag);
return this;
}
damit der Aufruf analog zum XmlBuilder
wie folgt erfolgen kann:
String table = new HtmlBuilder()
.appendOpeningTable()
.appendOpeningThead()
.appendOpeningTr()
.appendTh("Zeug")
.appendLeftAlignedTh("Anderes Zeug")
.appendRightAlignedTh("Noch mehr Zeug")
.appendLeftAlignedThWithClass("Zeug mit Klasse", "CSS-KLASSE")
.appendClosingTr()
.appendClosingThead()
.appendOpeningTbody()
.appendOpeningTr()
.appendTd("foo")
.appendTd("bar")
.appendRightAlignedTd("baz")
.appendTd("blubb", "CSS-KLASSE")
.appendClosingTr()
.appendOpeningTr()
.appendTd("foo 2")
.appendTd("bar 2")
.appendTd("baz 2")
.appendTd("blubb 2")
.appendClosingTr()
.appendClosingTbody()
.appendOpeningTfoot()
.appendP("Blubber")
.appendClosingTfoot()
.appendClosingTable()
.toString();
Dabei kommt dann sowas raus:
<table>
<thead>
<tr>
<th>Zeug</th>
<th align="left">Anderes Zeug</th>
<th align="right">Noch mehr Zeug</th>
<th class="CSS-KLASSE" align="left">Zeug mit Klasse</th>
</tr>
</thead>
<tbody>
<tr>
<td>foo</td>
<td>bar</td>
<td align="right">baz</td>
<td class="CSS-KLASSE">blubb</td>
</tr>
<tr>
<td>foo 2</td>
<td>bar 2</td>
<td>baz 2</td>
<td>blubb 2</td>
</tr>
</tbody>
<tfoot>
<p>Blubber</p>
</tfoot>
</table>
Ohne die oben gezeigten Weiterleitungsmethoden wäre in der Kette bereits nach appendOpeningTable()
Schluss, welches intern die Methode appendOpeningTag()
aus dem XmlBuilder
aufrufen würde. Und der Rückgabewert, nämlich der XmlBuilder
, kennt dann natürlich nicht die Methode appendOpeningThead()
, wodurch zurecht ein Kompilerfehler entsteht.
Das ist ziemlich schade, da so recht viele dieser Weiterleitungsmethoden im HtmlBuilder
stehen, aber noch fataler ist es in einem fachlich erweiterten HtmlBuilder
, der speziell auf das Projekt zugeschnittene Javascripte und dergleichen erzeugt und bestimmte Methoden anbietet, die außerhalb davon wenig Sinn machen. Hier habe ich darauf verzichtet, die vielen, vielen Methoden des HtmlBuilder
s weiterzuleiten, wodurch nur die umständlichere Verwendung mit der Wiederholung des builder
s in jeder Zeile verbleibt.
Gibt es da irgendeinen Trick, wie ich nicht ein Objekt der Klasse, sondern ein ggf. abgeleitetes Objekt, nämlich das der Klasse, die eigentlich aktiv ist, zurückgebe? Sozusagen ein “supper.this
” oder sowas. Vielleicht mit extends
oder so? Wenn ich nach dem Themenkomplex google, lande ich bei Seiten, die mir Vererbung erklären, aber nicht auf dieses Problem eingehen.