Patrones de diseño de creación: Object pool

Intención del patrón
Utilizar un pool (o agrupamiento) de objetos puede ofrecer un significativo aumento de rendimiento. Es más eficaz en situaciones donde el costo de la inicialización de instanciar una clase es alta, el ritmo de instanciación de la misma también es alta, pero el número de instancias en uso en cualquier momento es baja.


Ejemplo de problema
Los agrupamientos de objetos (también conocidos como agrupamientos de recursos) se utilizan para gestionar el caché de objetos. Un cliente con acceso a un agrupamiento de objetos puede evitar crear nuevos objetos simplemente pidiendo al agrupamiento uno que haya sido instanciado antes con anterioridad. En general, la agrupación será cada vez mayor, es decir, el agrupamiento va a crear por sí mismo nuevas instancias si está vacío, o bien, podría tratarse de un pool que restringe la cantidad de objetos creados.

Discusión
El Object Pool permite a otros "Obtener" objetos de su agrupamiento. Cuando dichos objetos ya no son necesitados por los procesos que los solicitaron con anterioridad, vuelven al agrupamiento para ser reutilizados.
Sin embargo, no es deseable tener que esperar que un objeto en particular sea liberado, por lo que el Object Pool también crea nuevas instancias a medida que son requeridos, pero debe implementar un mecanismo para "limpiar" periódicamente los objetos que no son utilizados.

Estructura
La idea general para el patrón Connection Pool es que si las instancias de una clase pueden ser reutilizadas, se evite crear nuevas instancias de la clase reutilizándolas.

  • <strong>Reusable</strong> - Instancias de las clases con este rol, colaboran con otros objetos durante un tiempo limitado, entonces ya no son necesarios para la colaboración.
  • <strong>Client</strong> - Instancias de las clases con este rol, son objetos reutilizables.
  • <strong>ReusablePool</strong> - Instancias de las clases con este rol, administran los objetos Reusable para uso de los objetos Cliente.


Usualmente, es conveniente mantener todos los objetos Reusables, que actualmente no están en uso, en la misma agrupación de objetos para que de esta manera sean administrados por una política coherente. Para lograr esto, la clase es diseñada para ser un Singleton. Su constructor debe ser privado, lo que obliga a otras clases a utilizar su método para obtener una instancia de la clase ReusablePool.
Un objeto Client llama al método acquareReusable del objeto ReusablePool cuando necesita un objeto Reusable. Un objeto ReusablePool mantiene una colección de objetos Reusable. Este usa la colección para contener un pool de objetos Reusable que no están actualmente en uso.
Si existe algún objeto Reusable en la colección cuando el método acquareReusable es llamado, éste quita uno de ellos de la colección y lo devuelve. Si la colección está vacía, entonces dicho método crea un nuevo objeto Reusable si puede. Si no puede crearlo, entonces espera a que un objeto Reusable vuelva a la colección.
En muchas aplicaciones del patrón Object Pool, existen razones para limitar el número total de objetos Reusable que pueden existir. En tales casos, el objeto ReusablePool que crea objetos Reusable es responsable de no crear más objetos Reusable que el número máximo especificado. Si ReusablePool es responsable de limitar el número de objetos que creará, entonces la clase ReusablePool debe tener un método que permita especificar el número máximo de objetos que pueden ser creados. Dicho método es indicado en la imagen anterior con el nombre setMaxPoolSize.


Ejemplo
Para aquellos que no sepan, para jugar bowling se debe cambiar los zapatos.
El mostrador para zapatos es un excelente ejemplo de Object Pool. Cuando se desea jugar, se obtiene un par (acquareReusable) de éste. Después del juego se retornan los zapatos al mostrador (releaseReusable).
Ckeck list
  1. Crear la clase ObjectPool con una colección de objetos privada.
  2. Crear los métodos Obtener (acquare) y Librerar (release) en la clase ObjectPool.
  3. Asegurarse que la clase ObjectPool es Singleton.

Reglas de oro
  • El patrón Factory Method puede ser usado para encapsular la lógica de creación de objetos. Sin embargo, no los administra después de su creación, el patrón Object Pool realiza un seguimiento de los objeto que crea.
  • Este patrón suele implementarse como Singleton.

Ejemplo de código en Java
// ObjectPool Class

public abstract class ObjectPool {
  private long expirationTime;

  private Hashtable locked, unlocked;

  public ObjectPool() {
    expirationTime = 30000; // 30 seconds
    locked = new Hashtable();
    unlocked = new Hashtable();
  }

  protected abstract T create();

  public abstract boolean validate(T o);

  public abstract void expire(T o);

  public synchronized T checkOut() {
    long now = System.currentTimeMillis();
    T t;
    if (unlocked.size() > 0) {
      Enumeration e = unlocked.keys();
      while (e.hasMoreElements()) {
        t = e.nextElement();
        if ((now - unlocked.get(t)) > expirationTime) {
          // object has expired
          unlocked.remove(t);
          expire(t);
          t = null;
        } else {
          if (validate(t)) {
            unlocked.remove(t);
            locked.put(t, now);
            return (t);
          } else {
            // object failed validation
            unlocked.remove(t);
            expire(t);
            t = null;
          }
        }
      }
    }
    // no objects available, create a new one
    t = create();
    locked.put(t, now);
    return (t);
  }

  public synchronized void checkIn(T t) {
    locked.remove(t);
    unlocked.put(t, System.currentTimeMillis());
  }
}

//The three remaining methods are abstract 
//and therefore must be implemented by the subclass

public class JDBCConnectionPool extends ObjectPool {

  private String dsn, usr, pwd;

  public JDBCConnectionPool(String driver, String dsn, String usr, String pwd) {
    super();
    try {
      Class.forName(driver).newInstance();
    } catch (Exception e) {
      e.printStackTrace();
    }
    this.dsn = dsn;
    this.usr = usr;
    this.pwd = pwd;
  }

  @Override
  protected Connection create() {
    try {
      return (DriverManager.getConnection(dsn, usr, pwd));
    } catch (SQLException e) {
      e.printStackTrace();
      return (null);
    }
  }

  @Override
  public void expire(Connection o) {
    try {
      ((Connection) o).close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

  @Override
  public boolean validate(Connection o) {
    try {
      return (!((Connection) o).isClosed());
    } catch (SQLException e) {
      e.printStackTrace();
      return (false);
    }
  }
}

JDBCConnectionPool le permitirá a la aplicación obtener y devolver las conexiones de bases de datos.

public class Main {
  public static void main(String args[]) {
    // Do something...
    ...

    // Create the ConnectionPool:
    JDBCConnectionPool pool = new JDBCConnectionPool(
      "org.hsqldb.jdbcDriver", "jdbc:hsqldb://localhost/mydb",
      "sa", "secret");

    // Get a connection:
    Connection con = pool.checkOut();

    // Use the connection
    ...

    // Return the connection:
    pool.checkIn(con);
 
  }
}

3 comentarios:

ciza morales dijo...

Excelente esta expliación del patrón, que buen aporte

Juan Barrionuevo dijo...

Ciza, muchas gracias por tu comentario.
Es muy grato saber que te ha gustado la explicación de este patrón.

Roque Perez dijo...

Hello, in the code when "t" is the same object, puting it as key return ERROR,,,how to solve this?

// no objects available, create a new one
t = create();
locked.put(t, now);
return (t);

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

 
Copyright 2009 Programación SOLIDa
BloggerTheme by BloggerThemes | Design by 9thsphere