應用程序框架實戰十七:DDD分層架構之值對象(層超類型篇)

  上一篇介紹了值對象的基本概念,獲得了一些朋友的支持,另外也有一些朋友提出了不一樣意見。這實際上是很天然的事情,設計原本就充滿了各類可能性,沒有絕對正確的作法,只有更好的實踐。可是設計與實踐的好與壞,對於不一樣的人,以及處於不一樣的環境都有不一樣的詮釋,這是一個仁者見仁,智者見智的問題。DDD很是抽象,以致於它的每個概念,對於不一樣的人都有不一樣的見解,更況且基於DDD的.Net實踐,就更難分辨哪個用法更標準、更正宗。架構

  我對DDD的認識雖然還很膚淺,用得也很山寨,但這可能更加適合初步接觸DDD的朋友。仍是那句老話,你不是搞學術研究的,你並不須要挖掘DDD的學術價值,而是要把它切實的用到你的項目上,併產生回報。你不該該問對或錯,而應該多看看哪些東西對你真正起做用,一方面須要多學習DDD理論知識,另外一方面能夠多參考其它人的用法,並琢磨出一套適合本身習慣的架構。特別是初學DDD的朋友,這一點更加劇要,DDD水很深,盲目的採用某些你搞不懂的技術,只會增長負擔。你也不須要把DDD全部東西都用起來,使用DDD不是爲了趕時髦,若是某些東西讓你感受複雜,你先了解下就能夠了,把搞懂的東西加入你的工具箱,而後項目上慢慢體驗,時間稍長,你就能產生突破並從中受益。但你若是人云亦云,把注意力放到純概念和一些名詞術語上,把別人的經驗生搬硬套到本身的項目,因爲別人的思想你可能沒有真正搞懂,另外別人的項目需求、團隊水平、所用技術可能和你都不一樣,這樣可能致使你維護了一個龐大的架構,但卻沒有撈到一丁點好處。框架

  我這個系列重點不在DDD,而是如何搭建本身的應用程序框架。介紹DDD分層架構只是保證本系列的完整性,因此我不會很是詳細的介紹。另外不少朋友迫切須要示例,我在此回覆一下,本系列前期主要進行框架建設,包括一些公共操做類和層超類型,待底子打牢以後,我會向你們展現個人山寨DDD用法,以及如何經過應用程序框架快速開發項目。之因此不上來就搞一堆代碼,是但願你經過這個系列能真正受益,你不只須要知道框架怎麼用,更須要知道這玩意是怎麼弄出來的,以及重要代碼的思考和演化過程。因此我寫得可能很是囉嗦,我但願.Net初學者也能看懂。個人時間比較有限,更新時間不會太快,不過只要有人願意繼續看,我會堅持寫完它。ide

  下面回到正文上來,本篇將完成DDD值對象的層超類型開發,全部代碼都從網上搜集整理,若是你們有更好的請把你的代碼發上來供你們參考,另外最好詳細介紹你的代碼爲什麼更好,以避免你們憑空瞎猜。工具

  首先,在Util.Domains類庫中建立一個名爲ValueObjectBase的抽象類。性能

  考慮值對象的相等性測試,怎樣才能認爲兩個值對象是相等的?這能夠經過比較兩個值對象的全部屬性值都相等來判斷,換句話說,兩個值對象有任何一個屬性值不一樣,都不相等。咱們須要重寫Equals、GetHashCode 、==、!=這幾個方法或運算符。單元測試

  在相等性比較中,咱們能夠經過反射來獲取全部屬性,並一一比較,以測試相等性。另外,GetHashCode將各屬性值的哈希碼使用簡單的異或操做計算出來。若是以爲性能很差,子類能夠重寫相關實現。學習

  另外,值對象有時候須要建立一個副本,能夠增長一個克隆方法Clone,採用淺表複製進行建立,因爲值對象不可變,因此不一樣的值對象共享相同的屬性值就不是什麼問題。爲了讓Clone更加好用,可讓它建立出強類型的值對象,而不是一個object,這須要將值對象層超類型修改成泛型,將值對象做爲泛型參數傳遞到基類。測試

  另外,前面介紹的實體狀態輸出和驗證方法對值對象一樣適用,因此須要在實體和值對象層超類型之上再增長一個基類,命名爲DomainBase。ui

  因爲值對象層超類型比較簡單,我就簡要介紹到這,下面是相關代碼,若有疑問請留言。this

  測試樣例Address類代碼以下,爲了簡單,我只留下兩個屬性。 

