Cosas simples que ayudan mucho - Lazy Load

Bueno pues hace rato que alguien me ha preguntado qué es Lazy Load y cómo la podemos utilizar. La contesto hasta ahora (3 días después de cuanto tenía que entregar su tarea XD).

Lazy Load es un patrón de diseño muy simple que nos ayuda a optimizar la carga de datos cuando éstos son obtenidos de alguna fuente de datos (un archivo, una base de datos, un servicio web, etc.).

Ahora a picar código:

<pre>
public class Note{
    private Date date;
    private String class_name;
    private String content;
    private Student student;

/*Getters y setters a continuacion*/
}

public class Student{
    private String name;
    private String lastname;
    private List<Note> notes;

    public void setName(String _name){ name = _name; }
    public void setLastname(String _lastname){ lastname = _lastname; }
    public void setNotes(List<Note> _notes){ notes = _notes; }

    public String getName(){ return name; }
    public String getLastname(){ return lastname; }
    public List<Note> getNotes(){ return notes; }

    public void addNote(Note note){
        if(notes == null) notes = new ArrayList<Note>();
        notes.add(note);
    }
}
</pre>

¿Qué tiene de malo el código anterior?. Siguiendo la teoría orientada a objetos, es un código perfecto, cada propiedad es privada con 2 métodos de acceso para esas propiedades y un método especial (lo último exclusivamente en la clase Student). Pero, en cuanto a aplicación en el mundo real tiene un problemón, ¿qué pasa si un estudiante (objeto de la clase student) tiene 100 ó 1000 ó ó 1000000 ó ....Bueno creo que ya lo han entendido. Obviamente que nuestro sistema quedaría más lento en hacer consultas de todos los estudiantes...En otras palabras al cargar sería algo cómo:
Cargar 100 estudiantes...para el estudiante 0 cargar las 100 notes que tiene....para el estudiante 1 cargar las 5230 notas que tiene...para el estudiante 2 cargar las 890 notas que tiene...así sucesivamente hasta llegar al estudiante 99. Teniendo una lista muy muy grande y poco eficiente en aspectos cómo memoria, ancho de banda (en caso de ser web) y sobretodo la bronca completa del servidor de bases de datos.

Es para esto que nos sirve la carga perezosa o Lazy Load (siempre se me olvida cómo se traduce al español). Ahora a poner el código utilizando la lazy load:

<pre>
public class Student{
    private String name;
    private String lastname;
    private List<Note> notes = null;

    public void setName(String _name){ name = _name; }
    public void setLastname(String _lastname){ lastname = _lastname; }
    public void setNotes(List<Note> _notes){ notes = _notes; }

    public String getName(){ return name; }
    public String getLastname(){ return lastname; }
    public List<Note> getNotes(){
        notes = Note.find("byStudent", this);
        if(notes == null) notes = new ArrayList<Note>();
        return notes;
    }

    public void addNote(Note note){
        getNotes();
        if(notes == null) notes = new ArrayList<Note>();
        notes.add(note);
    }
}
</pre>

¿Cómo es que cambió?...De entrada la propiedad 'notes' se inicializa nula, lo que quiere decir que al inicializarlo no es necesario, y hasta que hacemos un 'getNotes' nos traemos la lista de notas de dicho estudiante, requiriendolas sólo cuando es necesario, no siempre.

Espero que les haya gustado y lo hayan comprendido. También espero que no sea repost, pero anduve buscando y no encontré otro post. Saludos.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Entiendo que es un ejemplo,

Entiendo que es un ejemplo, pero el antes y el después no difieren tanto como dices. Quizá estas obviando algo?

Por ejemplo:

private List notes;

y

private List notes = null;

Dan identicos resultados. :(

Quizá me estoy perdiendo algo que para tí fue muy obvio ( el ejemplo está pensado en un contexto donde se use un ORM? )

Saludos.

100% de acuerdo @OscarRyz, a

100% de acuerdo @OscarRyz, a lo que iba es para hacer más obvio que debe empezar cómo nulo (mucha gente si no ve el '= null' detrás se pierde). Cabe clarificar que de ponerse minucioso también existen muchos errores herejías.

Y pues no está pensado en ese contexto sino en general. Pero, la forma más sencilla de explicarlo (o cómo yo llegué a entenderlo -dicho sea de paso-) fue así bajando algo de BD por medio de un ORM.

Sin embargo para eso estamos para apoyarnos unos a otros. Si consideras que existe una mejor manera de enriquecer (o incluso de replantear) el tema, bienvenido sea :).

