域模型模式示例

时间:2016-12-26 18:59:33

标签: java design-patterns

我只是想找到一些Martin Fowler的Domain Model模式的例子而且我不能。

从我在互联网上发现的领域模型只是添加一些"逻辑"类的方法。例如

public class Income {
    private String title;
    private String comment;
    private String date;
    private Double amount;
    private Integer category;

    public ExIn(String title, String comment, Double amount, Integer category, String date) {
        this.title = title;
        this.comment = comment;
        this.date = date;
        this.amount = amount;
        this.category = category;
    }

    public Integer getCategory() {
        return category;
    }

    public void setCategory(Integer category) {
        this.category = category;
    }

    // more getters and setters
    // Domain Model part starts
    public boolean isPositive()
    {
        return amount > 0 ? true : false;
    }
    // more methods like that 
}

我理解正确吗?如果没有,我会感激一下Domain Model Pattern的一个小例子。

3 个答案:

答案 0 :(得分:12)

  

我理解正确吗?如果没有,我会感激一点   示例

广义上说,是的。

Martin Fowler开始,域模型是包含行为和数据的域的对象模型
域模型经常与您具有承载数据的特定类的模型以及承载行为/处理的一些其他特定类相对。

如果我使用你的Income类,它看起来更像是一个包含属性/数据的类而不是具有真实行为的域模型。

public boolean isPositive(){
   return amount > 0 ? true : false;
}

是一种与模型无关的效用函数 你可以把它放在Math课程中。

我将尝试为您提供域模型示例,然后是模型分离数据和处理的版本。

假设在您正在建模的应用程序的域的要求中,我们需要为收入添加奖金。例如,这个奖金可以在圣诞节的冬天进行(但为什么不用于其他活动)

我们让域模型对象执行任务,而不是让服务类来执行此处理。

Incomes,高级对象可以在Income个实例上进行迭代并应用奖励,我们可以根据某些输入值设置奖励规则类来定义奖励。
我介绍了多个类,因为这个想法是允许每个对象根据他们的职责进行协作。

收入:

public class Incomes {
  List<Income> incomes = ...
  ....
  public void applyBonus(BonusRule bonusRule){
     for (Income income : incomes){
       income.applyBonus(bonusRule);
     }      
}

收入:

public class Income {

  private float amount;
...
  public void applyBonus(BonusRule bonusRule){
       float bonus = bonusRule.compute(this);
       amount += bonus;
  }      
...
}

ChristmasRule:

public class ChristmasBonusRule implements BonusRule {
...
  @Override
  public float compute(Income income){
       float bonus = ...
       return bonus;  
  }      
...
}

最后,我们可以通过这种方式应用处理:

void foo(){   
  // create a domain object that has both behavior and data
  Incomes incomes = ...; 
  // invoke a functional method on the object by passing another domain object
  incomes.applyBonus(new ChristmasBonusRule()); 
}

在一个将不同类中的数据和逻辑分开的设计中,它看起来更像是:

public class IncomeBonusService {
  // stateless : no incomes data inside it
  ....
  public void applyChristmasBonus(List<Income> incomes){
     for (Income income : incomes){
       // Christmas bonus computation here
       float bonus = ...
       income.setAmount(bonus + income.getAmount());
     }
  }
}

我们可以用这种方式应用处理:

// inject the service
@Autowired
IncomeBonusService incomeBonusService;

void foo(){       
   // create a domain object that has only data
   List<Income> incomes = ...; 
   // invoke a service method by passing data as parameter
   incomeBonusService.applyChristmasBonus(incomes); 
}

对象没有行为的模型设计(只有getter / setter)被称为Anemic Domain Model

此示例说明的两种方式之间存在巨大差异:

域名模型:

  • 对象很有意义。

  • 课程之间精确定义的行为责任 如此良好的隔离性,可测试性和可维护性 例如,添加/删除/单元测试BonusRule很容易。

  • 负责其州的对象 实际上,不需要提供setter,因为在与其他对象协作后,对象本身可以更新其状态 我们可以在Amount.applyBonus()中看到:

    float bonus = bonusRule.compute(this); amount += bonus;

贫血领域模型:

  • 所有逻辑都在服务类中 所以一个地方可以获得代码 几行,很好 但请注意,这种优势有一定的限制,因为随着逻辑变得庞大或复杂,最好的事情往往是在多个服务类中拆分逻辑。

