Visual Basic - Login/登入/登錄之物件導向實作

Login VB-OOP 實作前言

Login (登入/登錄) 動作,不管在任何應用系統 (AP, Application System)都能看到它的影子。像我的手機,一開機先問個 PIN 密碼...光一個手機,就能有好多密碼。
  1. 進公司刷卡(Card);
  2. 提錢(Card);
  3. 上網(Accout/Password);
  4. 回家開門(Key);
  5. 開車(Key);
  6. 指紋(生物辨識);

你能發現每天從早到晚,其實都在不斷的 Login / Logout,出門上班,對「家」而言,你是 Logout,開車上班,對「車」而言,你是 Login,到達公司,對「車」而言,你是 Logout,進公司,必須刷卡才能進入,那是 Login,下班刷卡離開公司,那是 Logout。

指紋辨識,這幾年拜 NB 幫的忙,很多 NB 都把指紋辨識當成基本配備,省下了打 Keyboard 的時間,刷一下手指,超方便的。還記得,大學時在「國家太空中心」打工,國家太空中心可以使用「掌紋辨識」,在當時可以說是很先進方便,辨識率也不錯,就我在那裡打工時間裡,最少沒有出錯過。

以下是以程杰(大話設計模式作者) Blog 裡兩篇文章為基礎,以這兩篇文章實作出 Visual Basic 可執行版本。

  1. 凭什么要用面向对象编程——面向对象重要设计原则概述
  2. 凭什么要用面向对象编程(补充)
非常推薦給學習過物件導向的人來看,Login 範例非常實用,只要是「AP」你一定會碰到 Login 問題,學習如何透過物件導向來處理此一問題。

我覺得另一重點是「思緒」,想想,如果是你,你會怎麼處理?是否會有一樣的思緒?而且我認為文章中提出的問題,都還算常見,不算是那種為了寫書或寫文章而特別設計的例子。

範例一:網頁登入 (CodeBehind) (對應文章 2007-7-1  對話)



WebForm_1 專案 - Login.aspx

Login.aspx 介面 HTML。

<h2>網頁登錄 ( CodeBehind )</h2>
<form id="form1" runat="server">
<div>
    <asp:Label ID="lbName" runat="server" Text="帳號:"></asp:Label>
    <asp:TextBox ID="tbName" runat="server"></asp:TextBox>
    <br />
    <asp:Label ID="lbPassword" runat="server" Text="密碼:"></asp:Label>
    <asp:TextBox ID="tbPassword" runat="server" TextMode="Password"></asp:TextBox>
    <br />
    <br />
    <asp:Button ID="btnLogin" runat="server" Text="登錄" />
</div>
</form>

Login.aspx.vb CodeBehind 程式碼。

Public Class Login
    Inherits System.Web.UI.Page

    Protected Sub tbLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        Dim userName As String = tbName.Text
        Dim passWord As String = tbPassword.Text

        If (login(userName, passWord)) Then
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub

    Private Function login(userName As String, password As String) As Boolean
        ' 訪問資料庫,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function

End Class

此範例為最最最基礎的 Login 程式碼,也是最最最一般的 Login 程式碼。注意,為了 Focus 在「Login」這件事情上面,我們的範例不會出現任何資料庫相關程式碼,要注意的地方,我都有打上註解。

另外準備兩個網頁, LoginOK.aspx及LoginFail.aspx,內容很簡易,一個打上「網頁登錄成功!」,一個打上「網頁登錄失敗!」,執行我們的第一個專案,這已經是一個能動的範例了。

範例二:手機登錄 ( Class UserAdmin ) (對應文章 2007-7-25 對話)


我個開一個新 WebForm 專案,假設它是手機登錄介面。

WebForm_2 專案 - Login.aspx


PhoneLogin.aspx 介面 HTML 同 WebForm_1 專案。

PhoneLogin.aspx.vb

Public Class Login
    Inherits System.Web.UI.Page

    Protected Sub tbLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        Dim userName As String = tbName.Text
        Dim password As String = tbPassword.Text

        ' 使用 Class UserAdmin 提供的認證功能。
        Dim ua As New UserAdmin
        If (ua.Login(userName, password)) Then
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub
End Class

