企業級應用架構(一) 三層架構之解耦


前言

   前段時間朋友拿了個網站給我,讓我幫忙添加幾個小功能,我爽快的答應了,但是當我打開源碼,我瞬間就奔潰了,整個項目連最基本的三層框架也沒有搭建,僅僅是封裝了一個sqlhelp作為數據庫的操作接口,項目中的SQL查詢語句無處不在,業務邏輯緊緊耦合在UI邏輯中,看到這樣的代碼,坦白來說,我什么興致都沒有了,但是礙着人情,我硬着頭皮,把基本功能的完成交差,通過這件事情,我對軟件分層進行了深入的思考。

三層架構

  說到三層架構,大伙都很熟悉,我也不再多啰嗦了,我們直接快速搭建一個。

     項目的引用關系是:StructWed->BLL,Model;BLL->DAL,Model;DAL->Model。下面我們來演示一下程序流程,假設我們的數據庫有一張訂單表Order表,我們的業務是針對Order表進行的增刪改查,那么根據三層架構的編程模式,我們就需要建立起對應的Model層實體,數據訪問層實體和業務層實體,我們分別用OrderModel,OrderDAL,OrderBLL表示,代碼如下,由於僅僅是為了演示,所以我並未提供相應的實現。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mode
{
    public class OrderModel
    {
        public int ID { get; set; }

        public int productName { get; set; }

        public DateTime CreateTime { get; set; }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mode;

namespace DaL
{
    public class OrderDAL
    {
        /// <summary>
        /// 向Order表插入數據
        /// </summary>
        /// <returns>成功:true,失敗:false</returns>
        public bool Insert()
        {
            return true;
        }
        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Update(OrderModel model)
        {
            return true;
        }
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Delete(int id)
        {
            return true;
        }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DaL;
using Mode;

namespace Bll
{
    public class OrderBLL
    {
        protected OrderDAL orderDal = new OrderDAL();
        
        public bool Insert(OrderModel model)
        {
            //業務點1
            //業務點2
            return orderDal.Insert();
        }

        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Update(OrderModel model)
        {
            //業務點1
            //業務點2
             return orderDal.Update(model);
        }
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Delete(int id)
        {
            //業務點1
            //業務點2
            return orderDal.Delete(id);
        }
    }
}
View Code

  好了,現在讓我們把目光聚焦到OrderBLL上面來。我們發現OrderBLL對數據庫Order表的所有操作都是在調用其內部創建的orderDAL實體的同名方法,換句話來說OrderBLL指示其內部orderDAL實體去完成數據庫的增刪改查。

  好現在,我們思考一下。假設有一天我們發現我們數據訪問層的orderDAL實體代碼寫的很不優雅,效率極差,我們想用一個更優雅的實體,比如OrderActiveDAL去替換掉它,那么這個時候我們OrderBLL的代碼就必須做相應的改動,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DaL;
using Mode;

namespace Bll
{
    public class OrderBLL
    {
        protected OrderActive orderActive = new OrderActive();
        
        public bool Insert(OrderModel model)
        {
            //業務點1
            //業務點2
            return orderActive.Insert();
        }

        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Update(OrderModel model)
        {
            //業務點1
            //業務點2
             return orderActive.Update(model);
        }
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Delete(int id)
        {
            //業務點1
            //業務點2
            return orderActive.Delete(id);
        }
    }
}
View Code

    這顯然不是一種好的處理方式。我們追求的是在替換orderDal實體的同時,不修改OrderBLL的任何代碼,換句話說就是解除BLL層與DAL層的耦合,為了達到這個目的,我們添加一個叫IDAL的接口類庫,如圖

      特別注意,我們在BLL層中添加了對IDA的引用,同時移除了對DAL的引用,這就意味着我們在BLL中無法創建(new)DAL層中的任何實體。下面我們在IDA項目中定義IOrder接口,如下

using Mode;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IDal
{
    public interface IOrder
    {
        /// <summary>
        /// 向Order表插入數據
        /// </summary>
        /// <returns>成功:true,失敗:false</returns>
        bool Insert();
       
        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        bool Update(OrderModel model);
       
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        bool Delete(int id);
    }
}
View Code

     我們讓DAL引用IDAL,同時讓OrderActiveDAL,OrderDal分別實現IOrder接口,如下

using IDal;
using Mode;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DaL
{
    class OrderActiveDAL : IOrder
    {
        /// <summary>
        /// 向Order表插入數據
        /// </summary>
        /// <returns>成功:true,失敗:false</returns>
        public bool Insert()
        {
            return true;
        }
        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Update(OrderModel model)
        {
            return true;
        }
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Delete(int id)
        {
            return true;
        }
    }
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mode;
using IDal;

namespace DaL
{
    public class OrderDAL : IOrder
    {
        /// <summary>
        /// 向Order表插入數據
        /// </summary>
        /// <returns>成功:true,失敗:false</returns>
        public bool Insert()
        {
            return true;
        }
        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Update(OrderModel model)
        {
            return true;
        }
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Delete(int id)
        {
            return true;
        }
    }
}
View Code

    現在我們隊BLL層OrderBLL做相應的改動,把數據訪問實體orderDAL的類型,定義為接口IOrder類型,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DaL;
using Mode;
using IDal;

namespace Bll
{
    public class OrderBLL
    {
        protected IOrder orderDal;

        public OrderBLL()
        { 
            //在這里需要實力化orderDal
        }

        public bool Insert(OrderModel model)
        {
            //業務點1
            //業務點2
            return orderDal.Insert();
        }

        /// <summary>
        /// 修改Order表數據
        /// </summary>
        /// <param name="model">表數據對應實體</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Update(OrderModel model)
        {
            //業務點1
            //業務點2
             return orderDal.Update(model);
        }
        /// <summary>
        /// 刪除Order表指定ID的記錄
        /// </summary>
        /// <param name="id">表ID</param>
        /// <returns>成功:true,失敗:false</returns>
        public bool Delete(int id)
        {
            //業務點1
            //業務點2
            return orderDal.Delete(id);
        }
    }
}
View Code

   好了,現在讓我們把目光聚集OrderBLL的構造函數。我們知道,OrderBLL會調用orderDAL去操作數據庫,而orderDAL的類型為IOrder,也就是說但凡實現了IOrder接口的類實例都可以賦值給orderDAL。但是現在問題的關鍵是我們如何為orderDAL賦值,前面我們說過BLL移除了DAL的引用,所以在BLL層中直接去new數據訪問層的實例,是不可能的。這里我提供兩種處理方式,第一種采用IOC容器如spring.net幫助我們創建DAL層實例然后在OrderBLL的構造函數中賦值給orderDAL,另一種則是利用工廠模式和反射來創建DAL層實例了,本文將詳述第二種,至於第一種在后面的系列中會有專門的章節講述。現在我們在項目中添加一個Factoy工廠類庫,並讓BLL層引用Factoy類庫,如圖:

 

   接着我們來寫工廠類。我們從配置文件中讀出程序集路徑和類的全名,利用反射的原理創建DAL層的實例,代碼如下

using IDal;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Factory
{
    public class OrderDALFactory
    {

        private static readonly string AssemblyName = ConfigurationManager.AppSettings["Assembly"];
        private static readonly string className = ConfigurationManager.AppSettings["className"];
        public static IOrder CreateOrder()
        {
            return (IOrder)Assembly.Load(AssemblyName).CreateInstance(className);
        }
    }
}
View Code

 
  最后我們在OrderBLL的構造函數利用OrderDALFactory工廠給orderDAL賦值

 public OrderBLL()
  { 
       //在這里需要實力化orderDal
       orderDal=OrderDALFactory.CreateOrder();
  }

  這樣我們就實現了BLL與DAL層的解耦,當我們BLL需要不同的IOrder實體的時候,我們只需要修改相應的配置文件即可。

總結  

  程序框架間層與層之間的解耦是富含挑戰的一項工作,充分的體現出了面向對象的編程思想,巧妙的運用了各類設計模式。本文實現方法相對簡單,僅當拋磚引玉。在接下來的文章中,我將就三層架構中各個層次的抽象與封裝做詳細說明。因為各個層次的抽象與封裝是針對不同技術點來實現的,比如數據訪問層,對EF技術與ADO.net技術的抽象與封裝細節上就會有所不通。但總體思想是一致的,那就是我們必須為每個層次抽象出統一的接口,供上層引用,同時我們必須提供相應的注入方式,為調用層引用的接口實例賦值實例化。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM