Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何重构类?(VB.Net)_Vb.net_.net 2.0_Refactoring - Fatal编程技术网

如何重构类?(VB.Net)

如何重构类?(VB.Net),vb.net,.net-2.0,refactoring,Vb.net,.net 2.0,Refactoring,我最近启动了一个项目,需要通过DirectoryServices与LDAP进行一些集成。我在其他应用程序中也这么做过,所以我进入其中一个应用程序,看看我是如何做到的——为什么要重新发明轮子呢?嗯,虽然这个轮子可以工作,但它是几年前开发的,有点异味(它是木制的,牢固地固定在以前的车辆上,很难修理,并且会产生潜在的颠簸) 所以我对自己说,现在是重构这只小狗的最佳时机,让它变得更便携、可重用、可靠、更易于配置等等。现在一切都很好,但我开始觉得从哪里开始有点不知所措。它应该是一个独立的图书馆吗?它应该如

我最近启动了一个项目,需要通过DirectoryServices与LDAP进行一些集成。我在其他应用程序中也这么做过,所以我进入其中一个应用程序,看看我是如何做到的——为什么要重新发明轮子呢?嗯,虽然这个轮子可以工作,但它是几年前开发的,有点异味(它是木制的,牢固地固定在以前的车辆上,很难修理,并且会产生潜在的颠簸)

所以我对自己说,现在是重构这只小狗的最佳时机,让它变得更便携、可重用、可靠、更易于配置等等。现在一切都很好,但我开始觉得从哪里开始有点不知所措。它应该是一个独立的图书馆吗?它应该如何配置?它应该使用国际奥委会吗?迪

因此,我(承认是主观的)问题是——给定一个相对较小但非常有用的类,比如下面的类,什么是重构它的好方法?您会问什么问题?您如何决定实施或不实施什么?关于配置灵活性,您的底线在哪里

[注意:请不要过分抨击此代码,好吗?它是很久以前编写的,在内部应用程序中运行良好。]

Public Class AccessControl

Public Shared Function AuthenticateUser(ByVal id As String, ByVal password As String) As Boolean
    Dim path As String = GetUserPath(id)
    If path IsNot Nothing Then
        Dim username As String = path.Split("/")(3)
        Dim userRoot As DirectoryEntry = New DirectoryEntry(path, username, password, AuthenticationTypes.None)
        Try
            userRoot.RefreshCache()
            Return True
        Catch ex As Exception
            Return False
        End Try
    Else
        Return False
    End If
End Function

Private Shared Function GetUserPath(ByVal id As String) As String
    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
    Dim searcher As New DirectorySearcher
    Dim results As SearchResultCollection
    Dim result As SearchResult
    Try
        With searcher
            .SearchRoot = root
            .PropertiesToLoad.Add("cn")
            .Filter = String.Format("cn={0}", id)
            results = .FindAll()
        End With
        If results.Count > 0 Then
            result = results(0)
            Return result.Path.ToString()
        Else
            Return Nothing
        End If
    Catch ex As Exception
        Return Nothing
    End Try
End Function

Public Shared Function GetUserInfo(ByVal id As String) As UserInfo
    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
    Dim searcher As New DirectorySearcher
    Dim results As SearchResultCollection
    Dim props() As String = {"id", "sn", "mail", "givenname", "container", "cn"}
    Try
        With searcher
            .SearchRoot = root
            .PropertiesToLoad.AddRange(props)
            .Filter = String.Format("cn={0}", id)
            results = .FindAll()
        End With
        If results.Count > 0 Then
            Dim properties As PropertyCollection = results(0).GetDirectoryEntry().Properties
            Dim user As New UserInfo(properties("id").Value)
            user.EmailAddress = properties("mail").Item(0).ToString
            user.FirstName = properties("givenname").Item(0).ToString
            user.LastName = properties("sn").Item(0).ToString
            user.OfficeLocation = properties("container").Item(0).ToString
            Return user
        Else
            Return New UserInfo
        End If
    Catch ex As Exception
        Return Nothing
    End Try
End Function

Public Shared Function IsMemberOfGroup(ByVal id As String, ByVal group As String) As Boolean
    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
    Dim searcher As New DirectorySearcher
    Dim results As SearchResultCollection
    Dim result As SearchResult
    Dim props() As String = {"cn", "member"}
    Try
        With searcher
            .SearchRoot = root
            .PropertiesToLoad.AddRange(props)
            .Filter = String.Format("cn={0}", group)
            results = .FindAll()
        End With
        If results.Count > 0 Then
            For Each result In results
                Dim members As PropertyValueCollection = result.GetDirectoryEntry().Properties("member")
                Dim member As String
                For i As Integer = 0 To members.Count - 1
                    member = members.Item(i).ToString
                    member = member.Substring(3, member.IndexOf(",") - 3).ToLowerInvariant
                    If member.Contains(id.ToLowerInvariant) Then Return True
                Next
            Next
        End If
        Return False
    Catch ex As Exception
        Return False
    End Try
