• 领域驱动(DDD)实战1


    领域驱动(DDD)实战---月份类YearMonth

    Net中有一个DateTime结构类,涉及时间和日期,这个类大量使用。可是,他的名称已经显著的表明他是表达某个具体的时刻。当我们不需要每天的具体时间时,如:我的程序逻辑仅仅需要年月(发工资的周期?),这个DateTime显得有些累赘,甚至不合用。 
    一般人们解决的方式,仍然使用DateTime而从数据上,设置hour,mintue等等为0。 然而,这与DDD的理念相背,名称有与含义有偏差,另外,数据一致性的维护,散布在各个角落,如,保证日期始终为1,小时,分钟为0。另外,与月份相关的功能,如:得到下一个月份,要么用DateTime本身的功能(AddMonths),要么提炼出一个Utitlies出来。 前者,需要开发者时刻重复DateTime到YearMonth的映射逻辑,后者是个反模式。 (本文版权属于© 2012 - 2013 予沁安

    这里,我创建出一个基本类型YearMonth,可以作为代码的基本砖块。

    [代码] 用Extension的方式,来增强代码流畅性和可读性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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;
            }
    }

    [代码] 从测试看功能:公用的测试基类,很简单,就是声明一个YearMonth对象做测试

    1
    2
    3
    4
    5
    public class YearMonthSpecs
        {
            protected static YearMonth subject;
     
        }

    [代码] 通过构造器,创建YearMonth对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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);
     
        }

    [代码] 通过流畅接口创建YearMonth: 2012.year(3)。你还可以自己定制为: 2012.年(3)

    1
    2
    3
    4
    5
    public class When_create_year_month_through_fluent_interface
        {
            private It should_create_year_month_properly =
                () => 2012.year(3).ShouldEqual(new YearMonth(2012, 3));
        }

    [代码] 通过字符串创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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模式,特别处理:世界末日的下一个月还是世界末日,创世纪的上一个月还是创世纪

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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结构类型的完整代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    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);
            }
        }
    }


    (本文版权属于© 2012 - 2013 予沁安 | 转载请注明作者和出处

  • 相关阅读:
    Linux —— 查询端口对应进程,并杀死
    Mac m1 安装 swoole
    Linux——列出所有打开的端口
    Swoole——创建TCP服务
    PHP——16进制转10进制
    PHP——SWOOLE发送串口指令
    PHP实现Modbus RTU CRC16校验
    Docker之操作系统Alpine
    jmeter 动态参数
    Docker部署Apache Airflow
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3082276.html
Copyright © 2020-2023  润新知