Объектно-ориентированное программирование и транзакции

Немного введения:

Класс содержит поля, и методы (позвольте мне пропустить свойства на этот раз).
Поля представляют состояние класса.
Методы описывают поведение класса.

В хорошо разработанном классе метод не изменит состояние класса, если это выдаст исключение, правильно? (Другими словами, что бы ни случилось, состояние класса не должно быть повреждено),

Вопрос:

Существует ли платформа, шаблон разработки, лучшая практика или язык программирования для вызова последовательности методов в транзакционном стиле, так, чтобы состояние любого класса не становилось измененным (в случае исключения), или все успешно выполняется?

Например:

// the class foo is now in the state S1
foo.MoveToState2();
// it is now (supposed to be) in the state S2
foo.MoveToFinalState();
// it is now (supposed to be) in the state, namely, S3

Конечно, исключение могло бы произойти оба в MoveToState2() и MoveToFinalState(). Но от этого блока кода я хочу класс foo быть или в S1 состояния или в S3.

Это - простой сценарий с включенным единым классом, нет ifнет whileникакие побочные эффекты, но я надеюсь, что представление является четким.

10
задан bohdan_trotsenko 29 December 2009 в 12:54
поделиться

9 ответов

Взгляните на шаблон Memento

Шаблон memento представляет собой шаблон проектирования программного обеспечения, который обеспечивает возможность восстановления объекта до его предыдущего состояния (отмена с помощью отката).

6
ответ дан 3 December 2019 в 18:33
поделиться

Самым простым и надежным "паттерном" для использования здесь является неизменяемая структура данных.

Вместо записи:

foo.MoveToState2();
foo.MoveToFinalState();

Вы пишете:

MyFoo foo2 = foo.MoveToState2();
MyFoo finalFoo = foo2.MoveToFinalState();

И реализуете методы соответственно - то есть MoveToState2 на самом деле ничего не меняет в MyFoo, он создает новую MyFoo, находящуюся в состоянии 2. Так же, как и в финальном состоянии.

Так работают классы строк в большинстве OO языков. Многие OO языки также начинают реализовывать (или уже реализовали) неизменяемые коллекции. После того, как у вас есть строительные блоки, довольно просто создать целую неизменяемую "сущность".

.
4
ответ дан 3 December 2019 в 18:33
поделиться

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

4
ответ дан 3 December 2019 в 18:33
поделиться

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

Поэтому программная транзакционная память может быть легко выражена в функциональных терминах - См. STM для F#

Ключевой идеей является концепция monads. Монад может быть использован для моделирования произвольных вычислений с помощью двух примитивов: Return для возврата значения и Bind для последовательности двух вычислений. Используя эти два элемента, можно смоделировать транзакционный монад, который контролирует и сохраняет все состояния в виде продолжений.

Можно попытаться смоделировать их объектно-ориентированным способом с помощью шаблона State+Memento, но в общем случае, транзакции в императивных языках (как и обычные OO-one) гораздо сложнее реализовать, так как можно выполнять произвольные побочные эффекты. Но, конечно, можно подумать об объекте, определяющем область видимости транзакции, который сохраняет, валидирует и восстанавливает данные по мере необходимости, так как они выставляют подходящий для этого интерфейс (паттерны, о которых я упоминал выше)

.
3
ответ дан 3 December 2019 в 18:33
поделиться

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

Но все становится действительно сложно, если побочные эффекты операторов "более внешними". Например, операции ввода/вывода, сетевые вызовы. Вы всегда должны анализировать общие изменения состояния ваших утверждений.

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

Обратная переадресация/прокрутка в прошлое часто не так тривиальна ;)

.
1
ответ дан 3 December 2019 в 18:33
поделиться

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

try {
   save state in local variables
   move to new state
} catch (innerException) {
   restore state from local variables
   throw new exception( innerException )
}
1
ответ дан 3 December 2019 в 18:33
поделиться
  1. Транзакционная память подходит здесь лучше всего.
  2. Вариантом может быть транзакционная память. Пример реализации можно найти здесь: http://www.codeproject.com/KB/dotnet/Transactional_Repository.aspx
  3. Memento pattern
  4. Также позвольте мне описать возможный шаблон реализации такого поведения: Определите базовый класс TransactionalEntity. Данный класс содержит словарь свойств. Все Ваши транзакционные классы наследуют от TransactionalEntity и должны работать над неким Dependency Properties/Fields, т.е. свойствами (getters/setters), которые хранят свои значения не в полях локального класса, а в словаре, который хранится в базовом классе. Затем вы определяете класс TransactionContext. Класс TransactionContext TransactionContext внутренне содержит набор словарей (по одному словарю на каждую сущность, участвующую в транзакции), и когда сущность, участвующая в транзакции, записывает все данные в словарь в контексте транзакции. Тогда все, что вам нужно, это, по сути, четыре метода:

    TransactionContext.StartTransaction(); TransactionalEntity.JoinTransaction(контекст TransactionContext); //если ваш язык/фреймворк поддерживает поля Thread Static, то вам не нужен этот метод. TransactionContext.CommitTransaction(); TransactionContext.RollbackTransaction();

Чтобы подвести итог, вам нужно сохранить состояние в базовом классе TransactionalEntity и во время транзакции TransactionalEntity будет взаимодействовать с TransactionContext.

Надеюсь, я достаточно хорошо объяснил.

.
1
ответ дан 3 December 2019 в 18:33
поделиться

Я думаю, что командный образец вполне может подойти для решения этой проблемы. Linky.

0
ответ дан 3 December 2019 в 18:33
поделиться

Я был удивлен, что никто не предложил явно простейший шаблон, чтобы использовать .. Узор

Таким образом, вы также можете устранить этот метод «FinalState» и просто используйте «ручку ()». Откуда вы знаете, какое финальное состояние? Шаблон Memento лучше всего используется с помощью команды, и обычно применяется к операциям GUI для реализации функции UNDO / REDO.

поля представляют собой состояние полей класса

представляет собой состояние объекта InstageSted ​​. Вы используете много раз неправильных определений терминов ООП. Обзор и исправить.

0
ответ дан 3 December 2019 в 18:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: