Objektorientierte Wiederverwendung und Entwurfsmuster emulieren

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie würden gern von der XSLT-Methode der Wiederverwendung mittels Ausschneiden und Einsetzen weggehen und Bibliotheken mit wiederverwendbarem XSLT-Code erzeugen.

Lösung

Sicherlich ist XSLT 2.0 keine objektorientierte Programmiersprache. Dennoch geht es bei der Objektorientierung genauso darum, wie man generischen, wiederverwendbaren Code erzeugt, wie es um die Erzeugung von Klassen, Objekten, Vererbung, Kapselung und dergleichen geht.

Es gibt zwei Eigenarten von XSLT 2.0, die einen objektorientierten Stil unterstützen. Die erste ist die Anweisung xsl:next-match und die zweite sind Tunnelparameter. Die Anweisung xsl:next-match ist eine Verallgemeinerung der XSLT 1.0-Anweisung xsl:apply-imports. Erinnern Sie sich, dass Sie in XSLT 1.0 xsl:apply-imports verwenden, um Templates mit niedrigerer Import-Präzedenz aufzurufen. Die Anweisung xsl:next-match verallgemeinert dieses Verhalten, indem sie es Ihnen erlaubt, passende Templates niedrigerer Priorität innerhalb desselben Stylesheets und importierender Stylesheets aufzurufen. Dies ähnelt dem Aufruf einer Basisklassenmethode in der objektorientierten Programmierung:

<xsl:template match="author | title | subtitle | deck" priority="1">
  <a name="{generate-id( )}">
    <span class="{name( )}">
      <xsl:apply-templates/>
    </span>
  </a>
</xsl:template>
<xsl:template match="author" priority="2">
  <div>
    <span class="by">By </span>
    <xsl:next-match/>
  </div>
</xsl:template>
<xsl:template match="title" priority="2">
  <h1 class="title">
    <xsl:next-match/>
  </h1>
</xsl:template>
<xsl:template match="deck" priority="2">
  <h2 class="deck">
    <xsl:next-match/>
  </h2>
</xsl:template>
<xsl:template match="subtitle" priority="2">
  <h2 class="subtitle">
    <xsl:next-match/>
  </h2>
</xsl:template>

Eine weitere Verbesserung in 2.0 ist die Fähigkeit, Parameter sowohl an xsl:next-match als auch an xsl:apply-imports zu übergeben:

<xsl:next-match>
  <xsl:with-param name="indent" select="2"/>
</xsl:next-match>

Eine weitere Fähigkeit von XSLT-2.0-Templates sind Tunnelparameter, eine Form der dynamischen Zuordnung, die in der funktionalen Programmierung populär ist. Tunnelparameter erlauben es Aufrufen an xsl:apply-templates, Parameter zu übergeben, die den unmittelbar passenden Templates nicht unbedingt bekannt sind. Allerdings werden diese Templates transparent von Aufruf zu Aufruf übertragen, bis sie bei einem Template ankommen, das solche Parameter enthält. Beachten Sie, dass sowohl an der Stelle des Aufrufs als auch an der Stelle, an der der Parameter akzeptiert wird, das Attribut tunnel="yes" verwendet werden muss:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!--Standardverarbeitungsregeln für doc -->
  <xsl:import href="doc.xslt"/>
  <!-- Ein eigener Parameter, der vom Autor von doc.xslt nicht vorgesehen war -->
  <xsl:param name="customParam"/>
  <xsl:template match="/">
    <!--Aufruf von Templates von doc.xslt, die customParam nicht kennen -->
    <xsl:apply-templates>
      <xsl:with-param name="customParam" select="$customParam" tunnel="yes"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="heading1">
    <!-- Mit heading1-Elementen soll auf der Grundlage von customParam etwas Besonderes gemacht werden -->
    <xsl:param name="customParam" tunnel="yes"/>
    <!-- ... -->
  </xsl:template>
</xsl:stylesheet>

Dies ist eine außerordentlich wichtige Verbesserung für XSLT, da sie es Ihnen erlaubt, anwendungsspezifische Templates von generischen Templates zu entkoppeln, ohne globale Parameter oder Variablen einzuführen.

Diskussion

Bei der objektorientierten Entwicklung ist das Konzept der Entwurfsmuster sehr verbreitet. Dabei handelt es sich um bewährte Techniken, die bei einer Vielzahl von Problemen angewendet werden. Die Muster fördern die Kommunikation zwischen Entwicklern, indem sie quasi-standardisierte Namen für die Techniken, die durch das Muster beschrieben werden, sowie den geeigneten Kontext bereitstellen.

Die Fähigkeiten, die in diesem Rezept und im Rezept Modularisierung und Modi beschrieben wurden, können verwendet werden, um einige der Standardmuster aus einer XSLT-Perspektive zu implementieren. Als Nächstes passen wir einige Standardmuster auf den Bereich von XSLT an.

Template-Methode

Definieren Sie das Gerüst eines Stylesheets in einer Operation, wobei einige Schritte an Templates delegiert werden, die durch importierende Stylesheets implementiert werden. Die Template-Methode erlaubt es anderen, bestimmte Schritte einer Transformation neu zu definieren, ohne die Struktur der Transformation zu ändern.

