vendredi 3 avril 2009

Fluent Interface - Xml Builder

Juste queqlue petits mots pour relater une méthode de programmation à mon sens bien utile.
Cette méthode s'appelle Fluent Interface programming.

En gros, le but est de créer une interface de manipulation IConfigurationFluent dont chacune des fonctions retourne une référence vers cette interface.
Il est ainsi possible d'appliquer, à la chaine, des appels aux méthodes. Cela s'avère bien pratique lorsque l'on manipule des informations.

Voici, ci-dessous, deux exemples en C# (un avec une interface Fluent, l'autre sans). Il n'est pas bien difficile d'évaluer l'élégance de programmation de l'interface Fluent.
public static void Main(string[] args)
        {
            //Exemple standard - Sans interface Fluent 
            IConfiguration config = new Configuration();
            config.SetColor("blue");
            config.SetHeight(1);
            config.SetLength(2);
            config.SetDepth(3);

            //Exemple Utilisant l'interface Fluent
            IConfigurationFluent config = 
                  new ConfigurationFluent().SetColor("blue")
                                           .SetHeight(1)
                                           .SetLength(2)
                                           .SetDepth(3);
        }

En autre, l'auteur de ce billet (qui n'est autre que Primoz Gabrijelcic, le  créateur de GpSynch) a utilisé cette méthode de programmation pour créer un objet Xml Builder.
Ainsi construite sur l'interface IGpFluentXmlBuilder, la classe de Primoz permet de créer un document XML aussi facilement que ça:
xmlWsdl := CreateFluentXml
    .AddProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"')
    .AddChild('definitions')
      ['xmlns',           'http://schemas.xmlsoap.org/wsdl/']
      ['xmlns:xs',        'http://www.w3.org/2001/XMLSchema']
      ['xmlns:soap',      'http://schemas.xmlsoap.org/wsdl/soap/']
      ['xmlns:soapenc',   'http://schemas.xmlsoap.org/soap/encoding/']
      ['xmlns:mime',      'http://schemas.xmlsoap.org/wsdl/mime/']
      ['name',            serviceName]
      ['xmlns:ns1',       'urn:' + intfName]
      ['xmlns:fs',        'http://fab-online.com/soap/']
      ['targetNamespace', 'http://fab-online.com/soap/']
      .AddChild('message')['name', 'fs:' + baseName + 'Request'].Anchor(nodeRequest)
      .AddSibling('message')['name', 'fs:' + baseName + 'Response'].Anchor(nodeResponse)
      .AddSibling('portType')['name', baseName]
      .Here
        .AddChild('operation')['name', baseName]
          .AddChild('input')['message', 'fs:' + baseName + 'Request']
          .AddSibling('output')['message', 'fs:' + baseName + 'Response']
      .Back
      .AddSibling('binding')
      .Here
        ['name', bindingName]
        ['type', 'fs:' + intfName]
        .AddChild('soap:binding')
          ['style', 'rpc']
          ['transport', 'http://schemas.xmlsoap.og/soap/http']
          .AddChild('operation')['name', baseName]
            .AddChild('soap:operation')
              ['soapAction', 'urn:' + baseName]
              ['style', 'rpc']
            .AddSibling('input')
              .AddChild('soap:body')
                ['use', 'encoded']
                ['encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/']
                ['namespace', 'urn:' + intfName + '-' + baseName]
              .Parent
            .AddSibling('output')
            .AddChild('soap:body')
              ['use', 'encoded']
              ['encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/']
              ['namespace', 'urn:' + intfName + '-' + baseName]
      .Back
      .AddSibling('service')['name', serviceName]
        .AddChild('port')
          ['name', portName]
          ['binding', 'fs:' + bindingName]
          .AddChild('soap:address')['location', serviceLocation];

Pour avoir moi-même écrit des routines de manipulation XML, je trouve cette approche très séduisante.
J'encourage vivement la lecture de son article.

Aucun commentaire: