面向对象的编程&交易

时间:2009-12-29 12:54:20

标签: oop architecture

一点介绍:

类包含字段和方法(这次让我跳过属性) 字段代表类的 方法描述了类的行为

在设计良好的类中,如果类抛出异常,方法不会改变类的状态,对吧? (换句话说,无论发生什么,班级的状态都不应该被破坏)

问题:

是否有框架,设计模式,最佳实践或编程语言来调用事务样式中的一系列方法,以便任何类的状态都不会被更改(如果是异常),或者一切都成功了?

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

当然,MoveToState2()MoveToFinalState()都可能发生异常。但是从这段代码中我希望类foo处于状态S1或S3。

这是一个简单的场景,涉及单个类,没有if,没有while,没有副作用,但我希望这个想法很明确。

10 个答案:

答案 0 :(得分:6)

查看Memento pattern

  

memento模式是一种软件设计模式,可以将对象恢复到以前的状态(通过回滚撤消)。

答案 1 :(得分:4)

不是最有效的方法,但您可以拥有一个代表您的交易数据的对象。启动事务时,请复制数据并对其执行所有操作。当事务成功结束时,将副本移动到您的实际数据 - 这可以使用指针完成,因此不必太低效。

答案 2 :(得分:4)

这里使用的最简单,最可靠的“模式”是一个不可变的数据结构。

而不是写作:

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

你写道:

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

并相应地实现这些方法 - 也就是说,MoveToState2实际上并未改变MyFoo的任何内容,它会创建一个处于状态2的新MyFoo。与最终状态类似。

这是大多数OO语言中的字符串类的工作方式。许多OO语言也开始实现(或已经实现)不可变集合。一旦你有了构建块,创建一个完整的不可变“实体”就相当简单了。

答案 3 :(得分:3)

功能编程是一种似乎非常适合事务计算的范例。由于没有明确声明不允许出现副作用,因此您可以完全控制所有数据流。

因此,软件事务内存可以用功能术语轻松表达 - 请参阅STM for F#

关键思想是monads的概念。 monad可用于通过两个基元对任意计算进行建模:返回以返回值,绑定以对两个计算进行排序。使用这两个,您可以建模一个事务monad,它以continuation的形式控制和保存所有状态。

可以尝试通过State + Memento模式以面向对象的方式对这些进行建模,但通常情况下,命令式语言中的事务(如常见的OO)更难以实现,因为你可以执行任意的副作用。但是当然你可以想象一个定义事务范围的对象,它根据需要保存,验证和恢复数据,因为它们为此公开了一个合适的接口(我上面提到的模式)。

答案 4 :(得分:1)

这在任何地方都很难实现,但只是在本地保存状态,然后在异常的情况下恢复它将在简单的场景中工作。您必须捕获并重新抛出异常,这可能会在某些语言中丢失某些上下文。如果可能的话,最好将其包装起来以保留上下文。

try {
   save state in local variables
   move to new state
} catch (innerException) {
   restore state from local variables
   throw new exception( innerException )
}

答案 5 :(得分:1)

  1. 交易记忆最合适。
  2. 选项可以是事务存储。您可以在此处找到示例实现: http://www.codeproject.com/KB/dotnet/Transactional_Repository.aspx
  3. 纪念品模式
  4. 还让我描述一下如何实现此类行为的可能模式: 定义基类TransactionalEntity。该类包含属性字典。 您的所有事务类都继承自TransactionalEntity,并且应该在某种依赖属性/字段上运行,即属性(getters / setters),它存储的值不在本地类字段中,而是在字典,存储在基类中。 然后定义TransactionContext类。 TransactionContext类内部包含一组字典(每个参与事务的实体都有一个字典),当事务实体参与事务时,它会将所有数据写入事务上下文中的字典。那么你所需要的只是四种方法:

    TransactionContext.StartTransaction(); TransactionalEntity.JoinTransaction(TransactionContext context); //如果您的语言/框架支持Thread Static字段,那么您不需要此方法 TransactionContext.CommitTransaction(); TransactionContext.RollbackTransaction();

  5. 总而言之,您需要在基类TransactionalEntity中存储状态,并且在事务TransactionalEntity期间将与TransactionContext合作。

    我希望,我已经解释得很清楚了。

答案 6 :(得分:1)

使用对象复制方法时,您必须注意要回滚的语句仅影响对象或数据本身(以及聚合)。

但如果陈述的副作用是“更多外部”,事情变得非常困难。例如I / O操作,网络调用。您始终必须分析语句的整体状态更改。

如果你触摸静态数据(或邪恶的可变单例),它也会变得非常棘手。恢复这些隔离的数据很困难,因为其他线程可能会在其间修改它们(您可能会面临丢失的更新)。

恢复/回滚过去往往不是那么简单;)

答案 7 :(得分:1)

我还会考虑saga模式,你可以将对象当前状态的副本传递给MoveToState2,如果它抛出异常,你可以在内部捕获它并使用原始状态的副本进行回滚。你也必须对MoveToState3做同样的事情。但是,如果服务器在回滚期间崩溃,您可能仍会遇到损坏状态,这就是数据库如此优秀的原因。

答案 8 :(得分:0)

我认为命令模式可能非常适合这个问题。 Linky.

答案 9 :(得分:0)

我很惊讶没有人明确建议使用最简单的模式.. State Pattern

通过这种方式,你也可以消除'finalState'方法并只使用'handle()'。 你怎么知道最终的状态是什么? memento模式最适用于Command模式,通常适用于GUI操作以实现撤消/重做功能。

  

字段代表类的状态

字段表示实例化对象的状态。您多次使用错误的OOP术语定义。检讨并纠正。