informaticaPC

Patrones de Diseño Software

Composite

Este útil patrón permite crear y manejar estructuras de objetos en forma de árbol, en las que un objeto puede contener a otro(s).

Patrón Composite en UML

En este punto cabe aclarar que las estructuras de este tipo se componen de nodos (un objeto que a su vez contiene otros objetos) y Hojas (objetos que no contienen otros), y que ambos comparten una misma Interface que define métodos que deben implementar.

Para ilustrar cómo funciona este patrón de diseño veamos a continuación un programa de ejemplo en el que gestionamos archivos y carpetas.

Main.java (Cliente según el diagrama anterior):

package estructurales.composite.composite01;

public class Main
{
    public static void main(String[] args)
    {
        // Crear la carpeta principal e insertar archivos
        Carpeta c1 = new Carpeta("CARPETA_1");
        c1.insertarNodo( new Archivo("Archivo1.txt") );
        c1.insertarNodo( new Archivo("Archivo2.txt") );
        c1.insertarNodo( new Archivo("Archivo3.txt") );

        // Crear una subcarpeta e insertar archivos
        Carpeta c2 = new Carpeta("CARPETA_2");
        c2.insertarNodo( new Archivo("Archivo4.txt") );
        c2.insertarNodo( new Archivo("Archivo5.txt") );

        // Insertar la subcarpeta dentro de la principal
        c1.insertarNodo( c2 );

        // Insertar otro archivo dentro de la carpeta principal
        c1.insertarNodo( new Archivo("Archivo6.txt") );

        c1.mostrar();

        System.out.println("--------------------");

        // Eliminamos la subcarpeta (junto con su contenido)
        c1.eliminarNodo( c2 );

        c1.mostrar();
    }
}

Nodo.java: (Componente según el diagrama anterior)

package estructurales.composite.composite01;

public abstract class Nodo
{
    public static final int ARCHIVO = 1;
    public static final int CARPETA = 2;

    protected String nombre = "";
    protected int tipoNodo;

    // ----------------------------

    public String getNombre()
    {
        return this.nombre;
    }

    // ----------------------------

    public void setNombre( String nombre )
    {
        this.nombre = nombre;
    }

    // ----------------------------

    public int getTipoNodo()
    {
        return this.tipoNodo;
    }

    // ----------------------------

    public void setTipoNodo( int tipoNodo )
    {
        this.tipoNodo = tipoNodo;
    }

    // ----------------------------

    // Método a implementar por las clases que hereden
    public abstract void mostrar();
}

Archivo.java: (Hoja según el diagrama anterior)

package estructurales.composite.composite01;

public class Archivo extends Nodo
{
    public Archivo( String nombre )
    {
        this.setTipoNodo( Nodo.ARCHIVO );
        this.setNombre( nombre );
    }

    // ----------------------------

    @Override
    public void mostrar()
    {
        System.out.println( "Archivo: [" + this.getNombre() + "]" );
    }
}

Carpeta.java: (Compuesto según el diagrama anterior)

package estructurales.composite.composite01;

import java.util.ArrayList;
import java.util.List;

public class Carpeta extends Nodo
{
    List<Nodo> nodos = new ArrayList<Nodo>();

    // ----------------------------

    public Carpeta( String nombre )
    {
        this.setTipoNodo( Nodo.CARPETA );
        this.setNombre( nombre );
    }

    // ----------------------------

    public void insertarNodo( Nodo nodo )
    {
        nodos.add( nodo );
    }

    // ----------------------------

    public void eliminarNodo( Nodo nodo )
    {
        nodos.remove( nodo );
    }

    // ----------------------------

    public List<Nodo> getNodos()
    {
        return nodos;
    }

    // ----------------------------

    public Nodo getNodo( int posicion )
    {
        return nodos.get( posicion );
    }

    // ----------------------------

    @Override
    public void mostrar()
    {
        System.out.println( "Listando carpeta [" + this.getNombre() + "]" );

        for (Nodo nodo : nodos)
        {
            nodo.mostrar();
        }
    }
}

Al ejecutarlo obtendríamos como resultado:

Ejemplo

EXPLICACIÓN:

  • Los archivos son hojas (no contienen otros elementos) y las carpetas serían nodos (pueden contener archivos así como otras carpetas).
  • Las clases Archivo y Carpeta heredan de la clase abstracta Nodo, que implementa los métodos comunes y define el método mostrar() que deberán implementar.
  • Dicho método en la clase Archivo abre el archivo para mostrar su contenido, y en la clase Carpeta muestra su contenido de forma recursiva (si contiene otras carpetas mostrará también lo que contienen).
  • Al ejecutar el programa se crea una carpeta principal y se le agregarán objetos de tipo Archivo así como otra subcarpeta que creamos posteriormente, y después mostramos su contenido.
  • A continuación eliminamos la subcarpeta y volvemos a mostrar el contenido de la principal

Veamos ahora otro ejemplo en el que se recorre cada elemento del árbol y se muestra el nombre del nodo padre de cada uno de ellos.

package estructurales.composite.composite02;

