2010-07-29 16 views
8

Estoy buscando implementar un objeto que está registrado por diario, o tiene una transacción persistente en él. Es decir, el objeto contiene datos (quizás un mapa). A medida que se realizan cambios en los datos, esos cambios se mantienen por separado, en la zona de pruebas si se quiere, de modo que cualquier objeto externo puede hacer referencia al estado base (antes de los cambios) o puede acceder a los datos más recientes. Luego hay otra operación que compromete los cambios en el estado base.patrón de diseño "Diario" o "transacciones"?

Me recuerda algo al sistema de archivos de diario de Linux. Los cambios en el sistema de archivos se escriben en un diario y luego solo se envían al almacenamiento permanente.

También es quizás más similar al concepto de una "transacción" en el mundo de las bases de datos relacionales; es decir, tiene algunos datos, comienza una transacción y manipula los datos de alguna manera. Los procesos concurrentes verán los datos antiguos sin ninguno de sus cambios. Luego puede "retrotraer" la transacción o "comprometer" sus cambios.

Estoy específicamente buscando implementar esto en Java, pero obviamente es un patrón general orientado a objetos, si es que existe. Espero que al menos se pueda crear, pero no estoy muy seguro de la mejor manera de implementarlo.

Además, supongamos que el objeto contiene toda una tonelada de datos, una jerarquía completa (subobjetos, etc.). Entonces uno no puede simplemente guardar dos copias de todo el árbol de datos; sería un desperdicio de memoria y la operación de copia (en la confirmación) tomaría demasiado tiempo. Estoy buscando implementar esto en el contexto de un juego, con una operación de compromiso por cuadro, por lo que realmente debe ser óptimo.

Respuesta

7

La mejor manera de lograr lo que desea es hacer que el objeto y todos sus subobjetos sean inmutables. Entonces los dos hilos pueden operar en ellos sin ningún conflicto, y no tiene que mantener dos copias de todo. Las únicas cosas que requerirían dos copias son las que realmente cambian, y esas pueden ser muy pequeñas.

Supongamos que el objeto A está compuesto de los objetos B y C. El objeto B está compuesto por los objetos D y E. Y el objeto C está compuesto por los objetos F y G. Por lo tanto, A, B y C son solo dos indicadores, y D, E, F y G son lo que sean.

Primero crea su instancia inicial y se la da a ambos hilos.

ThreadOne -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 
ThreadTwo -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 

Así que ambos hilos están apuntando en la misma instancia, sin memoria adicional se consume, y no hay problemas de threads, porque los objetos no cambian nunca.

Ahora ThreadOne necesita modificar el objeto F. Para ello, crea una nueva F, una nueva C para contenerla y una nueva A para contener la nueva C. La B original, D, E y G no cambian, y no necesitan ser copiados.

ThreadOne -> A2{ B1{ D1, E1 } C2{ F2, G1 } } 
ThreadTwo -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 

Los dos hilos están compartiendo los casos de B, D, E y G.

Ahora ThreadOne necesita modificar objeto E.

ThreadOne -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 
ThreadTwo -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 

Ahora ThreadTwo necesita la última versión, entonces ThreadOne simplemente le da un puntero a su copia.

ThreadOne -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 
ThreadTwo -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 

Dado que los objetos son inmutables, no hay peligro de cualquier problema de roscado, y ThreadOne puede ir a la derecha en hacer cambios, cada vez que la creación de una nueva instancia de sólo las partes que han cambiado, y sus envases.

ThreadOne -> A4{ B3{ D2, E2 } C2{ F2, G1 } } 
ThreadTwo -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 

ThreadOne -> A5{ B3{ D2, E2 } C3{ F3, G1 } } 
ThreadTwo -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 

Esto es rápido, eficiente en la memoria y seguro para roscas.

+1

Guau, eso es realmente genial y nunca he visto clases inmutables hasta ahora. Tienen positivos definitivos y creo que esta puede ser la mejor solución para este problema. El único inconveniente que veo es la tediosidad de volver a crear objetos padre cada vez que cambia un objeto secundario, pero para los demás beneficios, creo que puedo sobrellevarlo. – Ricket

1

Lo que usted está buscando es el Command Pattern. Puede crear objetos de comando que realicen y deshagan los cambios y simplemente almacene un estado base y luego vuelva a reproducir los comandos necesarios para colocar el objeto en el estado que desea.

+0

No del todo. Necesito que el estado base y los últimos estados sean accesibles al azar; Tendré dos hilos, uno que funciona del estado base y el otro que funciona con el último estado. Con el patrón de comando básicamente estaría volteando el objeto hacia adelante y hacia atrás (deshaciendo y rehaciendo) y sería MUY ineficiente, como seguramente puede imaginarse. – Ricket

+0

y luego repite desde el estado base al estado que "aleatoriamente" necesita, una vez que vuelve a un punto específico, "rebase" o "instantánea" cualquier estado desde el que necesite ejecutar y use eso como base y vuelva a reproducir desde allí . Este es el patrón "estándar" para hacer lo que está buscando. Si no ha intentado implementarlo, ___no sé__ cómo se formará. Así es como funcionan todos los sistemas de journal ling, no hay un algoritmo mágico para hacer esto. El comando son básicamente deltas del estado anterior. –

1

Lo que está describiendo suena muy parecido al Memento pattern. Sin embargo, especificó que no desea almacenar el estado completo del objeto, por lo que ese patrón puede no funcionar para usted "out-of-the-box".

Sin embargo, es posible que pueda modificarlo para que el recuerdo incluya solo los deltas, no el estado del objeto completo. Eso daría como resultado una huella de memoria más pequeña. Sin embargo, también significaría que solo podría retroceder los estados en orden, ya que sus deltas se construirían unos sobre otros.

1

Puede acoplar los patrones de Comando y Memento. Puede tener comandos concretos que implementen las operaciones de compromiso y retrotracción y los recuerdos que guardarán el estado del objeto modificado antes de la confirmación.