Otro ejemplo de lazy loading

Si, cuando se ha usado un ORM queda más claro, por que ves el efecto de cargar desde el inicio vs. cargar bajo demanda.

Otro ejemplo es el "modismo" Bill Pugh para inicializar un Singleton.

El método tradicional es:

class Elvis { 
    private static Elvis  instance;
    public static Elvis getInstance() { 
        if( instance == null ) { instance = new Elvis(); }
        return instance;
    }
}

Que ya de por sí es un buen ejemplo de Lazy loading, pero tiene el inconveniente de requiere una comparación contra null siempre que se invoca y no es thread safe ( a menos que se le ponga el "synchronized" con lo cual se vuelve un poco más lento en una aplicación con un solo thread).

La versión propuesta por Bill Pugh es la siguiente que aprovecha una propiedad muy específica de Java, la forma en la que las clases son cargadas.

<pre>
class /*<strike>Elvis</strike> */   Mexico  {  // como México no hay dos! ( por fortuna )
    private Mexico() { //  jejej ya privatice México
        System.out.println("Ajua!!!");
    }
    // Regresa la unica instancia
    public static Mexico getInstance() {
        return MexicoHolder.instance; //<-- lazy load
    }
    private static class MexicoHolder {
      private final static Mexico instance = new Mexico();// ja! ironias de la vida, un nuevo méxico.
    }
    // metodo de clase
    public static void metodoX() { System.out.println("Si, x"); }

    // metodo de instancia
    public void metodoY() { System.out.println("Si, y"); }

}
</pre>

El Lazy loading aparece aquí con un artificio de Java. En Java, los atributos de clase ( aquellos marcados con static ) son inicializados cuando la clase es cargada.

En este caso, el truco es crear una clase anidada "MexicoHolder" , que tiene la instancia singleton.

Cuando la clase Mexico es cargada e incluso métodos de clase ( como el metodoX ) son invocado repetidas veces, el singleton aún no ha sido creado, por que la clase "MexicoHolder" aún no ha sido cargada.

Es hasta el momento en que se obtiene el singleton, cuando la clase holder es cargada y el singleton inicializado.

Acá va el ejemplo.

<pre>
class TestIt {
  public static void main( String [] args ) {
    // Aún ejecutando metodos de clase
    // el singleton aún no se ha cargado.
    for( int i = 0 ; i < 3 ; i++ ) {
        Mexico.metodoX();
    }

    // Al pedir el singleton, se crea el objeto
    Mexico.getInstance().metodoY();

  }
}
// salida:

Si, x
Si, x
Si, x
Ajua!!!
Si, y
</pre>

:)

Como wishmaster77 comenta, es super importante conocer cuando puede utilizarse lazy loading. La ventaja más importante es que se evita tener información en memoria, cuando aún no es necesaria. La desventaja ( que no lo es tanto ) es que en la primera invocación la percepción será de que se tarda mucho.

Y yo qué en la segunda

Y yo qué en la segunda entrega de "Cosas simples que ayudan mucho" iba a poner singleton u.u

No pero muy muy buena explicación @OscarRyz, mejor manera de haber aportado al tema no creo que haya habido =).

Oops ni mó!.. jo jo, me

Oops ni mó!.. jo jo, me pasaste la idea por hipnotismo.

Se me ocurre para tu "cosas simples que ayudan mucho", dos temas:

1.- Usar llaves para incluso if/else/fors/whiles de una sola linea.
2.- Variables auto explicatorias en vez de temp1, temp2

Gracias por los comentarios.

Patrones de diseño...

Una pregunta o dos @wishmaster77...¿los patrones de diseño son realmente efectivos a la hora de programar? y ¿son exclusivos de la programación orientada a objetos?

Imagen de neko069

Disculpa que me meta en la

Disculpa que me meta en la conversación, pero quisiera darme la libertad de exponer mi punto de vista, igual me pueden corregir o complementar la respuesta.
Los patrones de diseño, son formas genéricas de dar soluciones a distintos problemas, por ejemplo, las fábricas de objetos (Factory), los singletons , los proxys , la inyección de dependencias, tienen cabida en algún proceso de la resolución de un problema. Con respecto a la segunda pregunta, no es que sean exclusivos de la programación orientada a objetos, es más como que cada lenguaje puede tener sus propios patrones de diseño, puesto que no todos los lenguajes están construidos bajo las mismas directrices.

