Login VB-OOP 實作前言
Login (登入/登錄) 動作,不管在任何應用系統 (AP, Application System)都能看到它的影子。像我的手機,一開機先問個 PIN 密碼...光一個手機,就能有好多密碼。
- 進公司刷卡(Card);
- 提錢(Card);
- 上網(Accout/Password);
- 回家開門(Key);
- 開車(Key);
- 指紋(生物辨識);
你能發現每天從早到晚,其實都在不斷的 Login / Logout,出門上班,對「家」而言,你是 Logout,開車上班,對「車」而言,你是 Login,到達公司,對「車」而言,你是 Logout,進公司,必須刷卡才能進入,那是 Login,下班刷卡離開公司,那是 Logout。
指紋辨識,這幾年拜 NB 幫的忙,很多 NB 都把指紋辨識當成基本配備,省下了打 Keyboard 的時間,刷一下手指,超方便的。還記得,大學時在「
國家太空中心」打工,國家太空中心可以使用「掌紋辨識」,在當時可以說是很先進方便,辨識率也不錯,就我在那裡打工時間裡,最少沒有出錯過。
以下是以程杰(大話設計模式作者) Blog 裡兩篇文章為基礎,以這兩篇文章實作出 Visual Basic 可執行版本。
- 凭什么要用面向对象编程——面向对象重要设计原则概述
- 凭什么要用面向对象编程(补充)
非常推薦給學習過物件導向的人來看,Login 範例非常實用,只要是「AP」你一定會碰到 Login 問題,學習如何透過物件導向來處理此一問題。
我覺得另一重點是「思緒」,想想,如果是你,你會怎麼處理?是否會有一樣的思緒?而且我認為文章中提出的問題,都還算常見,不算是那種為了寫書或寫文章而特別設計的例子。
範例一:網頁登入 (CodeBehind) (對應文章 2007-7-1 對話)
WebForm_1 專案 - Login.aspx
Login.aspx 介面 HTML。
01 | < h2 >網頁登錄 ( CodeBehind )</ h2 > |
02 | < form id = "form1" runat = "server" > |
04 | < asp:Label ID = "lbName" runat = "server" Text = "帳號:" ></ asp:Label > |
05 | < asp:TextBox ID = "tbName" runat = "server" ></ asp:TextBox > |
08 | < asp:Label ID = "lbPassword" runat = "server" Text = "密碼:" ></ asp:Label > |
09 | < asp:TextBox ID = "tbPassword" runat = "server" TextMode = "Password" ></ asp:TextBox > |
14 | < asp:Button ID = "btnLogin" runat = "server" Text = "登錄" /> |
Login.aspx.vb CodeBehind 程式碼。
02 | Inherits System.Web.UI.Page |
04 | Protected Sub tbLogin_Click(sender As Object , e As EventArgs) Handles btnLogin.Click |
05 | Dim userName As String = tbName.Text |
06 | Dim passWord As String = tbPassword.Text |
08 | If (login(userName, passWord)) Then |
09 | Response.Redirect( "LoginOK.aspx" ) |
11 | Response.Redirect( "LoginFail.aspx" ) |
15 | Private Function login(userName As String , password As String ) As Boolean |
18 | If userName = "kkbruce" And password = "12345" Then |
此範例為最最最基礎的 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
02 | Inherits System.Web.UI.Page |
04 | Protected Sub tbLogin_Click(sender As Object , e As EventArgs) Handles btnLogin.Click |
05 | Dim userName As String = tbName.Text |
06 | Dim password As String = tbPassword.Text |
09 | Dim ua As New UserAdmin |
10 | If (ua.Login(userName, password)) Then |
11 | Response.Redirect( "LoginOK.aspx" ) |
13 | Response.Redirect( "LoginFail.aspx" ) |
29 | Public Function Login(userName As String , password As String ) As Boolean |
32 | If userName = "kkbruce" And password = "12345" Then |
我們把原本的 Login 函式提升至一個 UserAdmin 類別裡,這樣就可以給大家共用。
範例三:WinForm 登錄 ( DataAccessLayer ) (對應文章 2007-7-29 對話)
DataAccessLayer (Windows --> 類別庫) 專案
將範例二的 Class UserAdmin 移至 DataAccessLayer 類別庫專案。
UserAdmin.vb
12 | Public Function Login(userName As String , password As String ) As Boolean |
15 | If userName = "kkbruce" And password = "12345" Then |
移至類別庫後,我們必須在要使用專案裡「加入參考」--> 選擇「Projects」 --> 選擇「DataAccessLayer」,然後在程式即可使用。
WinForm_1 專案
我們先把 WinForm Login UI拉好,
 |