''' <summary>
''' 用戶管理類別
''' </summary>
Public Class UserAdmin

    ''' <summary>
    ''' 登錄資料確認函式
    ''' </summary>
    ''' <param name="userName">帳號</param>
    ''' <param name="password">密碼</param>
    ''' <returns>Boolean</returns>
    Public Function Login(userName As String, password As String) As Boolean
        ' 訪問資料庫,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class

我們把原本的 Login 函式提升至一個 UserAdmin 類別裡,這樣就可以給大家共用。



範例三:WinForm 登錄 ( DataAccessLayer ) (對應文章 2007-7-29 對話)


DataAccessLayer (Windows --> 類別庫) 專案


將範例二的 Class UserAdmin 移至 DataAccessLayer 類別庫專案。

UserAdmin.vb

''' <summary>
''' 用戶管理類別
''' </summary>
Public Class UserAdmin

    ''' <summary>
    ''' 登錄資料確認函式
    ''' </summary>
    ''' <param name="userName">帳號</param>
    ''' <param name="password">密碼</param>
    ''' <returns>Boolean</returns>
    Public Function Login(userName As String, password As String) As Boolean
        ' 訪問資料庫,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class


移至類別庫後,我們必須在要使用專案裡「加入參考」--> 選擇「Projects」 --> 選擇「DataAccessLayer」,然後在程式即可使用。

WinForm_1 專案

我們先把 WinForm Login UI拉好,

圖一:WinForm Login UI
然後撰寫Button的程式碼:

Public Class Form1

    Private Sub bnLogin_Click(sender As System.Object, e As System.EventArgs) Handles bnLogin.Click
        Dim userName As String = tbName.Text
        Dim password As String = tbPassword.Text

        ' 使用 DataAccessLayer.UserAdmin 提供的認證功能。
        Dim ua As New DataAccessLayer.UserAdmin
        If ua.Login(userName, password) Then
            lbMsg.Text = "WinForm 登錄成功!"
        Else
            lbMsg.Text = "WinForm 登錄失敗!"
        End If
    End Sub
End Class

到這裡還算簡單。也讓我們學習到「類別庫」的使用,如果你還不會使用類別庫專案,請參考我的這一篇「元件類別(類別庫範本,Class Library) 」。

範例四:網頁登錄 (DataAccessLayer_DB)(對應文章 2007-8-3 對話)

此處會使用到「介面、繼承、多型」等特性。如果不懂,請參考「Visual Basic 裡 OOP 相關文章」。

DataAccessLayer_DB 類別庫專案

我們重新整理整個 DataAccessLayer 為 DataAccessLayer_DB,使它可以實現切換資料庫時,不用去動到原始程式碼。

IUserAdmin 介面,定義所有繼承類別必須要有一個「Login 函式」。

IUserAdmin.vb 介面