namespace Util.Domains.Tests.Samples { /// <summary>
    /// 地址 /// </summary>
    public class Address : ValueObjectBase<Address> { /// <summary>
        /// 初始化地址 /// </summary>
        /// <param name="city">城市</param>
        /// <param name="street">街道</param>
        public Address( string city, string street ) { City = city; Street = street; } /// <summary>
        /// 城市 /// </summary>
        public string City { get; private set; } /// <summary>
        /// 街道 /// </summary>
        public string Street { get; private set; } } }

 

  值對象單元測試類ValueObjectBaseTest代碼以下。

 

using Microsoft.VisualStudio.TestTools.UnitTesting; using Util.Domains.Tests.Samples; namespace Util.Domains.Tests { /// <summary>
    /// 值對象測試 /// </summary>
 [TestClass] public class ValueObjectBaseTest { /// <summary>
        /// 地址1 /// </summary>
        private Address _address1; /// <summary>
        /// 地址2 /// </summary>
        private Address _address2; /// <summary>
        /// 地址3 /// </summary>
        private Address _address3; /// <summary>
        /// 測試初始化 /// </summary>
 [TestInitialize] public void TestInit() { _address1 = new Address("a","b"); _address2 = new Address( "a", "b" ); _address3 = new Address( "1", "" ); } /// <summary>
        /// 測試對象相等性 /// </summary>
 [TestMethod] public void TestEquals() { Assert.IsFalse( _address1.Equals( null ) ); Assert.IsFalse( _address1 == null ); Assert.IsFalse( null == _address1 ); Assert.IsFalse( _address1.Equals(new Test()) ); Assert.IsTrue( _address1.Equals( _address2 ), "_address1.Equals( _address2 )" ); Assert.IsTrue( _address1 == _address2, "_address1 == _address2" ); Assert.IsFalse( _address1 != _address2, "_address1 != _address2" ); Assert.IsFalse( _address1 == _address3, "_address1 == _address3" ); } /// <summary>
        /// 測試哈希 /// </summary>
 [TestMethod] public void TestGetHashCode() { Assert.IsTrue( _address1.GetHashCode() == _address2.GetHashCode(), "_address1.GetHashCode() == _address2.GetHashCode()" ); Assert.IsFalse( _address1.GetHashCode() == _address3.GetHashCode(), "_address1.GetHashCode() == _address3.GetHashCode()" ); } /// <summary>
        /// 測試克隆 /// </summary>
 [TestMethod] public void TestClone() { _address3 = _address1.Clone(); Assert.IsTrue( _address1 == _address3 ); } } }

 

  DomainBase代碼以下。

 

