域模型模式示例

Ale*_*onk 5 java design-patterns

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

从我在Internet上发现的域模型只是向类中添加一些"逻辑"方法.例如

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 
}
Run Code Online (Sandbox Code Playgroud)

我理解正确吗?如果没有,我将非常感谢Domain Model Pattern的一个小例子.

dav*_*xxx 17

我理解正确吗?如果没有,我会感激一个小例子.

广义上,是的.

马丁福勒,领域模型是既包含行为和数据域的一个对象模型.
域模型经常与您具有承载数据的特定类的模型以及承载行为/处理的一些其他特定类相对.

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

public boolean isPositive(){
   return amount > 0 ? true : false;
}
Run Code Online (Sandbox Code Playgroud)

是一种与模型无关的效用函数.
你可以把它放在一个Math班级里.

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

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

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

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

收入:

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

收入:

public class Income {

  private float amount;
...
  public void applyBonus(BonusRule bonusRule){
       float bonus = bonusRule.compute(this);
       amount += bonus;
  }      
...
}
Run Code Online (Sandbox Code Playgroud)

ChristmasRule:

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

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

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()); 
}
Run Code Online (Sandbox Code Playgroud)

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

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());
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

// 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); 
}
Run Code Online (Sandbox Code Playgroud)

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


这个例子说明了两种方式之间的巨大差异:

领域模型:

  • 对象很有意义.

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

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

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

贫血领域模型:

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

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

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

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


Fuh*_*tor 6

你引用福勒的书是指拉尔曼的书,用于介绍性理解和例子.

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

在域模型中有一个概念,即类是概念性的而不是软件类,但软件类基于域(概念)类.Larman实施行为的方法遵循责任驱动设计和GoF设计模式.

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

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

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

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

福勒的书也提到了他在分析模式上写的另一本书的例子.

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

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

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

在此输入图像描述

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

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

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

谷歌搜索将为您找到生物医学研究综合领域组(BRIDG)的这个巨大模型.Gene例如,向下钻取到分子生物学子域和类,您将看到它没有行为,也没有此域模型中的任何其他(概念)类.

在此输入图像描述

域模型是否是编程语言?

Larman的哲学清楚地表明它们是独立于编程语言(概念而不是软件类),作为代码中的单独工件,将它们明确地绑定到问题域(需求).

另一方面,你会发现福勒说:"我更喜欢POJO域模型." ,这几乎是说域模型是在代码中定义的.

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

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