  • 但无论您需要多少个服务类,整个逻辑都位于服务类中,而不是其他位置。如果我们将它与可能在类的某些不同“类型”中爆炸的逻辑域模型进行比较,这可能会简化开发规范。

  • 为域类提供getter / setter的必要性 域名不对其州及其不变规则负责。
    因此,任何依赖于域类的类都可以“破坏”其状态。

作为旁注,一些框架(用于持久性,映射,序列化......)默认依赖于getter / setter。
这就是为什么这个模型尽管有其缺点,但在一些项目中也是如此。

答案 1 :(得分:6)

您引用的Fowler的书中引用Larman's book作为介绍性理解和示例。

有趣的是,Larman的域建模方法并没有向域类添加行为。

域模型中有一个概念,即类是概念而不是软件类,但软件类基于域(概念)类。 Larman实施行为的方法遵循responsibility driven design和GoF设计模式。

域模型仍然是软件开发过程中的一个独立元素。这种建模方式的好处在于您将问题与解决方案分开。域模型应该适用于问题(从问题域捕获需求而不解决实现细节)。

Larman提出“操作合同”作为确保行为在域模型中保持一致的一种方式。同样,合同应该独立于解决方案(实现)。合同具有后置条件,在操作发生后描述域模型中的约束。后置条件的示例是当顾客在商店中完成购买时,销售对象与顾客购买的每个商品相关联。

业务逻辑的实现应该遵守为域模型定义的合同(后置条件)。 Larman使用Controller GRASP pattern以及其他GRASP模式的方法最终将此逻辑放在各种类中(通常是域层,这是受域模型中的概念类启发的软件类)或处理的façade(Controller)类系统运作。

Larman的方法比这个解释更复杂,但重点是行为永远不会仅仅在域模型中定义为方法。 Larman多次说域(概念)类没有方法,因为它们不是不是软件类。

福勒的书也提到了他在Analysis Patterns上写的另一本书的例子。

  

模式来自各个领域,包括医疗保健,金融交易和会计。每个模式都以文本方式和简单的UML前符号描述(本书是在UML稳定为可用形式之前编写的)。

该书中的所有例子都没有显示软件类,即使用编程语言定义的方法(我能找到)。

我至少知道one book,其中分子生物学等领域的领域模型已经用UML发布。这是一个例子(注意UML被修改 - 子类型框显示在超类型框中 - 以节省空间):

enter image description here

上面的书没有对行为进行建模,可能是因为它们确实依赖于软件应用程序的要求。这些模型捕获了一些业务规则,例如:

  
      
  • 每种化学配方必须由2种或更多种化学元素化合物或两者组成。
  •   

但该书中的模型主要是数据模型

谷歌搜索会找到你this huge model for the Biomedical Research Integrated Domain Group (BRIDG)。例如,向下钻取到分子生物学子域和类Gene,您将看到它没有行为,也没有此域模型中的任何其他(概念)类。

enter image description here

是否使用编程语言编写域模型?

Larman的哲学清楚地表明它们与编程语言无关(概念而不是软件类),作为代码中的单独工件,将它们明确地与问题联系起来域(要求)。

另一方面,你会发现Fowler说"I prefer POJO domain models.",这几乎就是说域模型是在代码中定义的。

Eric Evans'DDD假设大多数软件开发中的复杂程度来自域,因此这种复杂域的模型对于管理复杂性至关重要。因此,当域复杂时,域建模是必需的。 DDD建议使用无处不在的域建模语言;也就是说,域专家和开发人员都很常见。这意味着至少在某些情况下,域模型不会用编程语言定义。

related question可能会有一些亮点(尽管它产生了大量的热量)。有些人批评这个问题的例子对于一个合理的领域模型来说太过琐碎(不够复杂)。

答案 2 :(得分:2)

“域模型”只是一个对象,它代表了您的业务领域中一些可辨别的概念。 “客户”,“订单”等。无论业务逻辑是什么,构成该逻辑的有形实体都是您域中的模型。

有些人会有很多业务逻辑(也许值得分解成其他类),有些人会很少(甚至没有)。

“域模型”和任何其他类之间的区别不是Java语言本身的构造,它主要是您定义的业务逻辑的语义构造。