• 领域驱动设计DDD:贫血模型和充血模型(比较重要)


    学习这章的原因:实际工作项目中有些实体内写了方法有些只是纯粹的属性定义。正好领域驱动设计看到了贫血和充血模型。记录一下。

    说实话虽然敏捷开发中大家不太关心你的功能设计背后的结构逻辑,但是本着对自己负责的态度,这部分需要特别注意

    1. - 普通程序员写hello word 直接print
    2. - 高级程序员写hello word 各种设计模式各种可拓展最后输出hello word
    3. - 技术专家写hello word ,直接打印hello word

     插一句:充血模型最大的困难是如何映射到数据库,这边需要看一下杨中科P165之后的教程。

    Part6-10.NET的充血模型与贫血模型_哔哩哔哩_bilibili

    1、贫血模型:一个类中只有属性或者成员变量,没有方法。

    2、充血模型:一个类中既有属性、成员变量,也有方法。

    举个🌰栗子:

    需求:定义一个类保存用户的用户名、密码、积分;用户必须具有用户名;为了保证安全,密码采用密码的散列值保存;用户的初始积分为10分;每次登录成功奖励5个积分,每次登录失败扣3个积分。 

    贫血模型

    1. class User
    2. {
    3. public string UserName { get; set; }//用户名
    4. public string PasswordHash { get; set; }//密码的散列值
    5. public int Credit { get; set; }//积分
    6. }

    功能实现

    最大的缺点:领域知识/业务逻辑的高耦合!真TM的臃肿!

    1. User u1 = new User(); u1.UserName = "yzk"; u1.Credit = 10;
    2. u1.PasswordHash = HashHelper.Hash("123456");//计算密码的散列值
    3. string pwd = Console.ReadLine();
    4. if(HashHelper.Hash(pwd)==u1.PasswordHash)
    5. {
    6. u1.Credit += 5;//登录增加5个积分
    7. Console.WriteLine("登录成功");
    8. }
    9. Else
    10. {
    11. if (u1.Credit < 3)
    12. Console.WriteLine("积分不足,无法扣减");
    13. else
    14. {
    15. u1.Credit -= 3;//登录失败,则扣3个积分
    16. Console.WriteLine("登录成功");
    17. }
    18. Console.WriteLine("登录失败");
    19. }

    充血模型

    不要用实现方式1,用2!

     意思是:参数名要和属性名保持一致!!!为什么!因为EF会跳过原本的get和set,去直接读属性!

     

     

    1. class User
    2. {//看这边傻逼
    3. public string UserName { get; init; } // init表示只能初始化时候设置
    4. public int Credit { get; private set; }//private 无法在外部被修改 只能内部方法修改
    5. private string? passwordHash;
    6. public User(string userName)//通过构造的参数,确保创建时候name一定不为空
    7. {
    8. this.UserName = userName;
    9. this.Credit =10;
    10. }
    11. public void ChangePassword(string newValue)
    12. {
    13. if(newValue.Length<6)
    14. {
    15. throw new Exception("密码太短");
    16. }
    17. this.passwordHash =Hash(newValue);
    18. }
    19. public bool CheckPassword(string password)
    20. {
    21. string hash = HashHelper.Hash(password);
    22. return passwordHash== hash;
    23. }
    24. public void DeductCredits(int delta)
    25. {
    26. if(delta<=0)
    27. {
    28. throw new Exception("额度不能为负值");
    29. }
    30. this.Credit -= delta;
    31. }
    32. public void AddCredits(int delta)
    33. {
    34. this.Credit += delta;
    35. }
    36. }

     看一下 private string? passwordHash; 不属于 属性 的 成员变量 

     

     

     

    功能实现

    1. User u1 = new User("yzk");
    2. u1.ChangePassword("123456");
    3. string pwd = Console.ReadLine();
    4. if (u1.CheckPassword(pwd))
    5. {
    6. u1.AddCredits(5);
    7. Console.WriteLine("登录成功");
    8. }
    9. else
    10. {
    11. Console.WriteLine("登录失败");
    12. }

    是所有的场景都适合使用充血模型吗?

    使用充血模型也就是使用基于充血模型的DDD的开发模式,上文也一再强调,充血模型也是定义模式复杂,设计难等,代码开发量也许时其他模型的多,其主要原因还是设计起来难。那就是说如果我们设计一个很简单的业务逻辑,那我们还需要这么复杂的设计思想吗? 并且这个业务在后续的迭代也不变复杂,那我个人认为我们就使用我们的基于贫血模型的面向过程的编程思想。简单的东西何必复杂化呢。

    当然我们在进行一个复杂的业务场景,那就需要进行基于充血模型的DDD(领域驱动模型)开发模式了。其实DDD的开发模式也就是充分的遵循OOP发三大特性(或者四大特性,封装,继承,多态,(抽象)),如果是贫血模型的面向过程编程那到最后的结果就是点练成线,由线变成网,密密麻麻不可维护。所以说复杂业务逻辑基于充血模型的进行开发。但是也会是有问题的那就是类膨胀,一个类有很多代码。这个还是可以解决的,那就是通过设计模式,和业务逻辑细分进行解决。

  • 相关阅读:
    【tg】6: MediaManager的主要功能
    .NET高级面试指南专题十七【 策略模式模式介绍,允许在运行时选择算法的行为】
    调试篇——断点与单步
    uniapp uni-list-item @click,uniapp uni-list-item带参数跳转
    微信小程序python+uniapp+hbuiderx宠物美容用品商城领养寄养系统i843n
    基于ClickHouse解决活动海量数据问题
    MySQL索引原理以及SQL优化
    解析java中的字面量和字符类型
    数据结构与算法(四):栈与队列
    《安卓逆向》Magisk的编译踩坑记录-安装方法-分享魔改后的Magisk过root检测方法
  • 原文地址:https://blog.csdn.net/dongnihao/article/details/126574431