三層架構--理論與實踐

00潤物無聲00發表於2015-02-23

    看視訊的時候,開篇提出了很多的問題,這讓我想起了“門衛定理”,你從哪裡來,要到那裡去,去幹什麼。學習本身就是這樣,提出問題比解決問題更重要,帶著問題學習,就會更加有目的性,注意力更集中。

一.理論篇:

1.三層架構是什麼?有那三層,他們的順序?

    三層架構是基於模組化程式設計的思想,為實現分解應用程式的需求,而逐漸形成的一種標準模式的模組劃分方法。從上到下分別是表現層(Presentation layer),業務邏輯層(Business Logic Layer),資料訪問層(Data access layer)。所謂三層體系結構,是在客戶端與資料庫之間加入了一個“中間層”


2.每一層的含義和功能,為什麼分三層?

表現層:位於最外層(最上層),最接近使用者。用於顯示資料和接收使用者輸入的資料,為使用者提供一種互動式操作的介面。

    UI設計原則是使用者至上,兼顧簡潔。

業務邏輯層:表示層和資料訪問層之間溝通的橋樑,主要負責資料的傳遞和處理

    從DAL中獲取資料,以供UI顯示用
    從UI中獲取使用者指令和資料,執行業務邏輯
    從UI中獲取使用者指令和資料,通過DAL寫入資料來源
職責機制
    UI->BLL->UI
    UI->BLL->DAL->BLL->UI

資料訪問層:也稱為是持久層,其功能主要是負責資料庫的訪問,可以訪問資料庫系統、二進位制檔案、文字文件或是XML文件。

    從資料來源載入資料(Select)
    向資料來源寫入資料(Insert/Update)
    從資料來源刪除資料(Delete)


三層的目的是實現“高內聚,低耦合”的思想,各層之間相互依靠介面連線,每一層的改變對其他層不會造成影響。

這樣做的好處有:

    1.開發團隊可以明確分工,統一程式碼格式,並行開發,提高效率。

    2.提高了系統的複用性,通過介面連線。

    3.使得系統更加健壯,系統的易於維護,如果一層出現了問題,只需要維護該層就可以了,同樣降低了維護的成本。

    4.分層之後讓系統更加清晰,把整體複雜的問題簡單化。

3.什麼時候需要三層?

    並不是所有的系統都要分三層或多層。任何事情都有兩面性。過多的分層限制了限制了客戶與開發人員的交流。由於層與層之間的引用關係,程式碼執行效率降低,會對系統的效能造成影響。 專案開發中具體問題要具體分析,盲目套用耦合反而會適得其反,不應分層而分層,不應模式而模式。對於業務邏輯簡單,沒有資料儲存層的系統,就沒有必要分層。

4.每一層的原則與層與層之間的引用?

DAL,BLL,UI分別在不同的程式集中。各層之間的引用關係如下:
    DAL所在程式集不引用BLL和UI
    BLL引用DAL
    UI直接引用BLL,可能間接引用DAL
    三層都引用實體層。



5.實體層怎麼理解?

    實體層為了封裝資料,為了資料在三層之間傳輸;對於大量的資料來說,用變數做引數不太合適 比如 把某個學生的所有資訊傳到下層,包括姓名 年齡 學號 班級...如果拿變數做引數,那在你的方法中起碼有N個引數,很容易造成引數匹配上的錯誤,而如果拿實體物件做引數則方便的多,只要傳一個學生實體就可以了,然後在方法裡通過get(),set()屬性獲取或者設定實體物件裡的成員屬性值 。

    實體物件實際上是對應著資料庫裡的每張表的,我們把表裡的欄位封裝在1個實體物件裡,當想用哪個欄位 ,就通過該實體物件的get() set() 把那個欄位提取出來。這比臨時建立1個變數要靈活的多,而且便於程式的維護和擴充套件。

    Model不知道外面的其他層次,獨立於其他層次,不會引用任何層次的程式集。其他的三層都會引用Model,都知道Modul的存在;

二.實踐篇

1.設計資料庫Login:

資料表
主鍵 列名 資料型別 允許Null值
dbo.Scores ID int


UserName
varchar(50)



Score
int

dbo.Users

ID
int



UserName
varchar(50)



PassWord
varchar(50)



Email
varchar(100)
允許

2.程式碼:

UI層