Gracias por la respuesta

@neko69.

Apenas le estoy dando una checadita a lo de patrones de diseño

Re: Patrones de diseño...

Pues todo tiene su función y su razón de ser...El decir: "Si, claro que son efectivos", sería igual que decir: "No, para nada no sirven". Es una pregunta compleja, no todo es un clavo para ser martillado...ni todo es una tuerca para ser enroscado.

La exclusividad de lo orientado a objetos, tampoco lo es. Sin embargo, aquí en JavaMexico creo encontrarás (no sólo de mí) que expliquemos patrones de diseño desde el punto de vista de un programador orientado a objetos (casi todos los que programamos por estas aguas -asumo- somos javaleros). Y (al igual que la pregunta 1), no todos los patrones de diseño son exclusivos de X o Y paradigma. Puede que algunos los puedas usar sólo en programación funcional, otros en estructural, otros en OOP...Y puede que otros sean sólo posibles en OOP o que puedan usarse en cualquier paradigma de programación.

¿Sí quedó bien o no respondí del todo tu pregunta?

Si, gracias @whismaster77

Por la respuesta. Si entendido y aclarado el asunto

0_o

Re: Si, gracias @WISHmaster77

De nada @Carraro, para algo más ya sabes.

OFF-Topic: @Carraro, 0 y van 2 ¡eh!; soy WISHmaster77 no whismaster...jajajajaja.

Imagen de dermonty

¿Entonces la cosa quedó así?

Hola que tal, antes que nada agradecer la publicación. Soy cara nueva por estos lados pero llevo rato siguiendo el sitio en twitter.

Al grano, si entendí la cosa quedó asi:

Antes, cada Student tenia una List con Notes, y ahora hay una "Pool" de Notes y sólo cuando las requerimos son cargadas, ¿lo capté bien?
Si fuera el caso, no acarrearía eso el problema que habria que buscar en un gigantesco pool de Notes?

Gracias por el tiempo dedicado a este comentario. Saludos y felices fiestas!

Imagen de neko069

No sé porqué.... pero me da

No sé porqué.... pero me da la impresión de que te equivocaste de post ;-)

Imagen de dermonty

¿Porqué?

¿Porqué el comentario? ¿Cual es el post al que haces referencia? Saludos.

Re: Re:Si, gracias @WISHmaster77

Si entendido y aclarado. @WISHmaster77

ja ja ja.

o_0

Ya no me equivocaré más.

Imagen de luxspes

Quien hablo de "pool"?

Antes, cada Student tenia una List con Notes, y ahora hay una "Pool" de Notes y sólo cuando las requerimos son cargadas, ¿lo capté bien?

Mas bien antes, cada Student tenia una cargaba con una List con Notes, y ahora hay una "Pool" de Notes sólo cuando las requerimos son cargadas.

Un pool, es una patron para reutilizar instancias de objetos, y bueno, este blog post (y los comentarios subsecuentes) no tratan sobre "pools" en ningun momento.

Imagen de luxspes

No sé porqué le puso un pool...

No sé porqué.... pero me da la impresión de que te equivocaste de post ;-)

No, si es este el post correcto, solo que por alguna razon dermonty agrego la idea de "pools" al lazy loading que se discute aquí (no se por que)

Imagen de dermonty

Gracias

Mas bien antes, cada Student tenia una cargaba con una List con Notes, y ahora hay una "Pool" de Notes sólo cuando las requerimos son cargadas.

Si, eso si lo entiendo, lo que preguntaba es que si para cargar las notas de ese Student se debe hacer una busqueda en todas las notas existentes, que yo asumo es esta función:

        notes = Note.find("byStudent", this);

Y sí, una disculpa por la confusión de mi mal uso del término pool

@dermonty

De hecho tu suposición fue bastante buena porque wishmaster obvio un poco el contexto.

Así es, el ejemplo se entiende mejor manejando un ORM. La llamada a  notes = Note.find("byStudent", this ); sería el disparador para hacer la búsqueda en la base de datos. Sin él ( aunque no aparezca como en el ejemplo ) se supone que las notas se cargarían desde el principio aún sin necesitarlas.

Entonces hablar de un pool de notas no es tan descabellado. De hecho es más cercano a la realidad.

Otro ejemplo puede ser la forma en la que los clientes de twitter cargan los mensajes. En vez de cargar los 1,00 o 10,000 mensajes que una cuenta tiene de un jalón, cargan de 20 en 20, y cargan solamente sobre demanda, cuando se requieren más.

