我對(duì)DDD的認(rèn)識(shí)雖然還很膚淺,用得也很山寨,但這可能更加適合初步接觸DDD的朋友。還是那句老話,你不是搞學(xué)術(shù)研究的,你并不需要挖掘DDD的學(xué)術(shù)價(jià)值,而是要把它切實(shí)的用到你的項(xiàng)目上,并產(chǎn)生回報(bào)。你不應(yīng)該問(wèn)對(duì)或錯(cuò),而應(yīng)該多看看哪些東西對(duì)你真正起作用,一方面需要多學(xué)習(xí)DDD理論知識(shí),另一方面可以多參考其它人的用法,并琢磨出一套適合自己習(xí)慣的架構(gòu)。特別是初學(xué)DDD的朋友,這一點(diǎn)更加重要,DDD水很深,盲目的采用某些你搞不懂的技術(shù),只會(huì)增加負(fù)擔(dān)。你也不需要把DDD所有東西都用起來(lái),使用DDD不是為了趕時(shí)髦,如果某些東西讓你感覺(jué)復(fù)雜,你先了解下就可以了,把搞懂的東西加入你的工具箱,然后項(xiàng)目上慢慢體驗(yàn),時(shí)間稍長(zhǎng),你就能產(chǎn)生突破并從中受益。但你如果人云亦云,把注意力放到純概念和一些名詞術(shù)語(yǔ)上,把別人的經(jīng)驗(yàn)生搬硬套到自己的項(xiàng)目,由于別人的思想你可能沒(méi)有真正搞懂,另外別人的項(xiàng)目需求、團(tuán)隊(duì)水平、所用技術(shù)可能和你都不同,這樣可能導(dǎo)致你維護(hù)了一個(gè)龐大的架構(gòu),但卻沒(méi)有撈到一丁點(diǎn)好處。
我這個(gè)系列重點(diǎn)不在DDD,而是如何搭建自己的應(yīng)用程序框架。介紹DDD分層架構(gòu)只是保證本系列的完整性,所以我不會(huì)非常詳細(xì)的介紹。另外很多朋友迫切需要示例,我在此回復(fù)一下,本系列前期主要進(jìn)行框架建設(shè),包括一些公共操作類(lèi)和層超類(lèi)型,待底子打牢之后,我會(huì)向大家展示我的山寨DDD用法,以及如何通過(guò)應(yīng)用程序框架快速開(kāi)發(fā)項(xiàng)目。之所以不上來(lái)就搞一堆代碼,是希望你通過(guò)這個(gè)系列能真正受益,你不僅需要知道框架怎么用,更需要知道這玩意是怎么弄出來(lái)的,以及重要代碼的思考和演化過(guò)程。所以我寫(xiě)得可能非常啰嗦,我希望.Net初學(xué)者也能看懂。我的時(shí)間比較有限,更新時(shí)間不會(huì)太快,不過(guò)只要有人愿意繼續(xù)看,我會(huì)堅(jiān)持寫(xiě)完它。
下面回到正文上來(lái),本篇將完成DDD值對(duì)象的層超類(lèi)型開(kāi)發(fā),所有代碼都從網(wǎng)上搜集整理,如果大家有更好的請(qǐng)把你的代碼發(fā)上來(lái)供大家參考,另外最好詳細(xì)介紹你的代碼為何更好,以免大家憑空瞎猜。
首先,在Util.Domains類(lèi)庫(kù)中創(chuàng)建一個(gè)名為ValueObjectBase的抽象類(lèi)。
考慮值對(duì)象的相等性測(cè)試,怎樣才能認(rèn)為兩個(gè)值對(duì)象是相等的?這可以通過(guò)比較兩個(gè)值對(duì)象的所有屬性值都相等來(lái)判斷,換句話說(shuō),兩個(gè)值對(duì)象有任何一個(gè)屬性值不同,都不相等。我們需要重寫(xiě)Equals、GetHashCode 、==、!=這幾個(gè)方法或運(yùn)算符。
在相等性比較中,我們可以通過(guò)反射來(lái)獲取所有屬性,并一一比較,以測(cè)試相等性。另外,GetHashCode將各屬性值的哈希碼使用簡(jiǎn)單的異或操作計(jì)算出來(lái)。如果覺(jué)得性能不好,子類(lèi)可以重寫(xiě)相關(guān)實(shí)現(xiàn)。
另外,值對(duì)象有時(shí)候需要?jiǎng)?chuàng)建一個(gè)副本,可以增加一個(gè)克隆方法Clone,采用淺表復(fù)制進(jìn)行創(chuàng)建,由于值對(duì)象不可變,所以不同的值對(duì)象共享相同的屬性值就不是什么問(wèn)題。為了讓Clone更加好用,可以讓它創(chuàng)建出強(qiáng)類(lèi)型的值對(duì)象,而不是一個(gè)object,這需要將值對(duì)象層超類(lèi)型修改為泛型,將值對(duì)象作為泛型參數(shù)傳遞到基類(lèi)。
另外,前面介紹的實(shí)體狀態(tài)輸出和驗(yàn)證方法對(duì)值對(duì)象同樣適用,所以需要在實(shí)體和值對(duì)象層超類(lèi)型之上再增加一個(gè)基類(lèi),命名為DomainBase。
由于值對(duì)象層超類(lèi)型比較簡(jiǎn)單,我就簡(jiǎn)要介紹到這,下面是相關(guān)代碼,如有疑問(wèn)請(qǐng)留言。
測(cè)試樣例Address類(lèi)代碼如下,為了簡(jiǎn)單,我只留下兩個(gè)屬性。
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; }
}
}
值對(duì)象單元測(cè)試類(lèi)ValueObjectBaseTest代碼如下。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Util.Domains.Tests.Samples;
namespace Util.Domains.Tests {
/// <summary>
/// 值對(duì)象測(cè)試
/// </summary>
[TestClass]
public class ValueObjectBaseTest {
/// <summary>
/// 地址1
/// </summary>
private Address _address1;
/// <summary>
/// 地址2
/// </summary>
private Address _address2;
/// <summary>
/// 地址3
/// </summary>
private Address _address3;
/// <summary>
/// 測(cè)試初始化
/// </summary>
[TestInitialize]
public void TestInit() {
_address1 = new Address("a","b");
_address2 = new Address( "a", "b" );
_address3 = new Address( "1", "" );
}
/// <summary>
/// 測(cè)試對(duì)象相等性
/// </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>
/// 測(cè)試哈希
/// </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>
/// 測(cè)試克隆
/// </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>
/// 領(lǐng)域?qū)禹敿?jí)基類(lèi)
/// </summary>
public abstract class DomainBase {
#region 構(gòu)造方法
/// <summary>
/// 初始化領(lǐng)域?qū)禹敿?jí)基類(lèi)
/// </summary>
protected DomainBase() {
_rules = new List<IValidationRule>();
_handler = new ValidationHandler();
}
#endregion
#region 字段
/// <summary>
/// 描述
/// </summary>
private StringBuilder _description;
/// <summary>
/// 驗(yàn)證規(guī)則集合
/// </summary>
private readonly List<IValidationRule> _rules;
/// <summary>
/// 驗(yàn)證處理器
/// </summary>
private IValidationHandler _handler;
#endregion
#region ToString(輸出領(lǐng)域?qū)ο蟮臓顟B(tài))
/// <summary>
/// 輸出領(lǐng)域?qū)ο蟮臓顟B(tài)
/// </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(設(shè)置驗(yàn)證處理器)
/// <summary>
/// 設(shè)置驗(yàn)證處理器
/// </summary>
/// <param name="handler">驗(yàn)證處理器</param>
public void SetValidationHandler( IValidationHandler handler ) {
if ( handler == null )
return;
_handler = handler;
}
#endregion
#region AddValidationRule(添加驗(yàn)證規(guī)則)
/// <summary>
/// 添加驗(yàn)證規(guī)則
/// </summary>
/// <param name="rule">驗(yàn)證規(guī)則</param>
public void AddValidationRule( IValidationRule rule ) {
if ( rule == null )
return;
_rules.Add( rule );
}
#endregion
#region Validate(驗(yàn)證)
/// <summary>
/// 驗(yàn)證
/// </summary>
public virtual void Validate() {
var result = GetValidationResult();
HandleValidationResult( result );
}
/// <summary>
/// 獲取驗(yàn)證結(jié)果
/// </summary>
private ValidationResultCollection GetValidationResult() {
var result = ValidationFactory.Create().Validate( this );
Validate( result );
foreach ( var rule in _rules )
result.Add( rule.Validate() );
return result;
}
/// <summary>
/// 驗(yàn)證并添加到驗(yàn)證結(jié)果集合
/// </summary>
/// <param name="results">驗(yàn)證結(jié)果集合</param>
protected virtual void Validate( ValidationResultCollection results ) {
}
/// <summary>
/// 處理驗(yàn)證結(jié)果
/// </summary>
private void HandleValidationResult( ValidationResultCollection results ) {
if ( results.IsValid )
return;
_handler.Handle( results );
}
#endregion
}
}
ValueObjectBase代碼如下。
using System;
using System.Linq;
namespace Util.Domains {
/// <summary>
/// 值對(duì)象
/// </summary>
/// <typeparam name="TValueObject">值對(duì)象類(lèi)型</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
}
}
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