''' <summary>
''' 定義 Login 函式
''' </summary>
Public Interface IUserAdmin
    Function Login(userName As String, password As String) As Boolean 
End Interface

接下來我們繼承 IUserAdmin 介面,實作 Access, Oracle, Sql Server 三個類別。

UserAdminAccess.vb


Public Class UserAdminAccess
    Implements IUserAdmin

    ''' <summary>
    ''' 實作 Access 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="userName">帳號</param>
    ''' <param name="password">密碼</param>
    ''' <returns>Boolean</returns>
    Public Function Login(userName As String, password As String) As Boolean Implements IUserAdmin.Login
        ' 訪問 Access,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class

UserAdminOracle.vb


Public Class UserAdminOracle
    Implements IUserAdmin

    ''' <summary>
    ''' 實作 Oracle 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="userName">帳號</param>
    ''' <param name="password">密碼</param>
    ''' <returns>Boolean</returns>
    Public Function Login(userName As String, password As String) As Boolean Implements IUserAdmin.Login
        ' 訪問 Oracle,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class

UserAdminSqlServer.vb


Public Class UserAdminSqlServer
    Implements IUserAdmin 

    ''' <summary>
    ''' 實作 SqlServer 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="userName">帳號</param>
    ''' <param name="password">密碼</param>
    ''' <returns>Boolean</returns>
    Public Function Login(userName As String, password As String) As Boolean Implements IUserAdmin.Login
        ' 訪問 Sql Server ,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class

這裡我們偷懶了,原因前面說過了,為了 Focus 在「Login」這件事情上面。雖然看起來都是一模一樣的程式碼,但在原應有的實作中,資料庫連線,SQL語法 … 都會有差異,尤其是在其他非MS系列資料庫,並不是都一樣,這裡我們再提醒一次。

Login.aspx.vb

Login UI HTML與範例一相同。我們直接程式碼。

Public Class Login
    Inherits System.Web.UI.Page

    Private Sub btnLogin_Click(sender As Object, e As System.EventArgs) Handles btnLogin.Click
        Dim userName As String = tbName.Text
        Dim password As String = tbPassword.Text

        ' 討論一
        ' 透過 DataAccessLayer_DB 可選擇對應的資料庫
        Dim ua As New DataAccessLayer_DB.UserAdminAccess
        'Dim ua As New DataAccessLayer_DB.UserAdminOracle 
        'Dim ua As New DataAccessLayer_DB.UserAdminSqlServer 

        If ua.Login(userName, password) Then
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub
End Class

以上寫法只差一個問題,如果我們要更換資料庫的話,必須動到此主程式。

範例五:UserAdminFactory 工廠模式 (對應文章 2007-8-8 對話)

我們在範例四最後,透過「Dim ua As New DataAccessLayer_DB.UserAdminAccess」的方式來產生實體,一旦我們要更換資料庫,就必須更動主程式,這不是好辦法,比較好的辦法透過工廠模式來新增實體,這樣的話,要修改也不必更動到此主程式,所以我們在類別庫中再新增一個 UserAdminFactory.vb 的工廠類別。

UserAdminFactory.vb 工廠類別

Public Class UserAdminFactory
    Private Shared ReadOnly db As String = "Access"

    ''' <summary>
    ''' 透過工廠來建立 IUserAdmin 實體,以去除程式碼中實體建立過程。
    ''' </summary>
    Public Shared Function CreateUserAdmin() As IUserAdmin
        Dim iua As IUserAdmin = Nothing

        Select Case db
            Case "Access"
                iua = New UserAdminAccess
            Case "Oracle"
                iua = New UserAdminOracle
            Case "SqlServer"
                iua = New UserAdminSqlServer
        End Select

        Return iua
    End Function
End Class

以原來主程式中產生實體改為使用工廠類別來產生。

' 討論二
' --- 使用工廠來產生實體,當我們變更資料庫時,就不必修改此段程式碼 ---
Dim ua As DataAccessLayer_DB.IUserAdmin  = DataAccessLayer_DB.UserAdminFactory.CreateUserAdmin()

範例六:改進工廠,Reflection - Config (對應文章 2007-8-15 對話)

讓工廠依賴 *.config 檔。此處使用到 Reflection,如不清楚,請參考「Visual Basic Reflection 相關文章」。

UserAdminFactory_Reflection.vb

Imports System.Configuration 
Imports System.Reflection 

Public Class UserAdminFactory_Reflection

    Private Shared ReadOnly AssemblyName As String = "DataAccessLayer_DB"
    Private Shared ReadOnly db As String =  ConfigurationManager.AppSettings("DB")

    Public Shared Function CreateUserAdmin() As IUserAdmin 
        ' 組合後的ClassName: DataAccessLayer_DB.UserAdminAccess
        Dim className As String = AssemblyName & ".UserAdmin" & db
        ' 透過 Reflection 產生對應的實體
        Dim iua As IUserAdmin = CType(Assembly.Load(AssemblyName).CreateInstance(className),IUserAdmin)
        Return iua 
    End Function
End Class

然後,在修改 WebForm_3 專案 Web.config 加入 appSettings 相關設定。

修改Web.confg的AppSettings設定

