失血模型

  • 失血模型简单来说就是模型中只有属性的setter和getter方法,并且只是简单的赋值或直接返回属性值,是对一个实体类最简单的封装,其他所有的业务行为和数据存储由专门的服务类和DAO来完成。以一个Person类为例,包含姓名、生日、年龄的简单属性,只有判断今天是否生日和过生日(过生日后年龄会+1)两个行为。

    1. public class Person extends Entity {
    2. /**
    3. * 姓名
    4. */
    5. private String name;
    6. /**
    7. * 生日
    8. */
    9. private Date birthday;
    10. /**
    11. * 年龄
    12. */
    13. private Integer age;
    14. public String getName() {
    15. return name;
    16. }
    17. public void setName(String name) {
    18. this.name = name;
    19. }
    20. public Date getBirthday() {
    21. return birthday;
    22. }
    23. public void setBirthday(Date birthday) {
    24. this.birthday = birthday;
    25. }
    26. public Integer getAge() {
    27. return age;
    28. }
    29. public void setAge(Integer age) {
    30. this.age = age;
    31. }
    32. }
    33. public interface PersonService {
    34. /**
    35. * 判断今天是否生日
    36. */
    37. boolean isTodayBirthday(Long personId);
    38. /**
    39. * 生日快乐,长一岁,age+1
    40. */
    41. void happyBirthday(Long personId);
    42. }

贫血模型

  • 贫血模型是在失血模型的基础上聚合了对应领域范畴的业务领域行为,不仅仅是简单的setter/getter,但在行为过程中对领域对象的状态发生的变化只停留在内存层面,不关心其数据的持久化,即不依赖Repository/DAO,把数据持久化放在service中按需处理
    1. public class Person extends Entity {
    2. /**
    3. * 姓名
    4. */
    5. private String name;
    6. /**
    7. * 生日
    8. */
    9. private Date birthday;
    10. /**
    11. * 年龄
    12. */
    13. private Integer age;
    14. /**
    15. * 判断今天是否生日
    16. */
    17. public boolean isTodayBirthday() {
    18. SimpleDateFormat sdf = new SimpleDateFormat("MM-dd");
    19. return sdf.format(birthday).equals(sdf.format(new Date()));
    20. }
    21. /**
    22. * 生日快乐,长一岁,age+1
    23. */
    24. public void happyBirthday() {
    25. age++;
    26. }
    27. public String getName() {
    28. return name;
    29. }
    30. public void setName(String name) {
    31. if (StringUtils.isBlank(name)) {
    32. // throw a exception
    33. }
    34. this.name = name;
    35. }
    36. public Date getBirthday() {
    37. return birthday;
    38. }
    39. public void setBirthday(Date birthday) {
    40. if (birthday == null) {
    41. // throw a exception
    42. }
    43. this.birthday = birthday;
    44. }
    45. public Integer getAge() {
    46. return age;
    47. }
    48. public void setAge(Integer age) {
    49. if (age == null || age < 0) {
    50. // throw a exception
    51. }
    52. this.age = age;
    53. }
    54. }

充血模型

  • 充血模型是在贫血模型的基础上,依赖repository,在业务领域行为执行过程中也负责数据的持久化存储。
    1. public class Person extends Entity {
    2. @Autowired
    3. private PersonRepository personRepository;
    4. /**
    5. * 姓名
    6. */
    7. private String name;
    8. /**
    9. * 生日
    10. */
    11. private Date birthday;
    12. /**
    13. * 年龄
    14. */
    15. private Integer age;
    16. /**
    17. * 判断今天是否生日
    18. */
    19. public boolean isTodayBirthday() {
    20. SimpleDateFormat sdf = new SimpleDateFormat("MM-dd");
    21. return sdf.format(birthday).equals(sdf.format(new Date()));
    22. }
    23. /**
    24. * 生日快乐,长一岁,age+1
    25. */
    26. public void happyBirthday() {
    27. age++;
    28. // 数据持久化
    29. personRepository.update(this);
    30. }
    31. public String getName() {
    32. return name;
    33. }
    34. public void setName(String name) {
    35. if (StringUtils.isBlank(name)) {
    36. // throw a exception
    37. }
    38. this.name = name;
    39. }
    40. public Date getBirthday() {
    41. return birthday;
    42. }
    43. public void setBirthday(Date birthday) {
    44. if (birthday == null) {
    45. // throw a exception
    46. }
    47. this.birthday = birthday;
    48. }
    49. public Integer getAge() {
    50. return age;
    51. }
    52. public void setAge(Integer age) {
    53. if (age == null || age < 0) {
    54. // throw a exception
    55. }
    56. this.age = age;
    57. }
    58. }

