【问题标题】:Object-oriented programming & transactions面向对象的编程和事务
【发布时间】:2010-12-30 18:39:59
【问题描述】:

一点介绍:

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

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

问题:

是否有框架、设计模式、最佳实践或编程语言以事务样式调用一系列方法,从而使任一类的状态都不会改变(在异常情况下),或者一切都会成功?

例如:

// 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,没有副作用,但我希望这个想法很清楚。

【问题讨论】:

    标签: oop architecture


    【解决方案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 上下文); //如果你的语言/框架支持线程静态字段,那么你不需要这个方法 TransactionContext.CommitTransaction(); TransactionContext.RollbackTransaction();

    综上所述,您需要将状态存储在基类TransactionalEntity中,并且在事务期间TransactionalEntity将与TransactionContext合作。

    希望,我已经解释得够好了。

    【讨论】:

      【解决方案2】:

      我也会考虑 saga 模式,您可以将对象当前状态的副本传递给 MoveToState2,如果它引发异常,您可以在内部捕获该异常并使用原始状态的副本进行回滚。您也必须对 MoveToState3 执行相同的操作。但是,如果服务器在回滚期间崩溃,您可能仍会处于损坏状态,这就是数据库如此出色的原因。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:

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

          通过这种方式,您还可以消除“finalState”方法而只使用“handle()”。 你怎么知道最终状态是什么? memento 模式最适合与 Command 模式一起使用,通常应用于 GUI 操作以实现撤消/重做功能。

          字段表示类的状态

          Fields 表示实例化对象的状态。您多次使用错误的 OOP 术语定义。审查并更正。

          【讨论】:

            【解决方案5】:

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

            而不是写:

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

            你写:

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

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

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

            【讨论】:

              【解决方案6】:

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

              但如果语句的副作用“更外在”,事情就会变得非常困难。例如 I/O 操作、网络调用。您总是必须分析语句的整体状态变化。

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

              恢复/回滚到过去通常不是那么简单;)

              【讨论】:

                【解决方案7】:

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

                因此软件事务内存可以很容易地用功能术语表达 - 请参阅STM for F#

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

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

                【讨论】:

                  【解决方案8】:

                  看看Memento pattern

                  备忘录模式是一种软件设计模式,它提供了将对象恢复到其先前状态(通过回滚撤消)的能力。

                  【讨论】:

                  • 正如我在帖子中所说,纪念品更多地用于 gui 环境中,用于持久性和撤消/重做。尽管基本概念是相同的(创建另一个代表状态的类),但在完全安全之前,您不应该转换到新状态。这建议尝试创建新状态,并且仅当此步骤成功时,才能更改新状态中的状态。
                  【解决方案9】:

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

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

                  【讨论】:

                    【解决方案10】:

                    我认为命令模式可以很好地解决这个问题。 Linky.

                    【讨论】:

                      猜你喜欢
                      • 2020-05-18
                      • 1970-01-01
                      • 2010-09-18
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-06-29
                      • 1970-01-01
                      • 2014-06-03
                      相关资源
                      最近更新 更多