<configuration>
    <appSettings>
        <add key="DB" value="Access"/>
        <!--
        <add key="DB" value="Oracle"/>
        <add key="DB" value="SqlServer"/>
        -->
    </appSettings>
    <system.web>
        <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
    </system.web>
</configuration>

這樣之後,讓我們的工廠來依賴 Web.config 或 App.config,當我們需要更換資料庫時,所以程式碼都不必修改。最後主程式 Loging 使用 Reflection (反映) 版本的工廠。

Login.aspx.vb 使用 Reflection 工廠

Public Class Login
    Inherits System.Web.UI.Page

    Private Sub btnLogin_Click(sender As Object, e As System.EventArgs) Handles btnLogin.Click
        Dim userName As String = tbName.Text
        Dim password As String = tbPassword.Text

        ' 討論一
        ' 透過 DataAccessLayer_DB 可選擇對應的資料庫
        'Dim ua As New DataAccessLayer_DB.UserAdminAccess
        'Dim ua As New DataAccessLayer_DB.UserAdminOracle 
        'Dim ua As New DataAccessLayer_DB.UserAdminSqlServer 

        ' --- 以上也產生更換資料庫,必須更動程式碼的問題 ---
        ' --- 應該修改使用工廠來產生實體 ---

        ' 討論二
        ' --- 使用工廠來產生實體,當我們變更資料庫時,就不必修改此段程式碼 ---
        'Dim ua As DataAccessLayer_DB.IUserAdmin  = DataAccessLayer_DB.UserAdminFactory.CreateUserAdmin()

        ' 討論三
        ' --- 使用 Reflection 來產生實體 ---
        ' --- 注意:Web.config 裡的 appSettings 段落 ---
        ' --- 透過修改 Web.config 設定檔,就能達到不修改程式而更換資料庫 ---
        Dim ua As DataAccessLayer_DB.IUserAdmin = DataAccessLayer_DB.UserAdminFactory_Reflection.CreateUserAdmin()

        If ua.Login(userName, password) Then
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub
End Class

範例七:打卡與指紋登錄 (DataAccessLayer_FingerMark)(對應文章 2007-9-23 對話)

本篇一剛頭我們就討論過,Login 的方法千奇百怪,例如「家」,除了 Key 之外,你還可能有申請安裝保全服務,那除了 Key 之外,連「家」都必須刷卡,你才能安心的 Login / Logout。所以不要覺得這裡的需求很奇怪。

Users.vb

使用者登錄相關屬性。

''' <summary>
''' 使用者登錄相關屬性類別
''' </summary>
Public Class Users
    Public Property UserID() As Integer 
    Public Property UserName() As String 
    Public Property Password() As String 
    Public Property CardID() As String 
    Public Property FingerMark() As String 
End Class

修改 IUserAdmin 介面,使用 Users 類別。

IUserAdmin 介面 - 使用 Users類別

''' <summary>
''' 定義 Loging 函式,使用 Users 類別
''' </summary>
Public Interface IUserAdmin
    Function Login(user As Users) As Boolean 
End Interface

接下來,我們新增三個類別來繼承實作此 IUserAdmin 介面,UserAdminAccess.vb、UserAdminOracle.vb、UserAdminSqlServer.vb。

UserAdminAccess.vb

Public Class UserAdminAccess
    Implements IUserAdmin

    ''' <summary>
    ''' 實作 Access 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="user">使用者相關屬性</param>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Function Login(user As Users) As Boolean Implements IUserAdmin.Login
        ' 訪問Access,進行帳號及密碼比對
        ' --- 我們假設"指紋"與"CardID"是一個字串比對 ---
        ' --- 只要任一條件通過認證即可 ---
        If NameAndPassword(user) OrElse FingerMark(user) OrElse CardID(user) Then
            Return True
        Else
            Return False
        End If
    End Function

    ' 以下三個依"重構原則"而重構而來的回應條件函式
    ' 正常情況下,return 的內容,必須與各別資料庫做確認,不合適提升至 Users.vb 裡
    Private Shared Function NameAndPassword(ByVal user As Users) As Boolean
        Return user.UserName = "kkbruce" AndAlso user.Password = "12345"
    End Function

    Private Shared Function FingerMark(ByVal user As Users) As Boolean
        Return user.FingerMark = "67890"
    End Function

    Private Shared Function CardID(ByVal user As Users) As Boolean
        Return user.CardID = "8888"
    End Function
