Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

A Generic Specification in VB.NET

5.00/5 (1 vote)
5 Apr 2009CPOL2 min read 18.9K  
Part three of a four part series of articles on the Specification Design Pattern.

Introduction

This is the third in a series of four articles discussing the Specification Design Pattern and how to implement it using VB.NET.

Background

The Specification Design Pattern was created by Eric Evans. He provides a description of the pattern in his book, 'Domain Driven Design'. This series of articles shows how to implement the pattern using VB.NET.

A Generic Specification

The IsSatisfiedBy function only needs to be written once in the base Specification class. The much simpler SpecificationRule which is coded in our concrete specifications only has to concern itself with the very specific task of examining the candidate object that is passed to it.

The logic for combining specifications using Operators is implemented in three functions in the base Specification. The concrete specifications will call these methods from overloaded operator functions. This will be seen in part four of this series.

VB
Protected Shared Function AndOperator(ByVal a As Specification(Of T), _
                 ByVal b As Specification(Of T)) As Specification(Of T)
  Dim newSpec As Specification(Of T) = a.Copy
  newSpec.SetAnd(b.Copy)
  Return newSpec
End Function
 
Protected Shared Function OrOperator(ByVal a As Specification(Of T), _
                     ByVal b As Specification(Of T)) As Specification(Of T)
  Dim newSpec As Specification(Of T) = a.Copy
  newSpec.SetOr(b.Copy)
  Return newSpec
End Function
 
Protected Shared Function NotOperator(ByVal a As Specification(Of T)) _
                                      As Specification(Of T)
  Dim newSpec As Specification(Of T) = a.Copy
  newSpec.SetNot(Not a.IsNegated)
  Return newSpec
End Function

These operator functions rely on four private functions: SetAnd, SetOr, SetNot, and Copy. These functions implement the real logic for creating complex combinations of specifications.

In simple terms, each specification has two branches from itself, each capable of pointing to another specification. When the specification is evaluated in the IsSatisfiedBy function seen above, that function takes care of evaluating any specifications it points to. This continues recursively until all specifications are evaluated.

The SetAnd and SetOr methods are passed a specification that is to be joined to an existing specification. The SetAnd method attaches it to the AND branch, and the SetOr method attaches it to the OR branch. Obviously, the specification being added could itself be the root of a tree containing multiple specifications.

VB
Private Sub SetAnd(ByVal spec As Specification(Of T))
  ' If setting to nothing, set it directly
  If spec Is Nothing Then
    AndSpec = spec
    Exit Sub
  End If
 
  ' If setting to an actual spec, then attach to the end of any existing spec
  If AndSpec Is Nothing Then
    ' No existing spec, so set directly
    AndSpec = spec
  Else
    ' Recursively call SetAnd until we get to the end of the chain of specs
    AndSpec.SetAnd(spec)
  End If
End Sub
 
Private Sub SetOr(ByVal spec As Specification(Of T))
  ' If setting to nothing, set it directly
  If spec Is Nothing Then
    OrSpec = spec
    Exit Sub
  End If
 
' If setting to an actual spec, then attach
' to the end of the existing chain of specs
  If OrSpec Is Nothing Then
    ' No existing spec, so set directly
    OrSpec = spec
  Else
    ' Recursively call SetOr until we get to the end of the chain of specs
    OrSpec.SetOr(spec)
  End If
End Sub

The NOT operator is a simple boolean flag. If it is set, then the result of the specification is negated.

VB
Private Sub SetNot(ByVal value As Boolean)
    IsNegated = value
End Sub

We also need a copy function that can copy a specification or a tree of specifications. This is used by the AndOperator, OrOperator, and NotOperator functions.

VB
Private Function Copy() As Specification(Of T)
  ' Copy this spec
  Dim newSpec As Specification(Of T)
  newSpec = CType(Me.MemberwiseClone, Specification(Of T))
 
  ' Clear the andSpec and orSpec so they can be pointed
  ' at copies of the existing andSpec and orSpec
  ' After the MemberwiseClone the newSpec points to the same subSpecs
  newSpec.SetAnd(Nothing)
  newSpec.SetOr(Nothing)
 
  If Not AndSpec Is Nothing Then
    newSpec.SetAnd(AndSpec.Copy)
  End If
 
  If Not OrSpec Is Nothing Then
    newSpec.SetOr(OrSpec.Copy)
  End If
 
  Return newSpec
End Function

In the concluding part of this series, we'll build a new version of the ContainerSpecification class which inherits from the Specification class. We'll then declare a number of ContainerSpecifications and combine them.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)