Object-oriented programming & transactions

2020-06-03 03:41发布

A little intro:

Class contains fields and methods (let me skip properties this time).
Fields represent a state of the class.
Methods describe behavior of the class.

In a well-designed class, a method won't change the class's state if it throws an exception, right? (In other words, whatever happens, class's state shouldn't be corrupted)

Question:

Is there a framework, a design pattern, best practice or a programming language to call a sequence of methods in a transactional style, so that either class's state don't get changed (in case of exception), or everything succeeds?

E.g.:

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

Surely, an exception might occur both in MoveToState2() and MoveToFinalState(). But from this block of code I want the class foo to be either in the state S1 or S3.

This is a simple scenario with a single class involved, no if's, no while's, no side effects, but I hope the idea is clear.

10条回答
一纸荒年 Trace。
2楼-- · 2020-06-03 03:53

I would also consider the saga pattern, you could pass a copy of the objects current state into MoveToState2 and if it throws an exception you could catch that internally and use the copy of the original state to rollback. You would have to do the same with MoveToState3 too. If however the server crashed during a rollback you might still get corrupted state, that's why databases are so good.

查看更多
混吃等死
3楼-- · 2020-06-03 03:56

Not the most efficient method, but you could have an object that represents your transactional data. When you start a transaction, make a copy of the data and perform all operations on that. When the transaction ends successfully, move the copy to your real data - this can be done using pointers, so need not be too inefficient.

查看更多
小情绪 Triste *
4楼-- · 2020-06-03 03:58

I think a Command Pattern could be well suited to this problem. Linky.

查看更多
看我几分像从前
5楼-- · 2020-06-03 04:00

I was astonished that no one suggested explicitly the simplest pattern to use .. the State Pattern

In this way you can also eliminate that 'finalState' method and just use 'handle()'. How do you know which final state is? The memento pattern is best used with the Command pattern, and usually applies to GUI operations to implement the undo/redo feature.

Fields represent a state of the class

Fields represents the state of the instanced object. You use many times wrong definitions of the OOP terms. Review and correct.

查看更多
Evening l夕情丶
6楼-- · 2020-06-03 04:01

Functional programming is a paradigm that seems to fit well to transactional computations. Since no side-effects are allowed without explicit declaration, you have full control of all data flow.

Therefore software transactional memory can be expressed easily in functional terms - See STM for F#

The key idea is the concept of monads. A monad can be used to model an arbitrary computation through two primitives: Return to return a value and Bind to sequence two computations. Using these two, you can model a transactional monad that controls and saves all state in form of continuations.

One could try to model these in an object-oriented way through a State+Memento pattern, but generally, transactions in imperative languages (like the common OO-ones) are much more difficult to implement since you can perform arbitrary side-effects. But of course you can think of an object defining a transaction scope, that saves, validates and restores data as needed, given they expose a suitable interface for this (the patterns I mentioned above).

查看更多
smile是对你的礼貌
7楼-- · 2020-06-03 04:03

This would be pretty ugly to implement everywhere, but just saving the state locally, then restoring it in the case of an exception would work in simple scenarios. You'd have to catch and rethrow the exception, which may lose some context in some languages. It might be better to wrap it if possible to retain the context.

try {
   save state in local variables
   move to new state
} catch (innerException) {
   restore state from local variables
   throw new exception( innerException )
}
查看更多
登录 后发表回答