Imagen de luxspes

to pool or not to pool, that is the dilemma

Entonces hablar de un pool de notas no es tan descabellado. De hecho es más cercano a la realidad.

Descabellado no, pero nadie lo habia mencionado, supongo que por eso le pareció a neko069 que dermonty se habia equivocado de discusión.

Entonces hablar de un pool de notas no es tan descabellado. De hecho es más cercano a la realidad.

Podria pensarse en un pool de notas, que actuara como una especie de cache para no tenerlas que ir a buscar a la base de datos, pero ahi ya nos estariamos metiendo mas en mecanismos de cache para optimizacion del lazy loading... y creo que entonces ya nos alejamos mucho de la idea original de este blog post... no?

En vez de cargar los 1,00 o 10,000 mensajes que una cuenta tiene de un jalón, cargan de 20 en 20, y cargan solamente sobre demanda, cuando se requieren más.

Ok, pero eso no significa que usen un pool (podrian... se me ocurren de hecho varias formas, pero no es "a fuerzas", tambien podrian ofrecer carga bajo demanda y no usar un pool).

Imagen de neko069

De hecho así fue, me sacó de

De hecho así fue, me sacó de onda que dermonty usara el término "pool" para referirse a la carga de los objetos Note ....
@OscarRyz ... creo que no entendí bien tu punto, a mi me parece más como que, el pool sería igual para las conexiones, y como dice @luxspes el tipo de carga sería más como que de un cache ... o ahora el que se perdió en la discusión fuí yo???... además creo que también ya me salí del tema central (que es la carga pasiva ....) necesito vacaciones...

Pues nadie lo mencionó pero

Pues nadie lo mencionó pero tampoco era así como que digamos "offtopic", por que si bien nadie mencionó ORM tampoco, el "findBy" sugiere buscar en algún lugar.

Un pool y un caché no son conceptos incompatibles, de hecho ambos se pueden implementar con, por ejemplo, un arreglo. El concepto no está limitado a una implementación especifica ( puede ser una base de datos, archivos planos, la red, objetos en memoria, whatever... )

Luego entonces, no estamos alejándonos del tópico inicial "LazyLoading" pues el punto es que un objeto inicialmente vacío, es cargado eventualmente de forma "perezosa" ya sea con un pool, cache o como sea.

Eeeeenfin!, el chiste es que no tiene los datos cuando se crea el objeto. :)

Re: De hecho así fue, me sacó de

Gracias, carga pasiva y no carga perezosa XD...siempre olvido el términon en español.

Re: Pues nadie lo mencionó pero

Así es. De hecho en un comentario anterior @OscarRyz me ha preguntado si era sólo para un ORM, pero no el tema es de discusión general, sólo (que desde mi punto de vista) es más simple entenderlo utilizando un ORM.

Y con lo del pool/cache/whatever, pues estaría bien discutirlo en otro tema (cómo lo han sugerido @neko69 y @luxspes).

jajajaj

Me ha gustado el ejemplo... creatividad tuya , buena muy buena.

Imagen de luxspes

la cosa es el modo com "pool" se introdujo en la discusion

Pues nadie lo mencionó pero tampoco era así como que digamos "offtopic", por que si bien nadie mencionó ORM tampoco, el "findBy" sugiere buscar en algún lugar.

La cuestion es que dermonty escribio: "Antes, cada Student tenia una List con Notes, y ahora hay una "Pool" de Notes y sólo cuando las requerimos son cargadas, ¿lo capté bien?"

Como queriendo implicar que el ejemplo de wishmaster77 (raiz de toda esta discusion) de alguna forma implicaba la introduccion de un pool (y no hay absolutamente nada en dicho ejemplo que implique un pool) y preguntando si "habia captado bien la idea", cosa que no hizo, por que la idea en ningun momento implicaba un pool.

Mi teoria (y no lo digo por molestar) es simplemente que dermonty utilizo la palabra "pool" sin entender realmente lo que es un "pool" (como el mismo lo admite mas adelante), quiza basado en incorrecto entendimiento de que para cargar algo solo cuando es requerido, es necesario tener un "pool" involucrado (cosa que por supuesto no es verdad)

jejej sip, de acuerdo. En

jejej sip, de acuerdo.

En fin, como siempre terminamos cambiando el tema. Así que concluiré diciendo:

.... y la respuesta a todo como bien sabemos es 0x2A, pero.. la pregunta fundamental es:

2B || !2B

¬¬