DDD分层架构之值对象

张军 2650 0

我对DDD的认识虽然还很肤浅,用得也很山寨,但这可能更加适合初步接触DDD的朋友。还是那句老话,你不是搞学术研究的,你并不需要挖掘DDD的学术价值,而是要把它切实的用到你的项目上,并产生回报。你不应该问对或错,而应该多看看哪些东西对你真正起作用,一方面需要多学习DDD理论知识,另一方面可以多参考其它人的用法,并琢磨出一套适合自己习惯的架构。特别是初学DDD的朋友,这一点更加重要,DDD水很深,盲目的采用某些你搞不懂的技术,只会增加负担。你也不需要把DDD所有东西都用起来,使用DDD不是为了赶时髦,如果某些东西让你感觉复杂,你先了解下就可以了,把搞懂的东西加入你的工具箱,然后项目上慢慢体验,时间稍长,你就能产生突破并从中受益。但你如果人云亦云,把注意力放到纯概念和一些名词术语上,把别人的经验生搬硬套到自己的项目,由于别人的思想你可能没有真正搞懂,另外别人的项目需求、团队水平、所用技术可能和你都不同,这样可能导致你维护了一个庞大的架构,但却没有捞到一丁点好处。

我这个系列重点不在DDD,而是如何搭建自己的应用程序框架。介绍DDD分层架构只是保证本系列的完整性,所以我不会非常详细的介绍。另外很多朋友迫切需要示例,我在此回复一下,本系列前期主要进行框架建设,包括一些公共操作类和层超类型,待底子打牢之后,我会向大家展示我的山寨DDD用法,以及如何通过应用程序框架快速开发项目。之所以不上来就搞一堆代码,是希望你通过这个系列能真正受益,你不仅需要知道框架怎么用,更需要知道这玩意是怎么弄出来的,以及重要代码的思考和演化过程。所以我写得可能非常啰嗦,我希望.Net初学者也能看懂。我的时间比较有限,更新时间不会太快,不过只要有人愿意继续看,我会坚持写完它。

下面回到正文上来,本篇将完成DDD值对象的层超类型开发,所有代码都从网上搜集整理,如果大家有更好的请把你的代码发上来供大家参考,另外最好详细介绍你的代码为何更好,以免大家凭空瞎猜。

首先,在Util.Domains类库中创建一个名为ValueObjectBase的抽象类。

考虑值对象的相等性测试,怎样才能认为两个值对象是相等的?这可以通过比较两个值对象的所有属性值都相等来判断,换句话说,两个值对象有任何一个属性值不同,都不相等。我们需要重写Equals、GetHashCode 、==、!=这几个方法或运算符。

在相等性比较中,我们可以通过反射来获取所有属性,并一一比较,以测试相等性。另外,GetHashCode将各属性值的哈希码使用简单的异或操作计算出来。如果觉得性能不好,子类可以重写相关实现。

另外,值对象有时候需要创建一个副本,可以增加一个克隆方法Clone,采用浅表复制进行创建,由于值对象不可变,所以不同的值对象共享相同的属性值就不是什么问题。为了让Clone更加好用,可以让它创建出强类型的值对象,而不是一个object,这需要将值对象层超类型修改为泛型,将值对象作为泛型参数传递到基类。

另外,前面介绍的实体状态输出和验证方法对值对象同样适用,所以需要在实体和值对象层超类型之上再增加一个基类,命名为DomainBase。

由于值对象层超类型比较简单,我就简要介绍到这,下面是相关代码,如有疑问请留言。

测试样例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

    }

}



更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论