End Class

UserAdminAccess.vb

Public Class UserAdminOracle
    Implements IUserAdmin

    ''' <summary>
    ''' 實作 Oracle 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="user">使用者相關屬性</param>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Function Login(user As Users) As Boolean Implements IUserAdmin.Login
        ' 訪問Oracle,進行帳號及密碼比對
        ' --- 我們假設"指紋"與"CardID"是一個字串比對 ---
        ' --- 只要任一條件通過認證即可 ---
        If NameAndPassword(user) OrElse
            FingerMark(user) OrElse
            CardID(user) Then
            Return True
        Else
            Return False
        End If
    End Function

    ' 以下三個依"重構原則"而重構而來的回應條件函式
    ' 正常情況下,return 的內容,必須與各別資料庫做確認,不合適提升至 Users.vb 裡
    Private Shared Function NameAndPassword(ByVal user As Users) As Boolean
        Return user.UserName = "kkbruce" AndAlso user.Password = "12345"
    End Function

    Private Shared Function FingerMark(ByVal user As Users) As Boolean
        Return user.FingerMark = "67890"
    End Function

    Private Shared Function CardID(ByVal user As Users) As Boolean
        Return user.CardID = "8888"
    End Function
End Class

UserAdminAccess.vb

Public Class UserAdminSqlServer
    Implements IUserAdmin

    ''' <summary>
    ''' 實作 Sql Server 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="user">使用者相關屬性</param>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Function Login(user As Users) As Boolean Implements IUserAdmin.Login
        ' 訪問 Sql Server,進行帳號及密碼比對
        ' --- 我們假設"指紋"與"CardID"是一個字串比對 ---
        ' --- 只要任一條件通過認證即可 ---
        If NameAndPassword(user) OrElse FingerMark(user) OrElse CardID(user) Then
            Return True
        Else
            Return False
        End If
    End Function

    ' 以下三個依"重構原則"而重構而來的回應條件函式
    ' 正常情況下,return 的內容,必須與各別資料庫做確認,不合適提升至 Users.vb 裡
    Private Shared Function NameAndPassword(ByVal user As Users) As Boolean
        Return user.UserName = "kkbruce" AndAlso user.Password = "12345"
    End Function

    Private Shared Function FingerMark(ByVal user As Users) As Boolean
        Return user.FingerMark = "67890"
    End Function

    Private Shared Function CardID(ByVal user As Users) As Boolean
        Return user.CardID = "8888"
    End Function
End Class

再提醒一次,這三個類別只是我們方便 Demo,所以程式都長的一樣,但真實實作上會所有不同。

為了不複雜範例,工廠我們使用前面 UserAdminFactory.vb 這個版本的廠工類別。

fingerLogin.aspx 指紋登錄

這裡我們新增一個 Webform_4 專案,假設它是使用指紋登錄,而在程式上,我們又假設指紋是一個字串。

fingerLogin.aspx UI HTML

<h2>指紋登錄 (DataAccessLayer_fingerMark)</h2>
<form id="form1" runat="server">
<div>
    <asp:Label ID="lbFinger" runat="server" Text="指紋:"></asp:Label>
    <asp:TextBox ID="tbFinger" runat="server"></asp:TextBox>
    <br />
    (假設 FingerMark 是 String = "67890")
    <br />
    <asp:Button ID="btnLogin" runat="server" Text="登錄" />
</div>
</form>

先加入類別庫的參考,接下來我們來使用 Users 類別及fingerMark來登入。

Public Class fingerLogin
    Inherits System.Web.UI.Page

    Protected Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        Dim user As New DataAccessLayer_fingerMark.Users
        user.FingerMark = tbFinger.Text 
        Dim ua As DataAccessLayer_fingerMark.IUserAdmin  = DataAccessLayer_fingerMark.UserAdminFactory.CreateUserAdmin()

        If ua.Login(user) then 
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub
End Class