<span style="font-family:Courier New;font-size:14px;">namespace LoginUI</span><span style="font-size:14px;"><span style="font-family:Courier New;">
{
    public partial class Form1 : Form       //Partial:區域性型別允許我們將一個類、結構或介面分成幾個部分,分別實現在幾個不同的.cs檔案中。各個部分可以再不同的原始檔中,原始檔要有相同的名稱空間;
    {
        public Form1()
        {
            InitializeComponent();<span style="white-space:pre">	</span>//初始化控制元件;
        }</span><span style="font-family:System;">
</span></span><span style="font-family:Courier New;font-size:14px;">
        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();  //例項化,開始轉到BLL中。UI呼叫BLL層;
            //mgr.Login(UserName ,PassWord );
            
            try //異常處理程式,將可能發生例外的程式碼放到Try語句中,catch捕捉例外。與On error goto的不同點
            {
                Login.Model.UserInfo user = mgr.UserLogin(UserName, PassWord);

                MessageBox.Show("登入使用者:" + user.UserName);
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}</span><span style="font-size:18px;">
</span>
BLL層

<span style="font-family:Courier New;font-size:14px;">namespace Login.BLL
{
    public class LoginManager       //類要設定為共有的才能訪問;
    {
        public Login.Model .UserInfo  UserLogin (string UserName, string PassWord)   //User就是一個資料模型;上竄下蹦;需要額外的建程式集;//返回Model的UserInfo;
        {
            //throw new NotFiniteNumberException();
            //Login.DAL .UserDAO  uDAO =new Login .DAL.UserDAO ();
            //uDAO.SelectUser(UserName, PassWord);

            Login.DAL.UserDAO uDAO = new Login.DAL.UserDAO();       //類例項化一個物件;
            Login.Model.UserInfo user = uDAO.SelectUser(UserName, PassWord);

            if (user != null)       //登入成功
            {
                Login.DAL.ScoreDAO sDAO = new Login.DAL.ScoreDAO();
                sDAO.UpdateScore(UserName, 10);     //加10
                return user;
            }
            else
            {
                throw new Exception("登入失敗");    //丟擲異常
            }
        }
    }
}</span>
DAL層

<span style="font-family:Courier New;font-size:14px;">namespace Login.DAL
{
    public class UserDAO
    {
        public Login.Model .UserInfo  SelectUser(string UserName, string PassWord) //檢驗使用者存在不存在,登入是否成功。“資料模型”
        {
            //throw new NotImplementedException();
            using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))   //連線
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = @"SELECT ID,UserName,PassWord,Email FROM USERS WHERE UserName=@UserName AND PassWord =@PassWord";
                cmd.CommandType = CommandType.Text;     //指定如何解釋命令字串,text表示SQL 文字命令。
                cmd.Parameters.Add(new SqlParameter("@UserName", UserName));
                cmd.Parameters.Add(new SqlParameter("@PassWord", PassWord));

                conn.Open();
                SqlDataReader reader = cmd.ExecuteReader(); //針對 Connection 執行 CommandText,並生成 IDataReader。 
                Login.Model.UserInfo user = null;
                while (reader.Read())       //.Read()使 IDataReader 前進到下一條記錄。
                {
                    if (user == null)
                    {
                        user = new Login.Model.UserInfo();
                    }
                    user.ID =reader.GetInt32(0);
                    user.UserName = reader.GetString(1);
                    user.PassWord = reader.GetString(2);<span style="white-space: pre;">	</span>//不需要從資料庫中取;
                    

                    if (!reader.IsDBNull(3))
                    {
                        user.Email = reader.GetString(3);
                    }
                }
                return user;
            }
        }
    }
}</span><span style="font-size:18px;">
</span>

連線字串

<span style="font-family:Courier New;font-size:14px;">namespace Login.DAL
{
    class DbUtil    //連線字串
    {
        public static string ConnString = @"Server=fxq-PC;database=Login;user ID =sa ;PassWord=fxq";
    }
}</span>
登入成功則資料庫中加10

<span style="font-family:Courier New;font-size:14px;">namespace Login.DAL
{
    public class ScoreDAO
    {
        public void UpdateScore(string UserName, int Value)
        {
            //throw new NotFiniteNumberException();
            using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))<span style="white-space: pre;">	//using的程式碼範圍內你能使用conn這個物件,一旦你超出了using的程式碼範圍,就沒有conn可用;</span>
            {
                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();                //不用關閉,因為用了Using。相當於在using塊結束的時候手動呼叫conn.Dispose()方法</span><span style="font-family:Courier New;font-size:14px;">

                cmd.ExecuteNonQuery();      //對連線執行 Transact-SQL 語句並返回受影響的行數。
            }
        }
    }
}</span>
Entity(Model):為了封裝資料,為了資料在三層之間傳輸;Model不知道外面的其他層次,獨立於其他層次,不會引用任何層次的程式集。其他的三層都會引用Model,即都知道Modul的存在;

<span style="font-family:Courier New;font-size:14px;">namespace Login.Model         
{
    public class UserInfo
    {   //.NET3.0以後支援的寫法;
        //public int ID { get; set; }
        //public string UserName{get ;set;}
        //public string PassWord { get; set; }
        //public string Email { get; set; }

        private int _ID;<span style="white-space: pre;">	</span>//更加安全;
        private string _UserName;
        private string _PassWord;
        private string _Email;

        public int ID
        {
            get { return _ID; }
            set { _ID = value; }
        }
        public string UserName
        {
            set { _UserName = value; }
            get { return _UserName; }
        }
        public string PassWord
        {
            set { _PassWord = value; }
            get { return _PassWord; }
        }
        public string Email
        {
            set { _Email = value; }
            get { return _Email; }
        }   
    }
}</span><span style="font-size:18px;">
</span>

三.總結

    瞭解三層,體會分層結構與不分層的區別和優勢。加深對物件導向的思想的認識,實現解耦,懂得化難為簡。理論結合實踐,將分層做到靈活運用,不為分層而分層,不為模式而模式;

相關文章