2010-07-29 2 views
8

Я ищу для реализации объекта, который ведется журналом или имеет постоянную транзакцию. То есть объект содержит данные (возможно, Карта). По мере внесения изменений в данные эти изменения сохраняются отдельно, изолированы, если вы захотите, чтобы любой внешний объект мог либо ссылаться на базовое состояние (до изменений), либо получать доступ к последним данным. Затем выполняется другая операция, которая фиксирует изменения в базовом состоянии.«Журналирование» или «дизайн транзакций»?

Это напоминает мне некоторую файловую систему ведения журнала Linux. Изменения в файловой системе записываются в журнал и только позже передаются в постоянное хранилище.

Возможно, это также похоже на концепцию «транзакции» в мире реляционной базы данных; то есть у вас есть некоторые данные, вы начинаете транзакцию и каким-то образом манипулируете данными. Параллельные процессы будут видеть старые данные без каких-либо изменений. Затем вы можете «отменить» транзакцию или «зафиксировать» свои изменения.

Я специально планирую реализовать это на Java, но, очевидно, это общий объектно-ориентированный шаблон, если он даже существует. Я надеюсь, что его можно хотя бы создать, но я не уверен в лучшем способе его реализации.

Кроме того, предположим, что объект содержит целую тонну данных, целую иерархию (вспомогательные объекты и т. Д.). Поэтому нельзя просто сохранить две копии всего дерева данных; это будет очень расточительно для памяти, и операция копирования (при фиксации) займет слишком много времени. Я хочу реализовать это в контексте игры с одной транзакцией на кадр, поэтому она действительно должна быть оптимальной.

ответ

7

Лучший способ добиться того, что вы хотите, - сделать объект и все его подобъекты неизменными. Тогда два потока могут работать на них без каких-либо конфликтов, и вам не нужно сохранять две копии всего. Единственное, что потребует двух копий, это вещи, которые действительно меняются, и они могут быть очень маленькими.

Предположим, что объект A состоит из объектов B и C. Объект B состоит из объектов D и E. И объект C состоит из объектов F и G. Таким образом, A, B и C представляют собой всего два указателя и D, E, F и G - все, что они есть.

Сначала вы создаете свой первоначальный экземпляр и передаете его обоим потокам.

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

Таким образом, обе нити указывают на тот же экземпляр, никакая дополнительная память не расходуется, и нет никаких проблем многопоточности, потому что объекты никогда не меняются.

Теперь ThreadOne необходимо изменить объект F. Для этого он просто создает новый F, новый C, чтобы его содержать, и новый A, содержащий новый C. Оригинальные B, D, E и G не изменяются и не нуждаются в копировании.

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

Два потока разделяют экземпляры B, D, E и G.

Теперь ThreadOne нужно изменить объект E.

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

Теперь ThreadTwo нужна последняя версия, поэтому ThreadOne просто дает ему указатель на его копию.

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

Поскольку объекты являются неизменными, не существует никакой опасности каких-либо проблем многопоточности, и ThreadOne может пойти прямо на внесение изменений, каждый раз создавая новый экземпляр только те части, которые были изменены, и их контейнеры.

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 } } 

Это быстрый, эффективный с точки зрения памяти и поточный сейф.

+1

Ничего себе, это действительно здорово, и до сих пор я никогда не смотрел в неизменные классы. У них есть определенные положительные моменты, и я думаю, что это действительно может быть лучшим решением этой проблемы! Единственный недостаток, который я вижу, - это утомительность воссоздания родительских объектов при каждом изменении дочернего объекта, но для других преимуществ я думаю, что смогу справиться с этим. – Ricket

1

Что вы ищете, это Command Pattern. Вы создаете объекты Command, которые выполняют и отменяют изменения, и просто сохраняете некоторое базовое состояние, а затем воспроизводите все команды, необходимые для того, чтобы объект попадал в нужное вам состояние.

+0

Не совсем. Мне нужно, чтобы базовое состояние и последние состояния были беспорядочно доступными; У меня будет два потока, один из которых работает с базовым состоянием, а другой - с последним состоянием. С шаблоном команды я бы в основном перевернул объект назад и вперед (отмена и повторное редактирование), и это было бы ОЧЕНЬ неэффективно, как вы можете себе представить. – Ricket

+0

, тогда вы переигрываете из базового состояния в любое состояние, которое вам «случайно» нужно, как только вы перейдете к определенной точке, вы «пересобираете» или «снимок» независимо от состояния, в котором вам нужно бежать, и использовать его в качестве базы и воспроизведения оттуда , Это «стандартный» шаблон для того, чтобы делать то, что вы ищете. Если вы не пытались его реализовать, вы не знаете, как он будет преформирован. Так работают все системы журналов, для этого не существует волшебного алгоритма. Команда в основном дельта из предыдущего состояния. –

1

То, что вы описываете, звучит очень похоже на Memento pattern. Однако вы указали, что не хотите сохранять полное состояние объекта, чтобы шаблон не работал для вас «из коробки».

Однако, возможно, вы сможете изменить его так, чтобы память включала только дельта, а не полное состояние объекта. Это приведет к уменьшению объема памяти. Однако это также означает, что вы можете только откатываться от состояния в порядке, поскольку их дельта будет строиться друг на друге.

1

Вы можете сочетать шаблоны Command и Memento. У вас могут быть конкретные команды, которые реализуют операции фиксации и отката и надписи, которые сохраняют состояние измененного объекта до фиксации.

Смежные вопросы