由变量分隔符分隔的对象链的类设计/模式



我正在VB.NET中创建一个生成SQL字符串的库。然而,生成WHERE子句(以及类似的结构)给我带来了一个问题。我已经确定,最简单的where子句结构本质上是

expression1 [AND expression2 [AND expressionN]]

这很容易在代码中用Expression对象列表来表示——然后程序只需要循环遍历每个对象,调用其overriden ToString()方法,每次都附加AND(最后一个除外)。易于理解的当考虑OR时,复杂性就来了:where子句的结构变成了

expression1 [[AND|OR] expression2 [[AND|OR] expressionN]]

我现在需要既能保存所有的表达式,又能跟踪每个表达式是由AND还是OR分隔的——(expr1, andOr1, expr2, andOr2, ... exprN, andOrN) 的集合

我还考虑过该结构也可以嵌套,例如:

(expression1 [[AND|OR] expression2]) [AND|OR] (expression3 [[AND|OR] expression4])

其中,每一个圆括号中的一半本身就是一个表达式。因此,我认为解决方案是通过进一步扩展Expression类,使表达式可以是表达式链:我有一个新的类ExpressionFromChainOfExpressions:

Public Class ExpressionFromChainOfExpressions
    Inherits Expression
    Private ExprChain As List(Of Expression)
    Public Sub AddExpression(Expr As Expression)
         If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
         ExprChain.Add(Expression)
    End Sub
    Public Overrides Function ToString() As String
        Dim outStr As String = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= Expr.ToString()
            If i < ExprChain.Count - 1
                'ToDo: Determine [AND|OR] and append
                'OLD: outStr &= " AND "
            End If
        Next
        Return outStr & ")"
    End Function
End Class

And/Or作为布尔标志或类似的东西绑定到Expression类中感觉是错误的-Expression应该在其他地方可重用(它们也会出现在其他情况下,如case when expression [[and|or] expression] ...,而And/Or本身不是表达式的一部分。

解决方案是一个3行集合技巧还是一个全新的类家族并不重要。可维护性和可扩展性很重要-它不能是黑客。如果有人知道能解决这类问题的设计模式,我会很有兴趣看看


编辑:根据要求,一些仅使用And的示例代码(由于实际移动部件的数量,从实际代码大大简化):

Public MustInherit Class Expression
    Public MustOverride Function ToString() As String
End Class

Public Class ExpressionFromString
    Inherits Expression
    Private ExprString As String
    Public Sub New(ExprString As String)
        Me.ExprString = ExprString
    End Sub
    Public Overrides Function ToString() As String
        Return ExprString
    End Function
End Class

Public Class ExpressionFromChainOfExpressions
    Inherits Expression
    Private ExprChain As List(Of Expression)
    Public Sub AddExpression(Expr As Expression)
         If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
         ExprChain.Add(Expression)
    End Sub
    Public Overrides Function ToString() As String
        Dim outStr As String = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= Expr.ToString()
            If i < ExprChain.Count - 1
                outStr &= " AND "
            End If
        Next
        Return outStr & ")"
    End Function
End Class

主要方法:

Public Module Module1
    Dim myExpr1 = New ExpressionFromString("A = B")
    Dim myExpr2 = New ExpressionFromString("C > 100")
    dIM myExpr3 = New ExpressionFromString("D Is Null")
    Dim myNestedChain = New ExpressionFromChainOfExpressions()
    myExprChain.Add(myExpr1)
    myExprChain.Add(myExpr2)
    Console.WriteLine("WHERE " & myNestedChain.ToString() )
    Dim myOuterChain = New ExpressionFromChainOfExpressions()
    myOuterChain.Add(myNestedChain)
    myOuterChain.Add(myExpr3)
    Console.WriteLine("WHERE " & myOuterChain.ToString() )
End Module

输出:

WHERE (A = B AND C > 100)
WHERE ((A = B AND C > 100) AND D Is Null)

下面是如何面对问题的建议:

Public Class ExpressionFromChainOfExpressions
    Inherits Expression
    Private ExprChain As List(Of Expression)
    Private ExprConn As List(Of Connector)
    Public Enum Connector
        AndConn
        OrConn
    End Enum
    Public Sub AddExpression(Expr As Expression) 'Might have to be changed to account for connectors
        If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
        ExprChain.Add(Expr)
    End Sub
    Public Overrides Function ToString() As String
        Dim outStr As String = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= ExprChain(i).ToString()
            If i < ExprChain.Count - 1 Then
                outStr &= 'will account for the given connector (as stored in ExprConn)
            End If
        Next
        Return outStr & ")"
    End Function
End Class

等同于您定义输入Expressions,定义输入Connectors(一个Public Enum,包括您想要的所有选项(暂时为"And"one_answers"Or");请记住,"And"one_answers"Or"不能使用,因此必须想出等效的名称)。通过遵循与当前版本的Class类似的想法,您可能会包含一个AddConnector函数,尽管这会使一切变得过于混乱(每次两次调用,难以将表达式与连接器关联起来,等等),两个参数:表达式和连接器(指以前输入的表达式或下一个表达式);通过这种方式,您将能够并行地填充两个列表(ExprChainExprConn),并使它们完全协调,以便在ToString()中使用。

我自己对varocarbas解决方案的变体,允许通过IsNested布尔值更好地控制嵌套,并且不依赖于两个集合的排序:

Public Class ExpressionFromChainOfExpressions
    Inherits Expression
    Private ExprChain As List(Of Expression)
    'Alternatively use an Enum as per varocarbas's solution 
    'for better consistency control
    Private ChainWord As String
    'If true, wraps ( ) around ToString()'s output
    Private IsNested As Boolean
    Public Sub New(ByVal ChainWord As String, ByVal IsNested As Boolean)
        Me.ChainWord = ChainWord
        Me.IsNested = IsNested
    End Sub
    Public Sub AddExpression(ByVal Expr As Expression)
        If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
        ExprChain.Add(Expr)
    End Sub
    Public Overrides Function ToString() As String
        Dim outStr As String = ""
        If IsNested Then outStr = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= ExprChain(i).ToString()
            If i < ExprChain.Count - 1 Then
                outStr &= String.Format(" {0} ", ChainWord)
            End If
        Next
        If IsNested Then outStr = ")"
        Return outStr
    End Function
End Class

允许复杂的输出,如

expr1 AND expr2 OR (exp3 AND (expr4 OR expr5 AND expr6 AND expr7))

最新更新