End Function

Public Shared Function GetMembersOfGroup(ByVal group As String) As List(Of String)
    Dim groupMembers As New List(Of String)
    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
    Dim searcher As New DirectorySearcher
    Dim results As SearchResultCollection
    Dim result As SearchResult
    Dim props() As String = {"cn", "member"}
    Try
        With searcher
            .SearchRoot = root
            .PropertiesToLoad.AddRange(props)
            .Filter = String.Format("cn={0}", group)
            results = .FindAll()
        End With
        If results.Count > 0 Then
            For Each result In results
                Dim members As PropertyValueCollection = result.GetDirectoryEntry().Properties("member")
                Dim member As String
                For i As Integer = 0 To members.Count - 1
                    member = members.Item(i).ToString
                    member = member.Substring(3, member.IndexOf(",") - 3).ToLowerInvariant
                    groupMembers.Add(member)
                Next
            Next
        End If
    Catch ex As Exception
        Return Nothing
    End Try
    Return groupMembers
End Function

End Class  
澄清:
-用户有一个单独的类(简单poco)

-没有组类,因为现在使用的只是ID列表,添加这些ID可能很有用。不过,我马上想到的第一件事是,有很多函数涉及的用户似乎与您创建的用户类没有关联

我会尝试以下几种方法:

  • 将以下内容添加到用户类:
    • 路径
    • 身份验证(字符串密码)-这可能是一个静态方法,不确定这里的用例
    • 组-我还建议为组创建一个实际的域对象。它可以有一个用户集合作为开始的属性

    • 我要做的第一件事是删除任何重复项。将中的常见功能剥离到单独的方法中

      另外,考虑每个类都应该有一个单独的角色/职责-您可能希望创建单独的用户和组类

      这里有一个常见重构的目录:


      <>你只应该考虑控制反转,如果你希望在不同的类中交换测试的原因等等…

      它可能不是最重要的重构,但我是早期回归的大粉丝。例如,你有:

      If results.Count > 0 Then
          Dim properties As PropertyCollection = results(0).GetDirectoryEntry().Properties
          Dim user As New UserInfo(properties("id").Value)
          user.EmailAddress = properties("mail").Item(0).ToString
          user.FirstName = properties("givenname").Item(0).ToString
          user.LastName = properties("sn").Item(0).ToString
          user.OfficeLocation = properties("container").Item(0).ToString
          Return user
      Else
          Return New UserInfo
      End If
      
      相反,我会使用:

      If results.Count == 0 Then Return New UserInfo
      
      Dim properties As PropertyCollection = results(0).GetDirectoryEntry().Properties
      Dim user As New UserInfo(properties("id").Value)
      user.EmailAddress = properties("mail").Item(0).ToString
      user.FirstName = properties("givenname").Item(0).ToString
      user.LastName = properties("sn").Item(0).ToString
      user.OfficeLocation = properties("container").Item(0).ToString
      Return user
      

      缩进意味着复杂性,并且在空结果案例的特殊处理中没有足够的复杂性来保证8行缩进。在某一点上,删除缩进实际上可以隐藏真正的复杂性,因此不要过分强调这一规则,但对于提供的代码,我肯定会使用提前返回。

      我认为修改异常处理也很重要。我在上述方法中看到的模式是:

      Try
          ...   
      Catch ex As Exception
          Return False
      End Try
      
      上面的代码本质上隐藏(吞咽)了它的异常。这可能是最初实现的,因为某些类型的异常是由于找不到用户而引发的,返回False或Nothing可能是合适的。但是,您可能会在应用程序中遇到其他类型的异常,您可能永远都不知道这些异常(例如OutOfMemoryException等)

      我建议只捕获您可能希望合法返回false/Nothing的特定类型的异常。对于其他人,让异常冒泡,或者至少记录它


      有关异常处理的其他提示,请阅读。

      以下是重构代码示例:

      Public Class AccessControl
      
          Public Shared Function AuthenticateUser(ByVal id As String, ByVal password As String) As Boolean
              Dim path As String
              Dim username As String
              Dim userRoot As DirectoryEntry
      
              path = GetUserPath(id)
      
              If path.Length = 0 Then
                  Return False
              End If
      
              username = path.Split("/")(3)
              userRoot = New DirectoryEntry(path, username, password, AuthenticationTypes.None)
      
              Try
                  userRoot.RefreshCache()
                  Return True
              Catch ex As Exception
                  ' Catching Exception might be accepted way of determining user is not authenticated for this case
                  ' TODO: Would be better to test for specific exception type to ensure this is the reason
                  Return False
              End Try
          End Function
      
          Private Shared Function GetUserPath(ByVal id As String) As String
              Dim results As SearchResultCollection
              Dim propertiesToLoad As String()
      
              propertiesToLoad = New String() {"cn"}
              results = GetSearchResultsForCommonName(id, propertiesToLoad)
      
              If results.Count = 0 Then
                  Return String.Empty
              Else
                  Debug.Assert(results.Count = 1)
                  Return results(0).Path
              End If
          End Function
      
          Public Shared Function GetUserInfo(ByVal id As String) As UserInfo
              Dim results As SearchResultCollection
              Dim propertiesToLoad As String()
      
              propertiesToLoad = New String() {"id", "sn", "mail", "givenname", "container", "cn"}
              results = GetSearchResultsForCommonName(id, propertiesToLoad)
      
              If results.Count = 0 Then
                  Return New UserInfo
              End If
      
              Debug.Assert(results.Count = 1)
              Return CreateUser(results(0).GetDirectoryEntry().Properties)
          End Function
      
          Public Shared Function IsMemberOfGroup(ByVal id As String, ByVal group As String) As Boolean
              Dim allMembersOfGroup As List(Of String)
              allMembersOfGroup = GetMembersOfGroup(group)
              Return allMembersOfGroup.Contains(id.ToLowerInvariant)
          End Function
      
          Public Shared Function GetMembersOfGroup(ByVal group As String) As List(Of String)
              Dim results As SearchResultCollection
              Dim propertiesToLoad As String()
      
              propertiesToLoad = New String() {"cn", "member"}
              results = GetSearchResultsForCommonName(group, propertiesToLoad)
      
              Return ConvertMemberPropertiesToList(results)
          End Function
      
          Private Shared Function GetStringValueForPropertyName(ByVal properties As PropertyCollection, ByVal propertyName As String) As String
              Return properties(propertyName).Item(0).ToString
          End Function
      
          Private Shared Function CreateUser(ByVal userProperties As PropertyCollection) As UserInfo
              Dim user As New UserInfo(userProperties("id").Value)
              With user
                  .EmailAddress = GetStringValueForPropertyName(userProperties, "mail")
                  .FirstName = GetStringValueForPropertyName(userProperties, "givenname")
                  .LastName = GetStringValueForPropertyName(userProperties, "sn")
                  .OfficeLocation = GetStringValueForPropertyName(userProperties, "container")
              End With
              Return user
          End Function
      
          Private Shared Function GetValueFromMemberProperty(ByVal member As String) As String
              Return member.Substring(3, member.IndexOf(",") - 3).ToLowerInvariant
          End Function
      
          Private Shared Function ConvertMemberPropertiesToList(ByVal results As SearchResultCollection) As List(Of String)
              Dim result As SearchResult
              Dim memberProperties As PropertyValueCollection
              Dim groupMembers As List(Of String)
      
              groupMembers = New List(Of String)
              For Each result In results
                  memberProperties = result.GetDirectoryEntry().Properties("member")
                  For i As Integer = 0 To memberProperties.Count - 1
                      groupMembers.Add(GetValueFromMemberProperty(memberProperties.Item(i).ToString))
                  Next
              Next
              Return groupMembers
          End Function
      
          Private Shared Function GetSearchResultsForCommonName(ByVal commonName As String, ByVal propertiesToLoad() As String) As SearchResultCollection
              Dim results As SearchResultCollection
              Dim searcher As New DirectorySearcher
              With searcher
                  .SearchRoot = GetDefaultSearchRoot()
                  .PropertiesToLoad.AddRange(propertiesToLoad)
                  .Filter = String.Format("cn={0}", commonName)
                  results = .FindAll()
              End With
              Return results
          End Function
      
          Private Shared Function GetDefaultSearchRoot() As DirectoryEntry
              Return New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
          End Function
      
      End Class
      
      我想你可以通过提取常数等进一步了解这一点,但这会消除大部分重复,等等。让我知道你的想法


      太晚了,我意识到。。。但同时也想回答你提出的一些问题。请看

      在开始破解之前,您可能已经准备好了一组全面的测试?我喜欢Chris在重构方面所做的工作。我可能提出的一个建议是在对Active directory的特定调用周围添加一个适配器。这将使整个类都可以测试,考虑到基础设施方面的代码比较繁重,这可能有点过分,但我已经围绕套接字类等编写了类似的适配器,以简化它们的api。