三層架構(3-tier architecture) 通常意義上的三層架構就是将整個業務應用劃分為:表現層(UI)、業務邏輯層(BLL)、資料通路層(DAL)。區分層次的目的即為了“高内聚,低耦合”的思想。
首先我們先用一組生活中的圖檔來說明三層的重要性。(摘自網絡)
生活中的執行個體
飯店有三個分工,服務員,廚師和采購員
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN0LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TQOhXQq50MFRUT4FEVkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TNxUDMwkTN3ETMzUDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
分三層,松耦合,更友善應對變化。
實際這種思想也适用于我們的三層架構。
UI隻負責顯示和采集使用者操作,不包含任何的業務相關的邏輯處理。
BLL 負責處理業務邏輯,通過UI傳來的操作指令,決定執行業務邏輯,在需要通路資料源的時候直接交給DAL處理。處理完成後,傳回必要的資料給UI.
DAL之提供基本的資料通路,不包含任何也不相關的邏輯處理。
二 系統登陸流程
以程式執行的順序跑一遍,是下圖這樣的流程。可以看到,每層的分工明确。各司其職。
此處示範的三層架構執行個體中,層層之間都要通過使用者名和密碼兩個變量來傳遞。這是一種方法,但是實際中也有可能,我們需要更多的資訊,如學号,年齡,性别,位址等。為了更加友善的傳輸資料,還可以通過實體來傳輸。将這些資訊做成某一實體的屬性,層與層之間穿實體,每層用時隻需調用實體的屬性即可。下文的模型Model就是依據這樣的思路建立的。.
設計過程
界面如圖。
首先先在資料庫中 建立Login資料庫,并設計Scores 和users兩表。
ID均為自增長。
MODEL1
生成一個使用者實體,便于傳輸資料。是以,其他各層之間都要添加對它的引用。
namespace Login.Model//其他程式集引用它,但它不會引用其他程式集
{
public class UserInfo
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
}
}
UI層,收集資料顯示資料,需要添加對BLL和Model的引用。将資料資訊賦給Model的UserInfo。我們看代碼便可知并沒有任何的業務和SQL等的操作。涉及到業務邏輯交給BLL。
namespace LoginUI
{
public partial class Form1 : Form
{
private void Form1_Load(object sender, EventArgs e)
{}
public Form1()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
string userName = txtUserName.Text.Trim();//收集資料
string password=txtPassword .Text;
Login.BLL .LoginManager mgr= new Login.BLL.LoginManager ();
Login.Model.UserInfo user = mgr.UserLogin(userName, password);//具體業務邏輯交給 BLL層 LoginManager
MessageBox.Show("登陸使用者:" + user.Username);//界面負責顯示
}
}
}
BLL介于 UI和DAL之間,需要添加對DAL和Model的引用。它做的業務邏輯是判斷使用者名密碼是否成立并增加積分。 但是,判斷過程需要用資料源,這一部分工作交給DAL,等DAL傳回資料之後,再繼續進行積分的業務操作。
namespace Login.BLL
{
public class LoginManager
{
public Login.Model.UserInfo UserLogin(string userName, string password)
{
//業務邏輯層要判斷使用者密碼是否成立 是以它需要資料庫中的資訊
Login.DAL.UserDAO uDao = new Login.DAL.UserDAO();
Login.Model .UserInfo user=uDao.SelectUser(userName, password);//把D層的查詢到的資訊再賦給Model中的UserInfo
//由于使用者密碼這些資訊需要通路資料庫 是以交給DAL處理。從這裡轉移到DAL。待DAL完成之後,傳回從資料庫中查詢回來 程式轉移到這裡。
//業務邏輯層接着判斷如果存在配套的使用者名和使用者密碼 則增加積分。
if (user != null)//user不為空,說明在資料庫中查到了符合條件的資訊。login successfully
{
Login.DAL.ScoreDAO sDao = new Login.DAL.ScoreDAO();
sDao.UpdateScore(userName,10);//由于在資料庫中增加積分 涉及到對資料的操作,程式從這裡跳轉到DAL 的UserDAO。然後将10傳進積分
return user;//将BAL層中的user 傳回UI
}
else
{
throw new Exception("登陸失敗。");
}
}
}
}
DAL層,需要添加對Model的引用。因為查詢使用者和添加積分都要對資料庫操作。是以在該層做了兩個類。UserDAO,将查詢到的資訊更新到UserInfo資訊中。ScoreDAO 直接在資料庫中添加積分資訊。
namespace Login.DAL
{// 隻提供基本的資料通路,不包含任何業務相關的邏輯處理
public class UserDAO
{
public Login.Model .UserInfo SelectUser(string userName, string password) //手動更改的public
{
//檢視資料庫是否有配套的使用者名和密碼
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))//使用using ,connection就可自動關閉
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = @"SELECT ID ,UserName,Password,Email From USERS WHERE [email protected] AND [email protected]";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add (new SqlParameter("@UserName",userName));
cmd.Parameters.Add (new SqlParameter("@Password",password));
conn.Open();
//讀取資料
SqlDataReader reader = cmd.ExecuteReader();
//判斷該條記錄是否存在
Login.Model.UserInfo user = null;
while (reader.Read())//reader是一條一條讀資料的,讀之前它會确認是還有資料。read()會傳回一個布爾值,有則為true,沒有就是false。
{
if (user == null)
{
user = new Login.Model.UserInfo();
}
user.ID = reader.GetInt32(0);
user.Username = reader.GetString(1);
user.Password = reader.GetString(2);
if (!reader.IsDBNull(3))//因為空值不能調用GetString方法 故需要做一個判斷
{
user.Email = reader.GetString(3);
}
}
return user;
}
}
}
}
ScoreDAO
namespace Login.DAL
{
public class ScoreDAO
{
public void UpdateScore(string userName, int value)
{//給該使用者在資料庫中添加積分
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))//using 可以自動關閉連接配接
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = @"INSERT INTO Scores(UserName,Score) Values(@UserName,@Score)";
cmd.Parameters.Add(new SqlParameter("@UserName", userName));
cmd.Parameters.Add(new SqlParameter("@Score",value));
conn.Open();
cmd.ExecuteNonQuery();
}
}
}
}
傳遞
整個過程清楚之後,我們再來 看一下Model的UserInfo類是如何在各層之間活蹦亂跳地傳輸的。
1,UI層,将輸入的使用者名和密碼賦給變量。将變量作為參數傳到BLL層的UserLogin(UserInfo類)中。
2,BLL層,将UserLogin得到的參數傳給DAL層SelectUser(UserInfo類)中。
3,DAL 通過得到的參數查詢資料庫。再将查詢到的資訊傳回給User,通過傳輸到BLL層。
4,BLL層将積分10和LoginUser中的userNameUser傳到DAL層的UpdateScore中。
5,DAL利用UpdateScore的參數更新資料庫,傳回到BLL層。
6,BLL層将User傳回到UI層
7,UI層顯示User資訊
以上是三層架構的基本執行個體,感覺斷點調試的收獲是最大的。不斷調試的過程就會慢慢的熟悉它的流程和工作過程。它的思想和設計模式是一樣一樣的。都在尋求更優更靈活的解決方案。有了這種層次感 在思考的時候,也會想哪裡是U層,怎麼樣用B層等。我感覺它讓我的思路更清晰了。