程式碼沒有特別的地方,就是使用 Users 類別,然後由工廠產生實體,最後進行登錄驗證。


接下去為第二篇補充篇內容。

DataAccessLayer_Final and WebForm_5

將驗證分離出來成為一個介面,各自實作驗證方法。最後這個範例,我們特別一些,我們將會把處理後的 SQL 語法顯示出來,來代表是登錄。

ICredentials 介面

''' <summary>
''' 定義驗證函式 ( 產生對應的 SQL 語法)
''' </summary>
Public Interface ICredentials
    Function CredentialUser() As String 
End Interface

以下是"帳號/密碼"、"刷卡"、"指紋"三者驗證方法作。

CredentialsNamePassword.vb 類別(帳號/密碼驗實作)

''' <summary>
''' Name - Password 憑證 ( SQL 語法:Name - Password 條件)
''' </summary>
Public Class CredentialsNamePassword
    Implements ICredentials

    Public Property UserName() As String
    Public Property Password() As String

    Public Function CredentialUser() As String Implements ICredentials.CredentialUser
        ' 注意:以下寫法會有 Sql Injection 的問題。
        Return " UserName='" & Me.UserName & "' and Password = '" & Me.Password & "'"
    End Function
End Class

CredentialsCard.vb 類別 (刷卡驗證方法實作)

''' <summary>
''' CardID 憑證 ( SQL 語法:CardID 條件)
''' </summary>
Public Class CredentialsCard
    Implements ICredentials

    Public Property CardID() As String

    Public Function CredentialUser() As String Implements ICredentials.CredentialUser
        Return " CardID='" & Me.CardID & "'"
    End Function
End Class

CredentialsFingerMark 類別(指紋驗證方法實作)

''' <summary>
''' FingerMark 憑證 ( SQL 語法:FingerMark 條件)
''' </summary>
Public Class CredentialsFingerMark
    Implements ICredentials

    Public Property FingerMark() As String

    Public Function CredentialUser() As String Implements ICredentials.CredentialUser
        Return " FingerMark='" & Me.FingerMark & "'"
    End Function
End Class

這裡主要是要回傳 SQL 語法 Where 的條件式。

IUserAdmin.vb 介面 -- 修改使用 ICredentials 介面

''' <summary>
''' 定義 Loging 函式 (使用 ICredentials 介面)
''' </summary>
Public Interface IUserAdmin
    Function Login(credentials As ICredentials) As String
End Interface

最後是三個繼承 IUserAdmin 介面的實作類別。

UserAdminSqlServer.vb 類別

Public Class UserAdminSqlServer
    Implements IUserAdmin

    ''' <summary>
    ''' 實作 Sql Server 版本的 IUserAdmin
    ''' </summary>
    ''' <param name="credentials">SQL Server憑證</param>
    ''' <returns>String (SQL 語法)</returns>
    Public Function Login(credentials As ICredentials) As String Implements IUserAdmin.Login
        Dim sqltxt As String = "select userid from users where"

        sqltxt &= credentials.CredentialUser()

        ' 使用 sqltxt 進行資料庫比對

        ' 以下假設是比對後的結果 (顯示 SQL 語法)
        Return sqltxt 
    End Function
End Class

另外兩個程式碼一模一樣,我就不在重覆了。(UserAdminAccess.vb、UserAdminOracle.vb)

WebForm_5 專案

先參考 DataAccessLayer_Final 類別庫專案。這裡只有一個 Login.aspx,你輸入資料後會顯示組合後的 SQL 語法。

Login.aspx UI HTML

<h2>網頁登入 (DataAccessLayer_Final)</h2>
<form id="form1" runat="server">
<div>
    <asp:Label ID="lbName" runat="server" Text="帳號:"></asp:Label>
    <asp:TextBox ID="tbName" runat="server"></asp:TextBox>
    <br />
    <asp:Label ID="lbPassword" runat="server" Text="密碼:"></asp:Label>
    <asp:TextBox ID="tbPassword" runat="server" TextMode="Password"></asp:TextBox>
    <br />
    <br />
    <asp:Button ID="btnLogin" runat="server" Text="登錄" />
    <br />
    <br />
    <asp:Label ID="lbSql" runat="server"></asp:Label>