using System.Collections.Generic; using System.Text; using Util.Validations; namespace Util.Domains { /// <summary>
    /// 領域層頂級基類 /// </summary>
    public abstract class DomainBase { #region 構造方法

        /// <summary>
        /// 初始化領域層頂級基類 /// </summary>
        protected DomainBase() { _rules = new List<IValidationRule>(); _handler = new ValidationHandler(); } #endregion

        #region 字段

        /// <summary>
        /// 描述 /// </summary>
        private StringBuilder _description; /// <summary>
        /// 驗證規則集合 /// </summary>
        private readonly List<IValidationRule> _rules; /// <summary>
        /// 驗證處理器 /// </summary>
        private IValidationHandler _handler; #endregion

        #region ToString(輸出領域對象的狀態)

        /// <summary>
        /// 輸出領域對象的狀態 /// </summary>
        public override string ToString() { _description = new StringBuilder(); AddDescriptions(); return _description.ToString().TrimEnd().TrimEnd( ',' ); } /// <summary>
        /// 添加描述 /// </summary>
        protected virtual void AddDescriptions() { } /// <summary>
        /// 添加描述 /// </summary>
        protected void AddDescription( string description ) { if ( string.IsNullOrWhiteSpace( description ) ) return; _description.Append( description ); } /// <summary>
        /// 添加描述 /// </summary>
        protected void AddDescription<T>( string name, T value ) { if ( string.IsNullOrWhiteSpace( value.ToStr() ) ) return; _description.AppendFormat( "{0}:{1},", name, value ); } #endregion

        #region SetValidationHandler(設置驗證處理器)

        /// <summary>
        /// 設置驗證處理器 /// </summary>
        /// <param name="handler">驗證處理器</param>
        public void SetValidationHandler( IValidationHandler handler ) { if ( handler == null ) return; _handler = handler; } #endregion

        #region AddValidationRule(添加驗證規則)

        /// <summary>
        /// 添加驗證規則 /// </summary>
        /// <param name="rule">驗證規則</param>
        public void AddValidationRule( IValidationRule rule ) { if ( rule == null ) return; _rules.Add( rule ); } #endregion

        #region Validate(驗證)

        /// <summary>
        /// 驗證 /// </summary>
        public virtual void Validate() { var result = GetValidationResult(); HandleValidationResult( result ); } /// <summary>
        /// 獲取驗證結果 /// </summary>
        private ValidationResultCollection GetValidationResult() { var result = ValidationFactory.Create().Validate( this ); Validate( result ); foreach ( var rule in _rules ) result.Add( rule.Validate() ); return result; } /// <summary>
        /// 驗證並添加到驗證結果集合 /// </summary>
        /// <param name="results">驗證結果集合</param>
        protected virtual void Validate( ValidationResultCollection results ) { } /// <summary>
        /// 處理驗證結果 /// </summary>
        private void HandleValidationResult( ValidationResultCollection results ) { if ( results.IsValid ) return; _handler.Handle( results ); } #endregion } }

  ValueObjectBase代碼以下。 

using System; using System.Linq; namespace Util.Domains { /// <summary>
    /// 值對象 /// </summary>
    /// <typeparam name="TValueObject">值對象類型</typeparam>
    public abstract class ValueObjectBase<TValueObject> : DomainBase, IEquatable<TValueObject> where TValueObject : ValueObjectBase<TValueObject> { #region Equals(相等性比較)

        /// <summary>
        /// 相等性比較 /// </summary>
        public bool Equals( TValueObject other ) { return this == other; } /// <summary>
        /// 相等性比較 /// </summary>
        public override bool Equals( object other ) { return Equals( other as TValueObject ); } #endregion

        #region ==(相等性比較)

        /// <summary>
        /// 相等性比較 /// </summary>
        public static bool operator ==( ValueObjectBase<TValueObject> valueObject1, ValueObjectBase<TValueObject> valueObject2 ) { if ( (object)valueObject1 == null && (object)valueObject2 == null ) return true; if ( (object)valueObject1 == null || (object)valueObject2 == null ) return false; if ( valueObject1.GetType() != valueObject2.GetType() ) return false; var properties = valueObject1.GetType().GetProperties(); return properties.All( property => property.GetValue( valueObject1 ) == property.GetValue( valueObject2 ) ); } #endregion

        #region !=(不相等比較)

        /// <summary>
        /// 不相等比較 /// </summary>
        public static bool operator !=( ValueObjectBase<TValueObject> valueObject1, ValueObjectBase<TValueObject> valueObject2 ) { return !( valueObject1 == valueObject2 ); } #endregion

        #region GetHashCode(獲取哈希)

        /// <summary>
        /// 獲取哈希 /// </summary>
        public override int GetHashCode() { var properties = GetType().GetProperties(); return properties.Select( property => property.GetValue( this ) ) .Where( value => value != null ) .Aggregate( 0, ( current, value ) => current ^ value.GetHashCode() ); } #endregion

        #region Clone(克隆副本)

        /// <summary>
        /// 克隆副本 /// </summary>
        public virtual TValueObject Clone() { return (TValueObject)MemberwiseClone(); } #endregion } }

  .Net應用程序框架交流QQ羣: 386092459,歡迎有興趣的朋友加入討論。

  謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/xiadao521/

  下載地址:http://files.cnblogs.com/xiadao521/Util.2014.11.27.1.rar

相關文章
相關標籤/搜索