閱讀目錄 前言 單元測試 糾正錯誤,重新出發 結語 一、前言 實際編碼已經寫了2篇了,在這過程中非常感謝有聽到觀點不同的聲音,藉著這個契機,今天這篇就把大家提出的建議一個個的過一遍,重新整理,重新出發,為了讓接下去的DDD之路走的更好。 二、單元測試 蟋蟀兄在我的第三篇文章下麵指出: 這點其實是我偷 ...
閱讀目錄
一、前言
實際編碼已經寫了2篇了,在這過程中非常感謝有聽到觀點不同的聲音,藉著這個契機,今天這篇就把大家提出的建議一個個的過一遍,重新整理,重新出發,為了讓接下去的DDD之路走的更好。
二、單元測試
蟋蟀兄在我的第三篇文章下麵指出:
這點其實是我偷懶了,單元測試其實不單單在DDD中是一個很重要的一環,在我們崇尚敏捷,快速迭代的大背景下,有良好的單元測試模塊可以保證快速迭代下的項目質量。有甚至可以使用測試先行的TDD模式。
單元測試的好處我就不多說了,那麼現在開始在項目中增加單元測試。單元測試有多種命名方式,我個人的方式是給每一個對象單獨建立一個測試類,然後裡面每個單元測試方法的命名規則為"方法名_條件_預期的結果"這樣子。那麼根據我們之前的Cart和CartItem的建模,編寫的單元測試如下:
[TestClass] public class CartTest { [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Constructor_CartIdDefault_ThrowArgumentException() { var cart = new Cart(default(Guid), Guid.NewGuid(), DateTime.Now); Assert.AreNotEqual(null, cart); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Constructor_UserIdDefault_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), default(Guid), DateTime.Now); Assert.AreNotEqual(null, cart); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Constructor_LastChangeTimeDefault_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), default(DateTime)); Assert.AreNotEqual(null, cart); } [TestMethod] public void AddCartItem_NotExisted_TotalItemCountIsIncreased() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100)); Assert.AreEqual(1, cart.TotalItemCount()); cart.AddCartItem(new CartItem(new Guid("22222222-2222-2222-2222-222222222222"), 1, 100)); Assert.AreEqual(2, cart.TotalItemCount()); } [TestMethod] public void AddCartItem_Existed_TotalItemCountIsNotIncreasedTotalItemNumIsIncreased() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100)); Assert.AreEqual(1, cart.TotalItemCount()); Assert.AreEqual(1, cart.TotalItemNum()); cart.AddCartItem(new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100)); Assert.AreEqual(1, cart.TotalItemCount()); Assert.AreEqual(2, cart.TotalItemNum()); } }
[TestClass] public class CartItemTest { [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ModifyQuantity_LessZero_ThrowArgumentException() { var cartItem = new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); cartItem.ModifyQuantity(-1); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ModifyQuantity_EqualsZero_ThrowArgumentException() { var cartItem = new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); cartItem.ModifyQuantity(0); } [TestMethod] public void ModifyQuantity_MoreZero_Success() { var cartItem = new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); cartItem.ModifyQuantity(10); Assert.AreEqual(10, cartItem.Quantity); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ModifyPrice_LessZero_ThrowArgumentException() { var cartItem = new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); cartItem.ModifyPrice(-1); } [TestMethod] public void ModifyQuantity_EqualsZero_Success() { var cartItem = new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); cartItem.ModifyQuantity(0); Assert.AreEqual(0, cartItem.Price); } [TestMethod] public void ModifyQuantity_MoreZero_Success() { var cartItem = new CartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); cartItem.ModifyQuantity(10); Assert.AreEqual(10, cartItem.Price); } }
三、糾正錯誤,重新出發
在寫CartItemTest的時候發現了一個問題。領域對象的設計中有一個要點,就是實體必須需要通過其所屬的聚合根才能訪問,這樣才能體現出聚合的的整體性,並且減少外界對聚合內部過多的瞭解。而目前對於CartItem的運用卻有些背道而馳的意思,由外部對象進行實例化,必然增加了外部調用方對整個購物項構造過程的瞭解。有一位園友tubo有提到這點。
我思考了下,覺得這位園友的建議是對的。他建議的改法恰恰能夠滿足這個要求,隱藏了構造CartItem實體的細節。
好了那先把CartItem的構造函數訪問類型設置為internal吧,這樣也只能在CartItem所在的Domain項目中進行實例化了,然後再修改Cart.AddCartItem方法的參數。變為如下:
public void AddCartItem(Guid productId, int quantity, decimal price) { var cartItem = new CartItem(productId, quantity, price); var existedCartItem = this._cartItems.FirstOrDefault(ent => ent.ProductId == cartItem.ProductId); if (existedCartItem == null) { this._cartItems.Add(cartItem); } else { existedCartItem.ModifyPrice(cartItem.Price); //有可能價格更新了,每次都更新一下。 existedCartItem.ModifyQuantity(existedCartItem.Quantity + cartItem.Quantity); } }
單元測試也做出相應的更改:
[TestClass] public class CartTest { [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Constructor_CartIdDefault_ThrowArgumentException() { var cart = new Cart(default(Guid), Guid.NewGuid(), DateTime.Now); Assert.AreNotEqual(null, cart); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Constructor_UserIdDefault_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), default(Guid), DateTime.Now); Assert.AreNotEqual(null, cart); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void Constructor_LastChangeTimeDefault_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), default(DateTime)); Assert.AreNotEqual(null, cart); } [TestMethod] public void AddCartItem_NotExisted_TotalItemCountIsIncreased() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); Assert.AreEqual(1, cart.TotalItemCount()); cart.AddCartItem(new Guid("22222222-2222-2222-2222-222222222222"), 1, 100); Assert.AreEqual(2, cart.TotalItemCount()); } [TestMethod] public void AddCartItem_Existed_TotalItemCountIsNotIncreasedTotalItemNumIsIncreased() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); Assert.AreEqual(1, cart.TotalItemCount()); Assert.AreEqual(1, cart.TotalItemNum()); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); Assert.AreEqual(1, cart.TotalItemCount()); Assert.AreEqual(2, cart.TotalItemNum()); } }
[TestClass] public class CartItemTest { [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ModifyQuantity_LessZero_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); var cartItem = cart.GetCartItem(new Guid("11111111-1111-1111-1111-111111111111")); Assert.AreNotEqual(null, cartItem); Assert.AreEqual(1, cartItem.Quantity); cartItem.ModifyQuantity(-1); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ModifyQuantity_EqualsZero_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); var cartItem = cart.GetCartItem(new Guid("11111111-1111-1111-1111-111111111111")); Assert.AreNotEqual(null, cartItem); Assert.AreEqual(1, cartItem.Quantity); cartItem.ModifyQuantity(0); } [TestMethod] public void ModifyQuantity_MoreZero_Success() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); var cartItem = cart.GetCartItem(new Guid("11111111-1111-1111-1111-111111111111")); Assert.AreNotEqual(null, cartItem); Assert.AreEqual(1, cartItem.Quantity); cartItem.ModifyQuantity(10); Assert.AreEqual(10, cartItem.Quantity); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ModifyPrice_LessZero_ThrowArgumentException() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); var cartItem = cart.GetCartItem(new Guid("11111111-1111-1111-1111-111111111111")); Assert.AreNotEqual(null, cartItem); Assert.AreEqual(100, cartItem.Price); cartItem.ModifyPrice(-1); } [TestMethod] public void ModifyPrice_EqualsZero_Success() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); var cartItem = cart.GetCartItem(new Guid("11111111-1111-1111-1111-111111111111")); Assert.AreNotEqual(null, cartItem); Assert.AreEqual(100, cartItem.Price); cartItem.ModifyPrice(0); Assert.AreEqual(0, cartItem.Price); } [TestMethod] public void ModifyPrice_MoreZero_Success() { var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now); cart.AddCartItem(new Guid("11111111-1111-1111-1111-111111111111"), 1, 100); var cartItem = cart.GetCartItem(new Guid("11111111-1111-1111-1111-111111111111")); Assert.AreNotEqual(null, cartItem); Assert.AreEqual(100, cartItem.Price); cartItem.ModifyPrice(10); Assert.AreEqual(10, cartItem.Price); } }
這樣一來,被玻璃魚兒和netfocus2位園友所指出的奇怪的“UserBuyProductDomainService”也自然消失了。應用層代碼變成:
public Result Buy(Guid userId, Guid productId, int quantity) { var product = DomainRegistry.ProductService().GetProduct(productId); if (product == null) { return Result.Fail("對不起,未能獲取產品信息請重試~"); } var cart = _getUserCartDomainService.GetUserCart(userId); cart.AddCartItem(productId, quantity, product.SalePrice); DomainRegistry.CartRepository().Save(cart); return Result.Success(); }
四、結語
DDD的道路是坎坷的,我希望通過在園子里發佈的文章能夠結交到志同道合的DDD之友,歡迎大家不吝嗇自己的見解,多多留言,也讓想學習或者正在學習DDD的園友少走一些彎路。
本文的源碼地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo5。
作者:Zachary_Fan
出處:http://www.cnblogs.com/Zachary-Fan/p/6069169.html