</div>
</form>

Login.aspx.vb 後端程式碼

Public Class Login
    Inherits System.Web.UI.Page

    Protected Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        ' 使用帳號-密碼方式來驗證
        Dim credentials As New DataAccessLayer_Final.CredentialsNamePassword
        credentials.UserName = tbName.Text
        credentials.Password = tbPassword.Text

        ' 使用 Sql Server
        Dim ua As DataAccessLayer_Final.IUserAdmin = DataAccessLayer_Final.UserAdminFactory.CreateUserAdmin()
        If String.IsNullOrEmpty(tbName.Text) AndAlso String.IsNullOrEmpty(tbPassword.Text) Then
            If ua.Login(credentials) <> "" Then
                lbSql.Text = String.Format("SQL 語法:{0}", ua.Login(credentials))
            End If
        Else
            lbSql.Text = ""
        End If
    End Sub
End Class

這樣我們就完成最後一個範例。

結論

多看、多練習。一個能看能動的範例,會比單純的程式碼有趣多了。你可以下載到我整個VB Loging OOP Source Code Project,雖然可能你邊看文章邊看Code邊執行會累了點,不過相信我,這會很值得。

參考資料

  1. 凭什么要用面向对象编程——面向对象重要设计原则概述
  2. 凭什么要用面向对象编程(补充)
  3. 元件類別(類別庫範本,Class Library)
  4. KKBruce : Visual Basic - OOP / Reflection 相關文章

2 則留言:

  1. 以上為尊重原作者,我對於程式碼盡量原汁原味。不過Allen大大有些指教,保留以下對話,讓大家更進一步了解,好還可以更好。

    * Dim ua As DataAccessLayer_fingerMark.IUserAdmin =DataAccessLayer_fingerMark.UserAdminFactory.CreateUserAdmin()
    * 這裡可以改一下
    * 你在這class裡,去寫了讀取config 值並用reflection生出物件
    * 但其實經由一個字串,生出物件,應該是由一支utility 的class來做,而不是寫在UserAdminFactory裡面
    * UserAdminFactory裡頂多只需要叫用這支utility就可以了
    才不會有好多class都寫了一些reflection的程式碼

    ...

    *Private Shared ReadOnly AssemblyName As String = "DataAccessLayer_DB"
    * 這也是不對的
    * 因為,在實務上,你也許一開始寫了access, sql ,oracle,並編譯成DataAccessLayer_DB.dll裡
    * 但一年後,若又需要加mysql呢?
    * 其實並不是去改這支dll哦
    * 而是加寫一支dll
    * 因此,你不宜將dll filename寫死而是在config 裡面寫成
    *
    * 日後若需要mysql,就寫成
    *

    回覆刪除
  2. 讀者來信:
    你好,最近我要做1個project.其中有關於VB和MS SQL的部份..
    所以有在關注你的blog,裡面內容很豐富,給了我很大的幫助..謝謝!^^

    我看了你寫的Login/登入/登錄之物件導向實作
    裡面有好幾個範例,但省略了一些細節.
    我研究了好久都研究不到..所以想向你請教一下...

    我想做的login界面是連接我的sql database取用戶 帳號 和密碼的...
    我想做到先查用戶名稱是否存在,不存在就有msgbox提示用戶名稱不正確.
    如果用戶名稱存在了,再配對密碼是否正確,不正確再出msgbox提示密碼不對這樣子..

    希望大大能詳細指教一下..謝謝...

    回答:
    本篇的重點在於OOP觀念上,而非程式碼實作上。

    至於你所提到的Login問題,方法-邏輯其實你已經自行提供了,只差程式碼實作部份。
    我建議參考另一位MVP 91哥的文章:http://www.dotblogs.com.tw/hatelove/category/5036.aspx
    前幾篇都是以Login為範例。

    如果你實作出來的程式碼有任何問題,歡迎再討論。

    回覆刪除

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。