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

时间:2013-08-23 08:20:29

标签: vb.net

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

expression1 [AND expression2 [AND expressionN]]

在代码中使用Expression对象列表很容易表示 - 程序只需遍历每个对象,调用其覆盖ToString()方法并每次追加AND(除了最后一个)。简单。当考虑OR时出现并发症:where子句的结构变为

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

我现在需要能够同时保存所有表达式,但也要跟踪ANDOR是否与下一个表达式分开 - 基本上是(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] ...应该可以在其他地方重复使用(它们也会出现在其他情况如AndOr / And本身不是表达式的一部分。

无论解决方案是3行收集技巧还是全新的类系列都无关紧要。可维护性和可扩展性很重要 - 它一定不是黑客。如果有人知道解决这类问题的设计模式,我会非常有兴趣看到它。


编辑:根据要求,一些示例代码只使用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)

输出:

{{1}}

2 个答案:

答案 0 :(得分:1)

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

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,定义输入ConnectorsPublic Enum包括您想要的所有替代方案(暂时为“And”和“Or”);熊请记住,“And”和“Or”不能使用,因此必须提出相同的名称)。通过遵循当前版本的Class之类的想法,您可能会包含AddConnector函数,但这会使一切变得太乱(每次两次调用,将表达式与连接器关联起来很困难等等) )。我建议你改变AddExpression,使它至少接受两个参数:Expression和Connector(参考先前输入的Expression或下一个);通过这种方式,您可以并行填充两个列表(ExprChainExprConn),并使它们完美协调,以便在ToString()中使用。

答案 1 :(得分:0)

我自己对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))