以声明式、易读的格式描述需求。
当定义应用程序应该如何工作时,需要将许多需求实现在源代码中。 显然,每个需求都可以用人类友好的语言描述,也可以用编程语言(计算机友好的语言)正式化。
通常,需求是作为任务/模型等的一部分批量实现的,而没有将特定需求直接转换为应用程序中的确切代码行/范围的明显对应关系。
在大多数情况下,规范(任务定义)中的每个单个需求都被转换为数据模型或业务逻辑中的一些代码,仅此而已。 这意味着此类实现提供的语义不多 - 如果未满足此需求,则不清楚如何以正式的方式向外部范围报告问题,以及/或以人类友好的格式向用户(通过 GUI)报告。 如果实现了此类报告,通常会导致需求实现分散到几个不同的部分:实际需求检查、用于向外部范围(或 API 用户)报告的错误描述,以及用于通过 GUI 向用户报告的人类友好的表示形式。
这种需求实现方式难以测试/验证、难以随着时间推移保持一致(当给定需求发生微小更改时),并且使源代码难以理解和推理。
理想情况下,应该有一个工具可以:
可以针对给定的数据值评估每个需求(该数据值可以是原子数据类型或复杂数据类型)。 换句话说,每个需求定义都可以表示为采用一个或多个输入参数并返回 Boolean
值的函数 - true
表示使用提供的输入值满足需求,false
表示相反。
推荐使用 SwiftPM 进行安装,但也支持开箱即用的 Carthage。
这是一个小巧且非常简单的库,但功能强大。
Requirement
是主要的数据类型,它实际上表示单个需求。 请注意,这是一个 struct
,因此一旦创建,它就作为一个单一的原子值工作。
要定义需求,请创建 Requirement
的实例。 它的构造函数接受两个必要的参数 - 以 String
形式表示的人类友好的描述,以及实现正式表示的闭包。 此外,Requirement
是一种泛型类型,Input
泛型类型表示闭包的预期输入参数的类型。
这是一个如何创建需求的示例,即整数不应等于零。
let r = Requirement<Int>("Non-zero") { $0 != 0 }
可以使用辅助类型别名 Require
来实现相同的目的
let r = Require<Int>("Non-zero") { $0 != 0 }
在上面的示例中,我们创建了一个 Requirement
实例,该实例应评估 Int
类型的值。 我们传递一个字符串作为构造函数函数的唯一参数,而第二个参数(闭包)作为尾随闭包传递。 该闭包包含每次需要评估此需求时将调用的代码,并将需要检查的相应值作为唯一的输入参数。
请注意,如果需求包含诸如 AND、OR 或任何其他逻辑运算符之类的短语,则此类需求应被分成独立的需求。
创建需求后,这是一个如何使用它来检查潜在合适值的示例。
if
r.isFulfilled(with: 14) // returns Bool
{
// given value - 14 (Int) - fulfills the requirement
// r.title - the description that has been provided
// during requirement initialization
print("\(r.title) -> YES")
}
else
{
// this code block will be executed,
// if 0 will be passed into the r.isFulfilled(...)
print("\(r.title) -> NO")
}
可以通过利用 Swift 错误处理 来完成相同的检查,请参见下面的示例。
do
{
try r.check(with: 0) // this will throw exception
}
catch
{
print(error) // error is of 'RequirementNotFulfilled' type
}
RequirementNotFulfilled
数据类型有两个参数
let requirement: String
,其中包含需求的描述;let input: Any
,其中包含已被评估且未能满足需求的精确输入数据值。虽然 Requirement
本身可能更适合于实现 数据模型,但有一些助手使用了相同的想法,但提供了特殊的 API,该 API 在实现 业务逻辑 时更方便内联使用。 这些助手被封装到名为 REQ
的特殊 enum
中,当需求未满足时,它们都会抛出 VerificationFailed
错误的实例,其中一些可能会返回可在代码中进一步使用的值。
当您有一个 Optional
值或者您有一个产生 Optional
值的函数/闭包,并且您只需要在它不是 nil
时才需要该值,否则抛出错误
// the following expression will throw
// if the value from closure is 'nil' or just return
// unwrapped value of the optional from closure overwise
let nonNilValue = try REQ.value("Value is NOT nil") {
// return here an optional value,
// it might be result of an expression
// or an optional value captured from the outer scope
}
与上述相同,但不返回任何内容。 当您有一个 Optional
值或者您有一个产生 Optional
值的函数/闭包,并且您需要确保该值不是 nil
,否则抛出错误
// the following expression does not return anything,
// it will throw if value IS 'nil'
// or pass through silently otherwise
try REQ.isNotNil("Value is NOT nil") {
// return here an optional value,
// it might be result of an expression
// or an optional value captured from the outer scope
}
当您有一个 Optional
值或者您有一个产生 Optional
值的函数/闭包,并且您需要确保该值是 nil
,否则抛出错误
// the following expression does not return anything,
// it will throw if value is NOT 'nil'
// or pass through silently otherwise
try REQ.isNil("Value IS nil") {
// return here an optional value,
// it might be result of an expression
// or an optional value captured from the outer scope
}
当您有一个 Bool
值或者您有一个产生 Bool
值的函数/闭包,并且您只想在它是 true
时才继续,否则抛出错误(如果它是 false
)
// the following expression does not return anything,
// it will throw if value is 'false'
// or pass through silently otherwise
try REQ.isTrue("Value is TRUE") {
// return here a boolean value,
// it might be result of an expression
// or an boolean value captured from the outer scope
}
VerificationFailed
错误类型只有一个参数
let description: String
,其中包含传递给相应 REQ.*
函数的需求描述。