胀血模型

  • 这种模式下连service都不需要了,所有的业务逻辑、数据存储都放到一个类中。
    1. public class Person extends Entity {
    2. @Autowired
    3. private PersonMapper personMapper;
    4. /**
    5. * 姓名
    6. */
    7. private String name;
    8. /**
    9. * 生日
    10. */
    11. private Date birthday;
    12. /**
    13. * 年龄
    14. */
    15. private Integer age;
    16. /**
    17. * 判断今天是否生日
    18. */
    19. public boolean isTodayBirthday() {
    20. SimpleDateFormat sdf = new SimpleDateFormat("MM-dd");
    21. return sdf.format(birthday).equals(sdf.format(new Date()));
    22. }
    23. /**
    24. * 生日快乐,长一岁,age+1
    25. */
    26. public void happyBirthday() {
    27. age++;
    28. modify();
    29. }
    30. /**
    31. * 查询
    32. */
    33. public static Person getById(Long id) {
    34. PersonDO personDO = personMapper.selectById(id);
    35. Person person = PersonConvertor.toPerson(personDO);
    36. return person;
    37. }
    38. /**
    39. * 新增
    40. */
    41. public static Person create(String name, Date birthday, Integer age) {
    42. // 这里省略了事务管理,入参校验等,相当于service层需要做的都在这里处理
    43. Person person = new Person();
    44. person.setName(name);
    45. person.setBirthday(birthday);
    46. person.setAge(age);
    47. personMapper.insert(person);
    48. return person;
    49. }
    50. /**
    51. * 修改
    52. */
    53. public void modify() {
    54. // 这里省略了事务管理,入参校验等,相当于service层需要做的都在这里处理
    55. personMapper.update(this);
    56. }
    57. public String getName() {
    58. return name;
    59. }
    60. public void setName(String name) {
    61. if (StringUtils.isBlank(name)) {
    62. // throw a exception
    63. }
    64. this.name = name;
    65. }
    66. public Date getBirthday() {
    67. return birthday;
    68. }
    69. public void setBirthday(Date birthday) {
    70. if (birthday == null) {
    71. // throw a exception
    72. }
    73. this.birthday = birthday;
    74. }
    75. public Integer getAge() {
    76. return age;
    77. }
    78. public void setAge(Integer age) {
    79. if (age == null || age < 0) {
    80. // throw a exception
    81. }
    82. this.age = age;
    83. }
    84. }

总体描述

  • 太轻 -> 数据驱动,而不够面向对象。
  • 太重 -> 不稳定,难以被高度复用。
  • 选用贫血模式还是充血模式,按照以下方案探讨:
    • 缩小依赖范围
      • 每一个微服务的内部的编码层面,倡导明确划分界限上下文,尽量以最小单元各司其职。减少不必要的耦合;从依赖范围来看,贫血模型的依赖范围是更小的。
    • 关注点分离
      • 关注点分离是对处理软件复杂性的指导原则,而单一职责是细致到指导如何面向对象设计一个类时的原则,他们都有一个共同的思想,就是解耦和增强内聚性,也就是我们经常说的“高内聚,低耦合”。
    • 可复用性
      • 领域之间尽量避免存在重复的业务逻辑这些方面就充分体现了可复用性这一点。
    • 向稳定的方向依赖
      • 试问如果一个软件系统,最核心的业务也扛不住变化的话,这个系统还有什么意义?