領(lǐng)域驅(qū)動(dòng)(DDD)實(shí)戰(zhàn)---月份類(lèi)YearMonth
Net中有一個(gè)DateTime結(jié)構(gòu)類(lèi),涉及時(shí)間和日期,這個(gè)類(lèi)大量使用??墒?,他的名稱(chēng)已經(jīng)顯著的表明他是表達(dá)某個(gè)具體的時(shí)刻。當(dāng)我們不需要每天的具體時(shí)間時(shí),如:我的程序邏輯僅僅需要年月(發(fā)工資的周期?),這個(gè)DateTime顯得有些累贅,甚至不合用。
一般人們解決的方式,仍然使用DateTime而從數(shù)據(jù)上,設(shè)置hour,mintue等等為0。 然而,這與DDD的理念相背,名稱(chēng)有與含義有偏差,另外,數(shù)據(jù)一致性的維護(hù),散布在各個(gè)角落,如,保證日期始終為1,小時(shí),分鐘為0。另外,與月份相關(guān)的功能,如:得到下一個(gè)月份,要么用DateTime本身的功能(AddMonths),要么提煉出一個(gè)Utitlies出來(lái)。 前者,需要開(kāi)發(fā)者時(shí)刻重復(fù)DateTime到Y(jié)earMonth的映射邏輯,后者是個(gè)反模式。 (本文版權(quán)屬于? 2012 - 2013 予沁安)
這里,我創(chuàng)建出一個(gè)基本類(lèi)型YearMonth,可以作為代碼的基本磚塊。
[代碼] 用Extension的方式,來(lái)增強(qiáng)代碼流暢性和可讀性
public static class YearMonthExtension
{
public static YearMonth year(this int year, int month)
{
return new YearMonth(year, month);
}
public static bool is_later_than(this YearMonth left, YearMonth right) {
return left.CompareTo(right) > 0;
}
public static bool is_ealier_than(this YearMonth left, YearMonth right) {
return left.CompareTo(right) < 0;
}
public static YearMonth get_ealier(this YearMonth left, YearMonth right)
{
if (left.is_ealier_than(right))
return left;
return right;
}
public static YearMonth get_later(this YearMonth left, YearMonth right)
{
if (left.is_later_than(right))
return left;
return right;
}
}
[代碼] 從測(cè)試看功能:公用的測(cè)試基類(lèi),很簡(jiǎn)單,就是聲明一個(gè)YearMonth對(duì)象做測(cè)試
public class YearMonthSpecs
{
protected static YearMonth subject;
}
[代碼] 通過(guò)構(gòu)造器,創(chuàng)建YearMonth對(duì)象
public class When_init_by_year_month
:YearMonthSpecs
{
private Because of =
() => subject = new YearMonth(2011,3);
private It year_should_set_properly =
() => subject.Year.ShouldEqual(2011);
private It month_should_set_properly =
() => subject.Month.ShouldEqual(3);
}
[代碼] 通過(guò)流暢接口創(chuàng)建YearMonth: 2012.year(3)。你還可以自己定制為: 2012.年(3)
public class When_create_year_month_through_fluent_interface
{
private It should_create_year_month_properly =
() => 2012.year(3).ShouldEqual(new YearMonth(2012, 3));
}
[代碼] 通過(guò)字符串創(chuàng)建
public class When_init_by_string : YearMonthSpecs
{
private Because of =
() => subject = new YearMonth("2011年01月");
private It year_should_set_properly =
() =>
{
subject.Year.ShouldEqual(2011);
subject.Month.ShouldEqual(1);
};
}
[代碼] Special Case模式,特別處理:世界末日的下一個(gè)月還是世界末日,創(chuàng)世紀(jì)的上一個(gè)月還是創(chuàng)世紀(jì)
private It far_past_last_month_is_still_far_past =
() => YearMonth.FarPast.get_last().ShouldEqual(YearMonth.FarPast);
private It far_past_next_month_is_still_far_past =
() => YearMonth.FarPast.get_next().ShouldEqual(YearMonth.FarPast);
private It far_future_last_month_is_stil_far_future =
() => YearMonth.FarFuture.get_last().ShouldEqual(YearMonth.FarFuture);
private It far_future_next_month_is_stil_far_future =
() => YearMonth.FarFuture.get_next().ShouldEqual(YearMonth.FarFuture);
YearMonth結(jié)構(gòu)類(lèi)型的完整代碼
using System;
using Skight.Arch.Domain.Interfaces;
namespace Skight.Arch.Domain.Entities
{
public struct YearMonth : IEquatable<YearMonth>, IComparable<YearMonth>
{
private readonly int ticks;
private readonly int year;
private readonly int month;
private const int MONTHS_PER_YEAR=12;
public static YearMonth FarPast = new YearMonth(0,1);
public static YearMonth FarFuture = new YearMonth(9999,12);
#region Constructors by ticks, year/month and datetime
internal YearMonth(int ticks)
{
this.ticks = ticks;
int remain;
year = Math.DivRem(ticks-1, MONTHS_PER_YEAR, out remain);
month = remain + 1;
}
public YearMonth(int year, int month)
:this(year*MONTHS_PER_YEAR + month){}
public YearMonth(DateTime date_time)
:this(date_time.Year,date_time.Month){}
public YearMonth(string yearMonth):this(int.Parse(yearMonth.Substring(0, 4))
,int.Parse(yearMonth.Substring(5, 2)))
{}
#endregion
public int Year { get { return year; } }
public int Month { get { return month; } }
public DateTime as_date_Time()
{
return new DateTime(Year,Month,1);
}
public override string ToString()
{
return string.Format("{0}年{1}月", year.ToString("0000"), month.ToString("00"));
}
#region Euqals and Compare
public bool Equals(YearMonth other)
{
return other.ticks == ticks;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof (YearMonth)) return false;
return Equals((YearMonth) obj);
}
public override int GetHashCode()
{
return ticks;
}
public int CompareTo(YearMonth other)
{
return ticks.CompareTo(other.ticks);
}
#endregion
#region Discrete interface
public YearMonth get_last()
{
if (Equals(FarPast))
return FarPast;
if (Equals(FarFuture))
return FarFuture;
return new YearMonth(ticks - 1);
}
public YearMonth get_last(int Dvalue)
{
if (Equals(FarPast))
return FarPast;
if (Equals(FarFuture))
return FarFuture;
return new YearMonth(ticks - Dvalue);
}
public YearMonth get_next()
{
if (Equals(FarPast))
return FarPast;
if (Equals(FarFuture))
return FarFuture;
return new YearMonth(ticks + 1);
}
public YearMonth get_next(int DValue)
{
if (Equals(FarPast))
return FarPast;
if (Equals(FarFuture))
return FarFuture;
return new YearMonth(ticks + DValue);
}
#endregion
public static implicit operator DateTime(YearMonth year_month)
{
return year_month.as_date_Time();
}
public static implicit operator YearMonth(DateTime date_time)
{
return new YearMonth(date_time);
}
}
}
皓月碧空,漫野如洗,行往卓越的路上

浙公網(wǎng)安備 33010602011771號(hào)