设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 IT综合资讯 查看内容

贫血领域模型是如何导致糟糕的软件产生

2013-10-23 11:26| 发布者: joejoe0332| 查看: 1430| 评论: 0|原作者: 季文昊, 雅典娜拉|来自: oschina

摘要:   使用贫血领域模型通常被认为是一种反模式,因为它鼓励程序员无意义地重复编写代码。下面我将简短(而琐碎)地用一个例子来阐述这个是如何产生的。我们可以通过细致的规划以及严格的编码规范来避免其发生,但是同 ...



  第四步:逻辑变更


  从长远来看,越简单的代码会变的越复杂。在这个迭代后期,我们的开发者在sendReminderMessage方法中添加了一些更复杂的逻辑(预处理用户名和校验邮箱地址)。


01public void sendReminderMessage(final IUser user) {
02   String formattedUserName = formatUserNameForMessage(user.getName());
03   String reminderMessage = this.messageGeneratorService.generateReminderMessage(formattedUserName);
04   if (isEmailAddressValid(user.getEmailAddress()) {
05      this.mailService.sendMessage(user.getEmailAddress(), reminderMessage);
06   }
07}
08 
09public boolean isEmailAddressValid(final String emailAddress) { // 是否邮箱地址有效
10   return emailAddress.contains('@');
11}
12 
13public String formatUserNameForMessage(final String userName) { // 为消息格式化用户名
14   return userName.toUpperCase();
15}

  我们现在有了sendReminderMessage方法的新版本(虽然是一个很简陋的验证系统),使得(曾经相似的)UserReminderService变得相当不同。


  哪里错了呢?


  用来给向用户发送消息的过程发生了变化 (需要进行校验).  由于该过程没有包含在User类内部,我们就必须追踪它在所有不同形式下的所有实现,然后对它们进行修改. 假设我们意识到 SignupVerificationService也需要校验,然后我们为它添加了校验, 我们仍然需要一种能够重复使用这端校验代码的方法.在需要校验的情况下,我们可能会把方法封装到mailService中,但对于其他的逻辑,比如用户姓 名格式化,已经被加入到不同的helper/service类中了,该怎么办呢?. 这些代码可能会被多个service类所需要,也可能只有一个service需要.


  第五步: 灾难来临


  不能被很容易的移入一个外部依赖(比如,mail service)的代码就不得不被service类所共享. 将这些方法放进一个超类中供两个service共享貌似是个好主意.  我们可以看到那样形成的代码对原始的domain没有影响. 不再是User,Message和MailService,我们最终以一群奇怪的生物,就像 AbstractUserService, UserValidationService, UserReminderService等等告终.  很快,我们就很难知道新的代码真正属于哪部分,也很难知道需要写那些新代码的方法所处的位置是否会影响它的使用或者重用.

1       AbstractUserService
2             /\
3             |
4             |
5     ------------------------------------
6    |                                  |
7UserValidationService       UserReminderService


  与此同时另一个开发者也写了另一个service,这个service是用来给某个Department实体(同样也使用email地址)发送消息的.这 位开发者想要使用AbstractUserService中的邮箱验证和名字格式化功能,但他的代码是为Departments服务的,而不是 Users,因此,代码结构中另一层又出现了:AbstractEntiryService.

  哪里错了呢?

  我们已经失去了对我们程序结构的控制,我们的开发团队开始发现很难再写出干净的代码. 我们的类需要比实际需求更多的公共方法来维护复杂的类关系 。

  总结


  通过贫血的领域模型来保持代码结构整洁并且可维护是当然不可能的.然而,当我们能够使用充血领域模型的时候,维护代码并且保持类接口简洁就变得非常容易了.


01public class User {
02   //Dependencies
03   private IMailService mailService;
04   private IMessageService messageService;
05 
06   private final String name;
07   private final String emailAddress;
08 
09  public User(final String name, final String emailAddress) {
10      this.name=name;
11      this.emailAddress=emailAddress;
12   }
13 
14    public void sendReminderMessage() {
15      deliverMessage( this.messageGeneratorService.generateReminderMessage(this.getName));
16   }
17 
18   public void sendVerificationEmail() {
19      deliverMessage(this.messageGeneratorService.generateVerificationMessage(this.getName));
20 
21   }
22 
23   private void deliverMessage(final String message) {
24      if (isEmailAddressValid(user.getEmailAddress()) {
25         this.mailService.sendMessage(user.getEmailAddress(), reminderMessage);
26      }
27   }
28 
29   public String getName() {
30      return this.name;
31   }
32}


  注意,我们不再需要email地址的get方法,而且,如果你能原谅我玩数字游戏,我们增加了两个User类的公共方法二不是引入两个(至少)额外的类. 当我们在适当的对象上执行方法的时候比在一个不自然的service对象上执行方法看起来更直观.


  MailService和MessageServices仍被允许留在系统中因为它们的角色很明确. 传送邮件是一个清晰的架构问题,应该被从领域对象中通过接口(IMailService)抽象出来.生成消息应该被如何抽象/封装可能是更值得商榷的,但 这篇文章就会比我与其的更长了.


  我希望你会喜欢这篇文章.


酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部