文章出處

回到目錄

規 約(Specification)模式:第一次看到這東西是在microsoft NLayer項目中,它是微軟對DDD的解說,就像petshop告訴了我們MVC如何使用一樣,這個規約模式最重要的作用是實現了查詢語句與查詢條件的 分離,查詢語句在底層是穩定的,不變的,而查詢條件是和具體業務,具體領域有關的,是易變的,如果我們為每一個領域的每一個新需求都寫一個新的方法,那就 會出現很多重復的代碼,不利于程序的最終擴展!

下面我們來看一個經典例子

一個IOrderRepository的接口,定義了一個訂單倉儲

        Order_Info GetOrder_InfoById(int orderID);
        List<Order_Info> GetOrder_Info(DateTime from, DateTime to);
        List<Order_Info> GetOrder_InfoByUser(int userID);


代碼本身沒有任何問題,你只要去實現它就可以了,當一個新的需求到了之后,你的接口要被擴展(這是不被提倡的,一般我們會新建一個接口),然后修改

原來的實現類,去實現接口新的方法(違背了OCP原則),這種做法是大多部開發團隊所經歷了,我,一個普通的人,也經歷了,但當我知道DDD后,當我看完

microsoft Nlayer項目之后,我知道,我一定要改變這種局面,于是,代碼在規約模式的指導下,進行重構了,呵呵。

先看一下規約模式的類關系圖

下面是我對原來結構的修改(由于原程序是三層架構,所以我就不改變原有架構了,只是對代碼進行重構,DAL層,BLL層,WEB層)

基礎設施層

IRepository倉儲接口如下,怎么去實現就不放了,呵呵

public interface IRepository<TEntity>
         where TEntity : class
    {
        /// <summary>
        /// 添加實體并提交到數據服務器
        /// </summary>
        /// <param name="item">Item to add to repository</param>
        void Add(TEntity item);

        /// <summary>
        /// 移除實體并提交到數據服務器
        /// 如果表存在約束,需要先刪除子表信息
        /// </summary>
        /// <param name="item">Item to delete</param>
        void Remove(TEntity item);

        /// <summary>
        /// 修改實體并提交到數據服務器
        /// </summary>
        /// <param name="item"></param>
        void Modify(TEntity item);

        /// <summary>
        /// 通過指定規約,得到實體對象
        /// </summary>
        /// <param name="specification"></param>
        /// <returns></returns>
        TEntity GetEntity(ISpecification<TEntity> specification);

        /// <summary>
        /// 通用表達式樹,得到實體
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        TEntity GetEntity(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// Get all elements of type {T} in repository
        /// </summary>
        /// <returns>List of selected elements</returns>
        IQueryable<TEntity> GetEntities();

        /// <summary>
        /// Get all elements of type {T} that matching a
        /// Specification <paramref name="specification"/>
        /// </summary>
        /// <param name="specification">Specification that result meet</param>
        /// <returns></returns>
        IQueryable<TEntity> GetEntities(ISpecification<TEntity> specification);

        /// <summary>
        /// 通用表達式樹,得到集合
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate);
    }

IOrderRepository接口如下

 public interface IOrderRepository :
        Domain.Core.IRepository<Order_Info>
    {
        void InsertOrder(Order_Info entity);
    }

DAL底層為數據持久化層,它是非常穩定的,只提供最基本的表操作,具體業務如何組成,全放在BLL層去實現

領域層

這一層中定義具體業務的規約,并組成查詢方法及調用DAL層的具體方法(DAL層來接受從BLL層傳過來的ISpecification參數)

    /// <summary>
    /// 根據下單日期得到訂單列表
    /// </summary>
    public class OrderFromDateSpecification : Specification<Order_Info>
    {
        DateTime? _fromDate;
        DateTime? _toDate;
        public OrderFromDateSpecification(DateTime? fromDate, DateTime? toDate)
        {
            _fromDate = fromDate ?? DateTime.MinValue;
            _toDate = toDate ?? DateTime.MaxValue;
        }
        public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
        {
            Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
            spec &= new DirectSpecification<Order_Info>(o => o.CreateDate >= _fromDate
                && o.CreateDate <= _toDate);
            return spec.SatisfiedBy();
        }
    }
    /// <summary>
    /// 通過用戶信息得到他的訂單列表
    /// </summary>
    public class OrderFromUserSpecification : Specification<Order_Info>
    {
        int _userID = default(Int32);
        public OrderFromUserSpecification(int userID)
        {
            _userID = userID;
        }
        public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
        {
            Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
            spec &= new DirectSpecification<Order_Info>(o => o.UserID == _userID);
            return spec.SatisfiedBy();
        }
    }

業務層真實的查詢主體,只要在一個方法里寫就OK了,然后它非常穩定,如果以后還有其它查詢業務出來,直接添加一個查詢規約即可

        /// <summary>
        /// 根據WEB層傳來及組件好的規約,返回集體
         /// </summary>
        /// <param name="spec"></param>
        /// <returns></returns>
        public List<Order_Info> GetOrder_InfoBySpec(ISpecification<Order_Info> spec)
        {
            return _iOrderRepository.GetEntities(spec).ToList();
        }

 

WEB層

Web層建立一個指定的規約,并為規約組件所需要的數據即可

        public ActionResult List(int? userID)
        {
            ISpecification<Order_Info> spec = new OrderFromUserSpecification(userID ?? 0);
            var model = orderService.GetOrder_InfoBySpec(spec);
            return View(model);
        }

如果這時來了個新需要,使用用戶名進行查詢,你可以直接建立一個OrderFromUserNameSpecification的規約即可,而不需要修改OrderService,呵呵!

回到目錄


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()