Stellen Sie sich ein Stylesheet vor, das den Standard definiert, nach dem Ihr Unternehmen die Darstellung eines XML-Dokuments für das Web aufbereitet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" >
  <xsl:output method="xhtml" indent="yes"/>
  <xsl:param name="titlePrefix" select=" '' " as="xs:string"/>
  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="concat($titlePrefix, /*/title)"/></title>
      </head>
      <body>
        <xsl:call-template name="standard-processing-sequence"/>
      </body>
    </html>
  </xsl:template>
  <xsl:template name="standard-processing-sequence">
    <xsl:apply-templates mode="front-matter">
      <xsl:with-param name="mode" select=" 'front-matter' " tunnel="yes" as="xs:string"/>
    </xsl:apply-templates>
    <xsl:apply-templates mode="toc">
      <xsl:with-param name="mode" select=" 'toc' " tunnel="yes" as="xs:string"/>
    </xsl:apply-templates>
    <xsl:apply-templates mode="body">
      <xsl:with-param name="mode" select=" 'body' " tunnel="yes" as="xs:string"/>
    </xsl:apply-templates>
    <xsl:apply-templates mode="appendicies">
      <xsl:with-param name="mode" select=" 'appendicies' " tunnel="yes" as="xs:string"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="/*" mode="#all">
    <xsl:param name="mode" tunnel="yes" as="xs:string"/>
    <div class="{$mode}">
      <xsl:apply-templates mode="#current"/>
    </div>
  </xsl:template>
  <!-- Hier kommen vorgegebene Templates für verschiedene Modi hin - diese können in importierenden Stylesheets überschrieben werden -->
</xsl:stylesheet>

Hier verwenden Sie Modi, um die einzelnen Hauptverarbeitungsstufen zu identifizieren. Allerdings übergeben Sie auch den Namen des aktuellen Modus in einem Tunnelparameter. Dies hat zwei Vorteile. Erstens ist es für das Debugging von Templates nützlich, die in mehreren Modi passen. Zweitens erlaubt es ähnlichen Mehr-Modi-Templates, deren Verhalten nur leicht variiert, diese Variation als eine Funktion des mode-Parameters zu implementieren, ohne unbedingt die speziellen Modi zu kennen. Falls beispielsweise das Template die Ausgabeelemente mit CSS-Stilen versieht, kann diesen Stilen der Name des Modus als Präfix vorangestellt oder eine andere allgemeine Zuordnung (z.B. Suche in einer Tabelle) verwendet werden, um vom Modus zum CSS-Stil zu gelangen.

Zuständigkeitskette

Vermeiden Sie es, den Initiator einer Transformation mit den Templates zu verbinden, die die Transformation ausführen, indem mehr als ein Template die Möglichkeit erhält, die Anforderung zu bearbeiten. Verlassen Sie sich auf Template-Filterung anstatt auf bedingte Logik, um das passende Template zu ermitteln.

Prioritäten sind der Schlüssel, um dieses Muster portabel zu machen, da einige XSLT-Prozessoren möglicherweise Templates mit unklarer Template-Präzedenz nicht verarbeiten. Dieses Beispiel stammt aus einem Projekt mit dynamischen Websites, an dem ich gearbeitet habe. Dort kam auch Cocoon zum Einsatz. Bei diesem Projekt wurde HTML mit Templates verwendet, bei dem die class-Attribute vorgaben, wie dynamischer Inhalt aus einer XML-Datenbank für das mit Templates versehene HTML aufbereitet wird. Ich lasse die Einzelheiten der einzelnen Templates weg, weil sie weniger wichtig sind als die Gesamtstruktur. In diesem Beispiel passt nur ein Template in einen gegebenen xhtm:td-Knoten, im allgemeinen Fall jedoch können Sie xsl:next-match verwenden, um die Wirkungen mehrerer passender Templates zu kombinieren:

<xsl:template match="xhtm:td[matches(@class, '^keep-(\w+)')]" mode="template" priority="2.1"> </xsl:template>
<xsl:template match="xhtm:td[matches(@class, '^(flow|list)-(\w+)')]" mode="template" priority="2.2"> </xsl:template>
<xsl:template match="xhtm:td[matches(@class, '^repeat-(\w+)')]" mode="template" priority="2.3"> </xsl:template>
<xsl:template match="xhtm:td[matches(@class, '^download-(\w+)')]" mode="template" priority="2.4"> </xsl:template>
<xsl:template match="xhtm:td[matches(@class, '^keep-(\w+)')]" mode="template" priority="2.1"> <xsl:template>

Dekorateur

Erweitern Sie das Verhalten niedriger priorisierter Templates, indem Sie Knoten erfassen, die eine höhere Priorität von Templates mit einer höheren Import-Präzedenz verwenden. Rufen Sie das Kernverhalten mittels xsl:next-match auf.

Dies ist die klassische Anwendung des nächsten Treffers, die wir in der Lösung besprochen haben, weshalb wir hier auf ein Beispiel verzichten.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de