public class Main
{
    public static void main(String[] args)
    {
        // Crear la carpeta principal e insertar archivos
        Carpeta c1 = new Carpeta( "CARPETA_1", null );
        c1.insertarNodo( new Archivo("Archivo1.txt", c1) );
        c1.insertarNodo( new Archivo("Archivo2.txt", c1) );
        c1.insertarNodo( new Archivo("Archivo3.txt", c1) );

        // Crear una subcarpeta e insertar archivos
        Carpeta c2 = new Carpeta("CARPETA_2", c1);
        c2.insertarNodo( new Archivo("Archivo4.txt", c2) );
        c2.insertarNodo( new Archivo("Archivo5.txt", c2) );

        // Insertar la subcarpeta dentro de la principal
        c1.insertarNodo( c2 );

        // Insertar otro archivo dentro de la carpeta principal
        c1.insertarNodo( new Archivo("Archivo6.txt", c1) );

        c1.recorrerNodos();
    }
}

Nodo.java: (Componente según el diagrama anterior)

package estructurales.composite.composite02;

public abstract class Nodo
{
    public static final int ARCHIVO = 1;
    public static final int CARPETA = 2;

    protected String nombre = "";
    protected int tipoNodo;

    protected Nodo nodoPadre;

    // ----------------------------

    public String getNombre()
    {
        return this.nombre;
    }

    // ----------------------------

    public void setNombre( String nombre )
    {
        this.nombre = nombre;
    }

    // ----------------------------

    public int getTipoNodo()
    {
        return this.tipoNodo;
    }

    // ----------------------------

    public void setTipoNodo( int tipoNodo )
    {
        this.tipoNodo = tipoNodo;
    }

    // ----------------------------

    public Nodo getNodoPadre()
    {
        return this.nodoPadre;
    }

    // ----------------------------

    public void setNodoPadre( Nodo nodoPadre )
    {
        this.nodoPadre = nodoPadre;
    }

    // ----------------------------

    // Método a implementar por las clases que hereden
    public abstract void mostrar();
}

Archivo.java: (Hoja según el diagrama anterior)

package estructurales.composite.composite02;

public class Archivo extends Nodo
{
    public Archivo( String nombre, Nodo nodoPadre )
    {
        this.setTipoNodo( Nodo.ARCHIVO );
        this.setNombre( nombre );

        this.setNodoPadre( nodoPadre );
    }

    // ----------------------------

    @Override
    public void mostrar()
    {
        System.out.println( "Mostrando el contenido del archivo [" + this.getNombre() + "]" );
    }
}

Carpeta.java: (Compuesto según el diagrama anterior)

package estructurales.composite.composite02;

import java.util.ArrayList;
import java.util.List;

public class Carpeta extends Nodo
{
    List<Nodo> nodos = new ArrayList<Nodo>();

    // ----------------------------

    public Carpeta( String nombre, Nodo nodoPadre )
    {
        this.setTipoNodo( Nodo.CARPETA );
        this.setNombre( nombre );

        this.setNodoPadre( nodoPadre );
    }

    // ----------------------------

    public void insertarNodo( Nodo nodo )
    {
        nodos.add( nodo );
    }

    // ----------------------------

    public void eliminarNodo( Nodo nodo )
    {
        nodos.remove( nodo );
    }

    // ----------------------------

    public List<Nodo> getNodos()
    {
        return nodos;
    }

    // ----------------------------

    public Nodo getNodo( int posicion )
    {
        return nodos.get( posicion );
    }

    // ----------------------------

    // Recorre cada nodo del árbol mostrando su nombre y el de su nodo padre
    public void mostrarPadres()
    {
        for (Nodo nodo : nodos)
        {
            System.out.println( "El nodo padre de [" + nodo.getNombre() + "] es [" + nodo.getNodoPadre().getNombre() + "]" );

            // Si se trata de otra carpeta, mostrar también su contenido
            if( nodo.getTipoNodo() == Nodo.CARPETA )
            {
                ((Carpeta) nodo).mostrarPadres();
            }
        }
    }

    // ----------------------------

    @Override
    public void mostrar()
    {
        System.out.println( "Listando carpeta [" + this.getNombre() + "]" );

        for (Nodo nodo : nodos)
        {
            nodo.mostrar();
        }
    }
}

Al ejecutarlo obtendríamos como resultado:

Ejemplo

EXPLICACIÓN:

  • En la clase abstracta Nodo se ha agregado la propiedad nodoPadre y los métodos necesarios para que los objetos de las clases que hereden de ésta guarden una referencia a su nodo padre en el momento de su instanciación.
  • El método mostrarPadres() de la clase Carpeta es el encargado de recorrer el árbol para ir mostrando el nombre de cada elemento así como el de su nodo padre.
Información

En en el diagrama UML del libro de GoF todos los métodos de la clase Compuesto se encuentran también en la clase abstracta de la que hereda, pero hemos dejado tan sólo operacion() dado que Hoja en principio no contiene otros hijos.

Primera página Anterior Siguiente Última página
Usamos cookies para ofrecerte una experiencia mejorada, el continuar navegando supone que aceptas su uso