测试已测试 | ✗ |
Lang语言 | SwiftSwift |
许可证 | MIT |
发布最后发布 | 2017年6月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✓ |
由 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),那么您将看到每次任何约束受到违反时,导致不一致更改的事务都将回滚到上一个状态(因此模型始终保持一致)。
这使得它可以简化模型更改的应用方式,因为您只需更改必须更改的内容,如果有任何错误,将抛出异常,您可以处理它,但数据仍然保持一致。