65.9K
CodeProject 正在变化。 阅读更多。
Home

使用规格设计模式

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.93/5 (4投票s)

2009年4月5日

CPOL

3分钟阅读

viewsIcon

26535

downloadIcon

194

这是关于规格设计模式的四部分系列文章的最后一部分。

引言

这是关于规格设计模式及其如何使用 VB.NET 实现的四部分系列文章中的第三篇。

背景

规格设计模式由 Eric Evans 创建。他在其著作《领域驱动设计》中对此模式进行了描述。本系列文章展示了如何使用 VB.NET 实现此模式。

使用代码

随附的示例项目在 GenericTests.vb 文件中包含单元测试。这些测试说明了如何使用通用规格。该项目使用 NUnit 进行单元测试。如果您未安装 NUnit,应从项目中删除 GenericTests.vb 并删除对 nunit.framework 的引用。

继承自通用规格

在本系列文章的第三篇中,我们创建了一个可重用且可继承的通用 Specification 基类,以创建适用于任何类型对象的规格。

在本最终文章中,我们将创建一个 ContainerSpecification,它使用 Specification 类来处理我们在早期文章中编写的 Container 对象。我们的 ContainerSpecification 声明如下:

Public Class ContainerSpecification
Inherits Specification(Of Container)

您会记得,当我们声明 第三部分 中的 Specification 类时,我们使用了 (Of T) 符号来指示将在创建实例时传入一个类型。通过指定 (Of Container),我们向 Specification 类表明 T 代表 Container

第二部分 中,我们看到容器的特性可以使用标志枚举来定义。由于 ContainerSpecification 代表一个必需的特性,因此我们需要在规格中定义一个变量来存储它。

Private _requiredFeature As ContainerFeature

Constructor 非常简单,它需要所需的特性作为参数并设置成员变量。

Public Sub New(ByVal requiredFeature As ContainerFeature)
  _requiredFeature = requiredFeature
End Sub

我们需要重写 SpecificationRule 函数。此函数定义在基类 Specification 中,但每个规格都必须实现自己的逻辑。在这种情况下,逻辑很简单,即检查传入的 Containercandidate 对象)是否具有规格定义的所需特性。

Protected Overrides Function SpecificationRule(ByVal candidate As Container) As Boolean
  Return CType(candidate.Features And _requiredFeature, Boolean)
End Function

最后,为了能够使用 AND、OR 和 NOT 等逻辑运算符组合 ContainerSpecifications,我们需要重载这些运算符。这些重载函数利用了基类 Specification 中的 AndOperatorOrOperatorNotOperator 函数。

Public Overloads Shared Operator And(ByVal a As ContainerSpecification, _
                 ByVal b As ContainerSpecification) As ContainerSpecification
  Return CType(Specification(Of Container).AndOperator(a, b), ContainerSpecification)
End Operator
 
Public Overloads Shared Operator Or(ByVal a As ContainerSpecification, _
                 ByVal b As ContainerSpecification) As ContainerSpecification
  Return CType(Specification(Of Container).OrOperator(a, b), ContainerSpecification)
End Operator
 
Public Overloads Shared Operator Not(ByVal a As ContainerSpecification) _
                 As ContainerSpecification
  Return CType(Specification(Of Container).NotOperator(a), ContainerSpecification)
End Operator

这就是创建规格所需要做的全部内容。大部分繁重的工作现在都由基规格类完成。我们不再需要担心 AND、OR 和 NOT 运算符是如何实现的,或者复杂的规格树是如何表示的。

随附的示例项目包含单元测试,演示了如何使用我们生成的规格。以下是一些示例:

Dim drum As New Drum("Acid", 500, New ContainerSpecification(ContainerFeature.Armored))
Dim container As New Container(10000, ContainerFeature.Armored)
Assert.IsTrue(drum.RequiredContainer.IsSatisfiedBy(container))

我们声明一个装有 500 单位的酸性物质的桶,并且需要一个装甲容器。然后我们声明一个装甲容器。

该桶有一个 RequiredContainer 属性,它是一个规格;其 IsSatisfiedBy 方法会检查我们的容器,并表明它适合该桶。

下一个示例展示了如何通过组合现有规格来定义复合规格。我们从装甲规格和通风规格开始。AND 和 OR 运算符允许我们定义代表 **任一** 装甲 **或** 通风,或者 **同时** 装甲 **和** 通风的新规格。

' Create Specifications
Dim armoredSpec As New ContainerSpecification(ContainerFeature.Armored)
Dim ventilatedSpec As New ContainerSpecification(ContainerFeature.Ventilated)
Dim either As ContainerSpecification = armoredSpec Or ventilatedSpec
Dim both As ContainerSpecification = armoredSpec And ventilatedSpec

过去,eitherboth 会是新的独立类。在这里,它们只是组合了现有类行为的变量。我们可以像使用任何规格一样使用它们。

例如,我们可以将复合规格传递给 Drum 的构造函数。

' Create Drums, using Specifications to define acceptable containers
Dim uraniumDrum As New Drum("Uranium", 5000, either)
Dim tntDrum As New Drum("TNT", 5000, both)

在这四部分系列文章中,我们了解了何时以及为何要使用规格。我们实现了一个简单的规格,并创建了一个可重用的通用基规格类。

伴随本系列的示例项目应为您提供有关此有用设计模式工作原理的更多见解。

© . All rights reserved.