交易 0.1.0

交易 0.1.0

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最后发布2017年6月
SwiftSwift 版本3.0
SPM支持 SPM

Anton Bronnikov 维护。



交易 0.1.0

  • 作者
  • Anton Bronnikov

交易

Transactions 框架简化了对模型进行原子更改的操作

  • 它提供了一种通用机制,将对象的一致层次结构“链接”起来,这些对象应该同步且原子性地更改其状态。
  • 它定义了在每次事务开始、预先提交完整性检查、提交和回滚时触发的回调函数。
  • 它提供了一种便利方法来在闭包中包装事务性代码。这样的闭包将被事务开始回调函数前置,提交或回滚后跟置,并且将对事务上下文中的每个成员运行隐含的完整性检查。

这种方法允许将约束检查和备份/恢复操作封装在每个单独的类中,从而将相关代码放在一起,使整个逻辑更清晰且易于维护。

示例

假设有一个饼图对象,它将包含表示饼图面积百分比的元素。所有百分比的总额必须是 100,每个百分比必须大于零且不超过 100。

以下是 PieChart 类可能实现的示例

import Transactions

class PieChart : Transactable  {

    private (set) var elements: [String: PieChartElement] = [:]
    private var elementsBackup: [String: PieChartElement] = [:] // Backup to be used in case of rollback

    // Transaction context is a mediator class that "links" transaction
    // tree together.  There are two kinds of it, a root and a node.
    var transactionContext: TransactionContext { return _transactionContext! }
    private var _transactionContext: TransactionContext? = nil

    init() {
        _transactionContext = TransactionContext.createRoot(owner: self)
    }

    func onBegin(transaction: Transaction) {
        // Back up of internal state is not needed because onCommit(), onRollback() and initialization
        // make it sure that backup is always up to date anyway.
    }

    func onValidateCommit() throws {
        guard elements.count == 0 || elements.values.reduce(0, { $0 + $1.percentage }) == 100 else {
            throw PieChartError.totalPercentageIsNot100
        }
    }

    func onCommit(transaction: Transaction) {
        elementsBackup = elements // Overwrite the backup to release (potentially) deleted objects
    }

    func onRollback(transaction: Transaction) {
        elements = elementsBackup // Restore the state from backup
    }

    // ...

}

.. onValidateCommit() 的实现确保图表为空或所有百分比的总额精确等于 100。

PieChartElement.onValidateCommit() 的实现将检查每个单独的百分比是否在 (0, 100] 的范围内

class PieChartElement : Transactable {

    let label: String

    var percentage: Int {
        get { return _percentage }
        set { assert(transactionIsActive); _percentage = newValue }
    }
    private var _percentage: Int
    private var _percentageBackup: Int

    var transactionContext: TransactionContext { return _transactionContext! }
    private var _transactionContext: TransactionContext? = nil

    init(chart: PieChart, label: String, percentage: Int) {
        self.label = label
        self._percentage = percentage
        self._percentageBackup = percentage
        _transactionContext = TransactionContext.createNode(owner: self, parent: chart)
    }

    func onBegin(transaction: Transaction) { }

    func onValidateCommit() throws {
        guard _percentage > 0 else {
            throw PieChartError.elementIsNotPositive
        }
        guard _percentage <= 100 else {
            throw PieChartError.elementIsGreaterThan100
        }
    }

    func onCommit(transaction: Transaction) {
        _percentageBackup = _percentage
    }

    func onRollback(transaction: Transaction) {
        _percentage = _percentageBackup
    }

}

示例代码的其余部分(为了完整性)

class PieChartElement : Transactable {

    let label: String

    var percentage: Int {
        get { return _percentage }
        set { assert(transactionIsActive); _percentage = newValue }
    }
    private var _percentage: Int
    private var _percentageBackup: Int

    var transactionContext: TransactionContext { return _transactionContext! }
    private var _transactionContext: TransactionContext? = nil

    init(chart: PieChart, label: String, percentage: Int) {
        self.label = label
        self._percentage = percentage
        self._percentageBackup = percentage
        _transactionContext = TransactionContext.createNode(owner: self, parent: chart)
    }

    func onBegin(transaction: Transaction) { }

    func onValidateCommit() throws {
        guard _percentage > 0 else {
            throw PieChartError.elementIsNotPositive
        }
        guard _percentage <= 100 else {
            throw PieChartError.elementIsGreater100
        }
    }

    func onCommit(transaction: Transaction) {
        _percentageBackup = _percentage
    }

    func onRollback(transaction: Transaction) {
        _percentage = _percentageBackup
    }

}

enum PieChartError : Error {
    case elementIsNotPositive
    case elementIsGreaterThan100
    case totalPercentageIsNot100
}

最后,如何在 Swift Playground 中使用它

let chart = PieChart()

do {
    try chart.transaction({
        chart.addElement(label: "Part #1", percentage: 50) // Try to change 50 to 60
        chart.addElement(label: "Part #2", percentage: 50)
    })
} catch {
    print(error)
}

chart.elements.values.forEach({
    print("\($0.label), \($0.percentage)%")
})

如果在 Playgroun 中运行上面的代码并在百分比上做出更改(使它们过大、负数或总和不是 100),那么您将看到每次任何约束受到违反时,导致不一致更改的事务都将回滚到上一个状态(因此模型始终保持一致)。

这使得它可以简化模型更改的应用方式,因为您只需更改必须更改的内容,如果有任何错误,将抛出异常,您可以处理它,但数据仍然保持一致。