jueves, 6 de noviembre de 2008

Codificación: datos de la aplicación como recursos XML embebidos (parte III).

Parte I: Resumen.

Parte II: La definción del archivo XML.

Parte III: Encapsulamiento.

Parte IV: Que funcione.

III. Encapsulamiento.

Con lo que tenemos hasta aquí ya podríamos armar nuestro menú de ejemplo copiando y pegando el código de prueba que tenemos y modificándolo para recorrer los datos de acuerdo a las necedidades del caso.

Pero hay un tema extremadamente importante que aclarar antes de seguir: cuidado con XML. Podemos definirlo y utilizarlo muy fácilmente, y así de fácil se nos puede ir de las manos. Ya he comentado algunos ejemplos en este blog:

  • XML * Inconciencia.
  • XML (donde comenzaba con la muy acertada frase: "XML is like Violence, if it doesn't solve the problem, use some more").
  • Pobre XML.

XML es una muy buena herramienta para guardar los datos, pero nunca, nunca jamás (créanme) accedan directamente a ellos desde toda la aplicación.

En este sentido son asimilables a una base de datos: no accedemos a ellos creando una conexión y los objetos necesarios para ejecutar un comando SQL en cada módulo, clase o procedimiento en el que los necesitamos. Siempre creamos una capa de datos que puede ser más o menos compleja y con más o menos funcionalidades de acuerdo al caso, pero nunca, nunca accedemos a ellos directamente.

Las razones principales son:

  • Claridad del código.
  • Si cada vez que deseamos obtener información de un menú hacemos

    
    //......
    
                string resourceName = "EjemploRecursosXMLEmbebido.Menu.xml";
                
                XmlDocument menuesXML = new XmlDocument();
                using (Stream s = this.GetType().Assembly.GetManifestResourceStream(resourceName))
                {
                    menuesXML.Load(s);
                }
    
    //.... (etcétera)...
            
    

    Estaremos ensuciando el procedimiento con todo este montón de cuestiones acerca de los recursos, los streams y demás que dificultan la lectura.

  • Mantenimiento.
  • Imaginemos que utilizamos el código anterior en muchas clases de nuestro proyecto. El simple hecho de cambiar el nombre del recurso implicaría buscar la cadena "EjemploRecursosXMLEmbebido.Menu.xml" por todos lados y reemplazarla.

    Es fácil cometer errores al utilizar XPath. Tal vez no todos los programadores del equipo están familiarizados con él. En cada acceso podría cometerse un error diferente.

  • Mejoras, nuevas funcionalidades, reutilización.
  • Veremos que hay mucho para mejorar en la utilización de recursos XML. Las posibilidades son muchas y usualmente iremos aprendiendo sobre la marcha, a medida que surjan las necesidades. Por ello es que necesitamos tener encapsulado el acceso, para poder modificarlo sin que el resto del sistema se vea afectado.

Espero haberlos convencido. Si es así, estarán dispuestos a crear un par de clases que encapsulen el recurso y lo hagan invisible al resto de la aplicación.

Examinemos la estructura de cada nodo que representa un ítem del menú:

<menu id="compras">
  <titulo>Compras</titulo>
  <submenues>
    <!-- lista de elementos menu con la misma estructura-->
  </submenues>
</menu>

Podemos crear una clase (MenuItem) que encapsule ese nodo y exponga los datos que contiene como propiedades:

using System;
using System.Collections.Generic;
using System.Xml;

namespace EjemploRecursosXMLEmbebido
{
    public class MenuItem
    {
        XmlNode _nodo;

        public MenuItem(XmlNode nodo)
        {
            _nodo = nodo;
        }

        public string Id
        {
            get { return _nodo.SelectSingleNode("@id").InnerText; }
        }

        public string Titulo
        {
            get { return _nodo.SelectSingleNode("titulo").InnerText; }
        }

        public IEnumerable<MenuItem> Submenues()
        {
            XmlNodeList submenues = _nodo.SelectNodes("submenues/menu");

            foreach (XmlNode menuNodo in submenues)
                yield return new MenuItem(menuNodo);

        }

    }
}

Y otra clase que nos dé acceso al recurso (Menues):

using System;
using System.Xml;
using System.IO;
using System.Collections.Generic;

namespace EjemploRecursosXMLEmbebido
{
    public static class Menues
    {
        private static XmlDocument _menuDocument;
        private const string _resourceName = "EjemploRecursosXMLEmbebido.Menu.xml";
        
        static Menues()
        {
            _menuDocument = new XmlDocument();
            using (Stream s = typeof(Menues).Assembly.GetManifestResourceStream(_resourceName))
                _menuDocument.Load(s);
        }

        public static MenuItem Raiz
        {
            get { return new MenuItem(_menuDocument.SelectSingleNode("menu")); }
        }

    }
}

Ya estamos listos para otra prueba. Colocamos un nuevo botón sobre el formulario, cuyo código será:

        private void button2_Click(object sender, EventArgs e)
        {
            foreach (MenuItem menuItem in Menues.Raiz.Submenues())
            {
                MessageBox.Show(menuItem.Titulo);
            }
        }

Vemos que ahora, para nuestro front-end de pruebas, el XML simplemente no existe, ni siquiera "sabe" que es un recurso XML. Simplemente solicita a la clase Menues el menú raíz y recorre sus submenúes mostrando el título.

Pero seguimos sin tener un menú como la gente. Paciencia...

Parte I: Resumen.

Parte II: La definción del archivo XML.

Parte III: Encapsulamiento.

Parte IV: Que funcione.

No hay comentarios.: