viernes, 9 de julio de 2010

Memento command

El patrón de diseño memento es muy simple y bastante útil. Como todo en la vida, también tiene sus puntos negativos. Por esta razón existe otro patrón de diseño llamado command que soluciona esos problemas.

El patrón de diseño Memento

Supongamos que queremos hacer un sistema de Undo/Redo. Esto significa que, hagamos la acción que hagamos sobre un objeto que llamaré primario, hemos de tener la posibilidad de volver a un estado anterior. (Nota: Llamo acción a una operación que modifica el estado.)

El patrón de diseño memento resuelve este problema y es muy sencillo. Lo que hay que hacer es incluir en el objeto dos operaciones:

  • Guarda estado
  • Recupera estado

Estas operaciones no son acciones en el sentido de modificar el estado, incluso al recuperar el estado lo que hacemos es volver a un estado anterior, no modificar el actual.

La operación que guarda el estado lo que hace es generar un objeto con el estado encapsulado dentro. A este objeto se le llama el objeto memento (recuerdo). Este objeto no tiene operaciones y lo único que se puede hacer con él es pasarlo a "recupera estado". Al recuperar estado no se destruye el memento, sólo se usa para recuperar el estado.

De esta manera:

  • Se reifica el estado en un objeto memento. A partir de este momento se puede guardar, ordenar, etiquetar y, en general, gestionar los estados.
  • El objeto memento es opaco no necesitamos tener conocimiento de cómo es ese estado por dentro.
  • Objetos memento de objetos primarios de distinto tipo deberán tener distinto tipo, de esta forma se mantendría la seguridad del tipado.
  • La idea de copiar y restaurar estados es simple y fácil de entender.

Sin embargo, el patrón memento tiene un gran problema. Siempre se guarda todo el estado y este puede ser muy grande. Si quisiéramos tener un sistema de Undo/Redo que recuerde muchos estados anteriores, el problema del almacenamiento de los estados llega a ser costoso en términos de uso de memoria.

Hacia un sistema de estados diferencial

La solución obvia es hacer un sistema de cambio de estados diferencial. En este sistema sólo se guardan los cambios entre dos estados. De hecho, la operación "Undo" suele deshacer únicamente una acción cada vez. ¿Por qué guardar cambios de estado completos cuando sólo es una acción la involucrada?

Ideemos entonces un sistema que por cada acción genere un objeto "minimemento" que indique cómo se deshace esa acción desde el estado actual. Llamaré a estos objetos átomos.

Ahora las dos operaciones del objeto primario serían:

  • Obtener el átomo.
  • Realizar "undo" usando el átomo.

Gráficamente sería así:


He sido cuidadoso en distinguir los objetos que existen en memoria (son los sombreados en azul) y los que existieron pero que ahora no existen (los de relleno blanco).

Este pequeño cambio que hemos hecho conlleva una gran pérdida. Ahora no tenemos objetos memento y si queremos pasar del estado 0 al estado 3 no basta con usar el átomo C. Tenemos que usar los átomos A, B y C en ese orden. De hecho, si intentásemos usar el átomo C sobre el estado 0 tendríamos un resultado inesperado ya que los átomos sólo deshacen una acción. En concreto, el átomo C la acción que va del estado 3 al 2. Por esa razón, deshacerla tiene que ser forzosamente del estado 2 al 3. En definitiva: al usar un sistema diferencial se añade una precondición que no existía en la operación de "recuperar estado" con el patrón memento.

En principio, no pasa nada ya que de todas formas podemos volver a cualquier estado anterior y, con un pequeño cambio, posterior.

Rehaciendo estados

Supongamos que hemos aplicado con éxito los átomos A, B y C de manera que hemos vuelto al estado 3. Como ocurría con el patrón memento, esto no significa que hayamos destruido los átomos A, B y C. De hecho, podríamos usarlos para volver a aplicar las acciones y llegar de nuevo al estado 0.

Esto sólo implicaría tener que añadir una nueva operación de realizar "redo" usando un átomo.

El lector astuto habrá visto que lo que hacen los átomo es reificar las acciones. Es decir, las acciones son objetos ahora (acción=átomo). Esta idea es la que subyace al patrón de diseño command.

Existe realmente un pequeño cambio para llegar al patrón de diseño command. Nosotros venimos del patrón memento y es el objeto primario el que tiene las operaciones "undo" y "redo". En el caso del patrón command estas operaciones están en el propio átomo que guardará internamente una referencia al objeto primario.

Las ideas clave del patrón de diseño command son:

  • Las acciones son objetos. Si quieres modificar el objeto primario lo que hay que hacer es crear un objeto acción del tipo adecuado.
  • Sólo las acciones modifican el objeto primario.
  • Las acciones, al ser objetos, pueden anotarse, extenderse, heredarse, etc. Por esa razón pueden hacerse, deshacerse y rehacerse. Si fueran sólo una llamada, sólo podrían hacerse.

0 comentarios:

Publicar un comentario en la entrada