圖一:WinForm Login UI |
然後撰寫Button的程式碼:
03 | Private Sub bnLogin_Click(sender As System. Object , e As System.EventArgs) Handles bnLogin.Click |
04 | Dim userName As String = tbName.Text |
05 | Dim password As String = tbPassword.Text |
08 | Dim ua As New DataAccessLayer.UserAdmin |
09 | If ua.Login(userName, password) Then |
10 | lbMsg.Text = "WinForm 登錄成功!" |
12 | lbMsg.Text = "WinForm 登錄失敗!" |
到這裡還算簡單。也讓我們學習到「類別庫」的使用,如果你還不會使用類別庫專案,請參考我的這一篇「
元件類別(類別庫範本,Class Library) 」。
範例四:網頁登錄 (DataAccessLayer_DB)(對應文章 2007-8-3 對話)
此處會使用到「介面、繼承、多型」等特性。如果不懂,請參考「
Visual Basic 裡 OOP 相關文章」。
DataAccessLayer_DB 類別庫專案
我們重新整理整個 DataAccessLayer 為 DataAccessLayer_DB,使它可以實現切換資料庫時,不用去動到原始程式碼。
IUserAdmin 介面,定義所有繼承類別必須要有一個「Login 函式」。
IUserAdmin.vb 介面
4 | Public Interface IUserAdmin |
5 | Function Login(userName As String , password As String ) As Boolean |
接下來我們繼承 IUserAdmin 介面,實作 Access, Oracle, Sql Server 三個類別。
UserAdminAccess.vb
01 | Public Class UserAdminAccess |
10 | Public Function Login(userName As String , password As String ) As Boolean Implements IUserAdmin.Login |
13 | If userName = "kkbruce" And password = "12345" Then |
UserAdminOracle.vb
01 | Public Class UserAdminOracle |
10 | Public Function Login(userName As String , password As String ) As Boolean Implements IUserAdmin.Login |
13 | If userName = "kkbruce" And password = "12345" Then |
UserAdminSqlServer.vb
01 | Public Class UserAdminSqlServer |
10 | Public Function Login(userName As String , password As String ) As Boolean Implements IUserAdmin.Login |
13 | If userName = "kkbruce" And password = "12345" Then |
這裡我們偷懶了,原因前面說過了,
為了 Focus 在「Login」這件事情上面。雖然看起來都是一模一樣的程式碼,但在原應有的實作中,資料庫連線,SQL語法 … 都會有差異,尤其是在其他非MS系列資料庫,並不是都一樣,這裡我們再提醒一次。
Login.aspx.vb
Login UI HTML與範例一相同。我們直接程式碼。
02 | Inherits System.Web.UI.Page |
04 | Private Sub btnLogin_Click(sender As Object , e As System.EventArgs) Handles btnLogin.Click |
05 | Dim userName As String = tbName.Text |
06 | Dim password As String = tbPassword.Text |
10 | Dim ua As New DataAccessLayer_DB.UserAdminAccess |
14 | If ua.Login(userName, password) Then |
15 | Response.Redirect( "LoginOK.aspx" ) |
17 | Response.Redirect( "LoginFail.aspx" ) |
以上寫法只差一個問題,如果我們要更換資料庫的話,必須動到此主程式。
範例五:UserAdminFactory 工廠模式 (對應文章 2007-8-8 對話)
我們在範例四最後,透過「Dim ua As New DataAccessLayer_DB.UserAdminAccess」的方式來產生實體,一旦我們要更換資料庫,就必須更動主程式,這不是好辦法,比較好的辦法透過工廠模式來新增實體,這樣的話,要修改也不必更動到此主程式,所以我們在類別庫中再新增一個 UserAdminFactory.vb 的工廠類別。
UserAdminFactory.vb 工廠類別
01 | Public Class UserAdminFactory |
02 | Private Shared ReadOnly db As String = "Access" |
07 | Public Shared Function CreateUserAdmin() As IUserAdmin |
08 | Dim iua As IUserAdmin = Nothing |
12 | iua = New UserAdminAccess |
14 | iua = New UserAdminOracle |
16 | iua = New UserAdminSqlServer |
以原來主程式中產生實體改為使用工廠類別來產生。
3 | Dim ua As DataAccessLayer_DB.IUserAdmin = DataAccessLayer_DB.UserAdminFactory.CreateUserAdmin() |
範例六:改進工廠,Reflection - Config (對應文章 2007-8-15 對話)
讓工廠依賴 *.config 檔。此處使用到 Reflection,如不清楚,請參考「
Visual Basic Reflection 相關文章」。
UserAdminFactory_Reflection.vb
01 | Imports System.Configuration |
02 | Imports System.Reflection |
04 | Public Class UserAdminFactory_Reflection |
06 | Private Shared ReadOnly AssemblyName As String = "DataAccessLayer_DB" |
07 | Private Shared ReadOnly db As String = ConfigurationManager.AppSettings( "DB" ) |
09 | Public Shared Function CreateUserAdmin() As IUserAdmin |
11 | Dim className As String = AssemblyName & ".UserAdmin" & db |
13 | Dim iua As IUserAdmin = CType ( Assembly .Load(AssemblyName).CreateInstance(className),IUserAdmin) |
然後,在修改 WebForm_3 專案 Web.config 加入 appSettings 相關設定。
修改Web.confg的AppSettings設定
03 | < add key = "DB" value = "Access" /> |
10 | < compilation debug = "true" strict = "false" explicit = "true" targetFramework = "4.0" /> |
這樣之後,讓我們的工廠來依賴 Web.config 或 App.config,當我們需要更換資料庫時,所以程式碼都不必修改。最後主程式 Loging 使用 Reflection (反映) 版本的工廠。
Login.aspx.vb 使用 Reflection 工廠
02 | Inherits System.Web.UI.Page |
04 | Private Sub btnLogin_Click(sender As Object , e As System.EventArgs) Handles btnLogin.Click |
05 | Dim userName As String = tbName.Text |
06 | Dim password As String = tbPassword.Text |
25 | Dim ua As DataAccessLayer_DB.IUserAdmin = DataAccessLayer_DB.UserAdminFactory_Reflection.CreateUserAdmin() |
27 | If ua.Login(userName, password) Then |
28 | Response.Redirect( "LoginOK.aspx" ) |
30 | Response.Redirect( "LoginFail.aspx" ) |
範例七:打卡與指紋登錄 (DataAccessLayer_FingerMark)(對應文章 2007-9-23 對話)
本篇一剛頭我們就討論過,Login 的方法千奇百怪,例如「家」,除了 Key 之外,你還可能有申請安裝保全服務,那除了 Key 之外,連「家」都必須刷卡,你才能安心的 Login / Logout。所以不要覺得這裡的需求很奇怪。
Users.vb
使用者登錄相關屬性。
05 | Public Property UserID() As Integer |
06 | Public Property UserName() As String |
07 | Public Property Password() As String |
08 | Public Property CardID() As String |
09 | Public Property FingerMark() As String |
修改 IUserAdmin 介面,使用 Users 類別。
IUserAdmin 介面 - 使用 Users類別
4 | Public Interface IUserAdmin |
5 | Function Login(user As Users) As Boolean |
接下來,我們新增三個類別來繼承實作此 IUserAdmin 介面,UserAdminAccess.vb、UserAdminOracle.vb、UserAdminSqlServer.vb。
UserAdminAccess.vb
01 | Public Class UserAdminAccess |
10 | Public Function Login(user As Users) As Boolean Implements IUserAdmin.Login |
14 | If NameAndPassword(user) OrElse FingerMark(user) OrElse CardID(user) Then |
23 | Private Shared Function NameAndPassword( ByVal user As Users) As Boolean |
24 | Return user.UserName = "kkbruce" AndAlso user.Password = "12345" |
27 | Private Shared Function FingerMark( ByVal user As Users) As Boolean |
28 | Return user.FingerMark = "67890" |
31 | Private Shared Function CardID( ByVal user As Users) As Boolean |
32 | Return user.CardID = "8888" |
UserAdminAccess.vb
01 | Public Class UserAdminOracle |
10 | Public Function Login(user As Users) As Boolean Implements IUserAdmin.Login |
14 | If NameAndPassword(user) OrElse |
15 | FingerMark(user) OrElse |
25 | Private Shared Function NameAndPassword( ByVal user As Users) As Boolean |
26 | Return user.UserName = "kkbruce" AndAlso user.Password = "12345" |
29 | Private Shared Function FingerMark( ByVal user As Users) As Boolean |
30 | Return user.FingerMark = "67890" |
33 | Private Shared Function CardID( ByVal user As Users) As Boolean |
34 | Return user.CardID = "8888" |
UserAdminAccess.vb
01 | Public Class UserAdminSqlServer |
10 | Public Function Login(user As Users) As Boolean Implements IUserAdmin.Login |
14 | If NameAndPassword(user) OrElse FingerMark(user) OrElse CardID(user) Then |
23 | Private Shared Function NameAndPassword( ByVal user As Users) As Boolean |
24 | Return user.UserName = "kkbruce" AndAlso user.Password = "12345" |
27 | Private Shared Function FingerMark( ByVal user As Users) As Boolean |
28 | Return user.FingerMark = "67890" |
31 | Private Shared Function CardID( ByVal user As Users) As Boolean |
32 | Return user.CardID = "8888" |
再提醒一次,這三個類別只是我們方便 Demo,所以程式都長的一樣,但真實實作上會所有不同。
為了不複雜範例,工廠我們使用前面 UserAdminFactory.vb 這個版本的廠工類別。
fingerLogin.aspx 指紋登錄
這裡我們新增一個 Webform_4 專案,假設它是使用指紋登錄,而在程式上,我們又假設指紋是一個字串。
fingerLogin.aspx UI HTML
01 | < h2 >指紋登錄 (DataAccessLayer_fingerMark)</ h2 > |
02 | < form id = "form1" runat = "server" > |
04 | < asp:Label ID = "lbFinger" runat = "server" Text = "指紋:" ></ asp:Label > |
05 | < asp:TextBox ID = "tbFinger" runat = "server" ></ asp:TextBox > |
08 | (假設 FingerMark 是 String = "67890") |
11 | < asp:Button ID = "btnLogin" runat = "server" Text = "登錄" /> |
先加入類別庫的參考,接下來我們來使用 Users 類別及fingerMark來登入。
01 | Public Class fingerLogin |
02 | Inherits System.Web.UI.Page |
04 | Protected Sub btnLogin_Click(sender As Object , e As EventArgs) Handles btnLogin.Click |
05 | Dim user As New DataAccessLayer_fingerMark.Users |
06 | user.FingerMark = tbFinger.Text |
07 | Dim ua As DataAccessLayer_fingerMark.IUserAdmin = DataAccessLayer_fingerMark.UserAdminFactory.CreateUserAdmin() |
09 | If ua.Login(user) then |
10 | Response.Redirect( "LoginOK.aspx" ) |
12 | Response.Redirect( "LoginFail.aspx" ) |
程式碼沒有特別的地方,就是使用 Users 類別,然後由工廠產生實體,最後進行登錄驗證。
接下去為第二篇補充篇內容。
DataAccessLayer_Final and WebForm_5
將驗證分離出來成為一個介面,各自實作驗證方法。最後這個範例,我們特別一些,我們將會把處理後的 SQL 語法顯示出來,來代表是登錄。
ICredentials 介面
4 | Public Interface ICredentials |
5 | Function CredentialUser() As String |
以下是"帳號/密碼"、"刷卡"、"指紋"三者驗證方法作。
CredentialsNamePassword.vb 類別(帳號/密碼驗實作)
04 | Public Class CredentialsNamePassword |
05 | Implements ICredentials |
07 | Public Property UserName() As String |
08 | Public Property Password() As String |
10 | Public Function CredentialUser() As String Implements ICredentials.CredentialUser |
12 | Return " UserName='" & Me .UserName & "' and Password = '" & Me .Password & "'" |
CredentialsCard.vb 類別 (刷卡驗證方法實作)
04 | Public Class CredentialsCard |
05 | Implements ICredentials |
07 | Public Property CardID() As String |
09 | Public Function CredentialUser() As String Implements ICredentials.CredentialUser |
10 | Return " CardID='" & Me .CardID & "'" |
CredentialsFingerMark 類別(指紋驗證方法實作)
04 | Public Class CredentialsFingerMark |
05 | Implements ICredentials |
07 | Public Property FingerMark() As String |
09 | Public Function CredentialUser() As String Implements ICredentials.CredentialUser |
10 | Return " FingerMark='" & Me .FingerMark & "'" |
這裡主要是要回傳 SQL 語法 Where 的條件式。
IUserAdmin.vb 介面 -- 修改使用 ICredentials 介面
4 | Public Interface IUserAdmin |
5 | Function Login(credentials As ICredentials) As String |
最後是三個繼承 IUserAdmin 介面的實作類別。
UserAdminSqlServer.vb 類別
01 | Public Class UserAdminSqlServer |
09 | Public Function Login(credentials As ICredentials) As String Implements IUserAdmin.Login |
10 | Dim sqltxt As String = "select userid from users where" |
12 | sqltxt &= credentials.CredentialUser() |
另外兩個程式碼一模一樣,我就不在重覆了。(UserAdminAccess.vb、UserAdminOracle.vb)
WebForm_5 專案
先參考 DataAccessLayer_Final 類別庫專案。這裡只有一個 Login.aspx,你輸入資料後會顯示組合後的 SQL 語法。
Login.aspx UI HTML
01 | < h2 >網頁登入 (DataAccessLayer_Final)</ h2 > |
02 | < form id = "form1" runat = "server" > |
04 | < asp:Label ID = "lbName" runat = "server" Text = "帳號:" ></ asp:Label > |
05 | < asp:TextBox ID = "tbName" runat = "server" ></ asp:TextBox > |
08 | < asp:Label ID = "lbPassword" runat = "server" Text = "密碼:" ></ asp:Label > |
09 | < asp:TextBox ID = "tbPassword" runat = "server" TextMode = "Password" ></ asp:TextBox > |
14 | < asp:Button ID = "btnLogin" runat = "server" Text = "登錄" /> |
19 | < asp:Label ID = "lbSql" runat = "server" ></ asp:Label > |
Login.aspx.vb 後端程式碼
02 | Inherits System.Web.UI.Page |
04 | Protected Sub btnLogin_Click(sender As Object , e As EventArgs) Handles btnLogin.Click |
06 | Dim credentials As New DataAccessLayer_Final.CredentialsNamePassword |
07 | credentials.UserName = tbName.Text |
08 | credentials.Password = tbPassword.Text |
11 | Dim ua As DataAccessLayer_Final.IUserAdmin = DataAccessLayer_Final.UserAdminFactory.CreateUserAdmin() |
12 | If String .IsNullOrEmpty(tbName.Text) AndAlso String .IsNullOrEmpty(tbPassword.Text) Then |
13 | If ua.Login(credentials) <> "" Then |
14 | lbSql.Text = String .Format( "SQL 語法:{0}" , ua.Login(credentials)) |
這樣我們就完成最後一個範例。
結論
多看、多練習。一個能看能動的範例,會比單純的程式碼有趣多了。你可以下載到我整個
VB Loging OOP Source Code Project,雖然可能你邊看文章邊看Code邊執行會累了點,不過相信我,這會很值得。
參考資料
- 凭什么要用面向对象编程——面向对象重要设计原则概述
- 凭什么要用面向对象编程(补充)
- 元件類別(類別庫範本,Class Library)
- KKBruce : Visual Basic - OOP / Reflection 相關文章
以上為尊重原作者,我對於程式碼盡量原汁原味。不過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,就寫成
*
讀者來信:
回覆刪除你好,最近我要做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為範例。
如果你實作出來的程式碼有任何問題,歡迎再討論。