上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见。这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好的实践。但是设计与实践的好与坏,对于不同的人,以及处于不同的环境都有不同的诠释,这是一个仁者见仁,智者见智的问题。DDD非常抽象,以至于它的每一个概念,对于不同的人都有不同的看法,更何况基于DDD的.Net实践,就更难分辨哪一个用法更标准、更正宗。

  我对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( "", "" );
} /// <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( , ( 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

应用程序框架实战十七:DDD分层架构之值对象(层超类型篇)的更多相关文章

  1. 应用程序框架实战十三:DDD分层架构之我见

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  2. 应用程序框架实战十三:DDD分层架构之我见(转)

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  3. 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)

    前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...

  4. 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)

    前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...

  5. DDD分层架构之值对象(介绍篇)

    DDD分层架构之值对象(介绍篇) 前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使 ...

  6. DDD分层架构之值对象(层超类型篇)

    DDD分层架构之值对象(层超类型篇) 上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见.这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好 ...

  7. DDD分层架构之仓储

    DDD分层架构之仓储(层超类型基础篇) 前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操 ...

  8. 【转载】DDD分层架构的三种模式

    引言 在讨论DDD分层架构的模式之前,我们先一起回顾一下DDD和分层架构的相关知识. DDD DDD(Domain Driven Design,领域驱动设计)作为一种软件开发方法,它可以帮助我们设计高 ...

  9. DDD分层架构的三种模式

    引言 在讨论DDD分层架构的模式之前,我们先一起回顾一下DDD和分层架构的相关知识. DDD DDD(Domain Driven Design,领域驱动设计)作为一种软件开发方法,它可以帮助我们设计高 ...

随机推荐

  1. 简单的jquery选项卡效果

    html部分 <ul class="tab"> <li>最新</li> <li class="cur">热门&l ...

  2. jquery mobile 问问多多

    jquery mobile  问题多多,兼容性太差.android4.1下完全崩溃.以后再也不用jquery mobile了

  3. 关于i++,++i 的理解

    由于经常有同学在遇到 i++.++i 时犯困难.所以举例说明一下: int i = 20;        int sum = i++ * 30; //这个等式中 i = 20        int s ...

  4. java并发之volatile

    volatile是轻量级的synchronized,它在多处理器应用开发中保证了共享变量的“可见性”(可见性指当一个线程修改共享变量后,其它线程可以看到这个修改). volatile如果使用合理会比s ...

  5. 浏览器访问Servlet

    浏览器访问Servlet1.指定一个Servlet路径(                     在web.xml :              <servlet>             ...

  6. mac远程桌面连接windows 8.1 update,提示: 远程桌面连接无法验证您希望连接的计算机的身份

    在网上找到解决方案: SolutionEnable RDP security layer in Group Policy on the machine: Verify that the firewal ...

  7. Centos 使用YUM安装MariaDB

    1.在 /etc/yum.repos.d/ 下建立 MariaDB.repo,内容如下: [azureuser@mono etc]$ cd /etc/yum.repos.d [azureuser@mo ...

  8. Hadoop学习笔记—11.MapReduce中的排序和分组

    一.写在之前的 1.1 回顾Map阶段四大步骤 首先,我们回顾一下在MapReduce中,排序和分组在哪里被执行: 从上图中可以清楚地看出,在Step1.4也就是第四步中,需要对不同分区中的数据进行排 ...

  9. [.net 面向对象程序设计深入](4)MVC 6 —— 谈谈MVC的版本变迁及新版本6.0发展方向

    [.net 面向对象程序设计深入](4)MVC 6 ——谈谈MVC的版本变迁及新版本6.0发展方向 1.关于MVC 在本篇中不再详细介绍MVC的基础概念,这些东西百度要比我写的全面多了,MVC从1.0 ...

  10. lua中实现异步资源读写

    同样还是更新方面的需求,当我们检测到版本是新安装的以后,要进行upd目录清除.如果使用os.execute执行 rm -rf ooxx 是非常快的但由于os.execute一旦报错,那整个lua进程就 ...