我理解正确吗?如果没有的话,我将不胜感激
例子。
从广义上讲,是的。
From 马丁·福勒 http://martinfowler.com/eaaCatalog/domainModel.html, 领域模型是包含行为和数据的领域对象模型.
领域模型通常与具有特定类来承载数据和其他一些特定类来承载行为/处理的模型相反。
如果我带你的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);
}
}
Income :
public class Income {
private float amount;
...
public void applyBonus(BonusRule bonusRule){
float bonus = bonusRule.compute(this);
amount += bonus;
}
...
}
圣诞节规则:
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)的模型设计称为贫血域模型 https://www.martinfowler.com/bliki/AnemicDomainModel.html.
此示例说明了两种方式之间的巨大差异:
域模型:
对象是有意义的。
班级之间明确定义的行为责任。
所以具有良好的隔离性、可测试性和可维护性。
例如,添加/删除/单元测试BonusRule
简单。
-
对其状态负责的对象。
事实上,不需要提供 setter,因为对象可以在与其他对象协作后自行更新其状态。
我们可以看到,在Amount.applyBonus()
:
float bonus = bonusRule.compute(this);
amount += bonus;
贫血域模型:
所有逻辑都在服务类中。
所以只有一个地方可以获取代码。
几行就可以了。
但请注意,这种优势有一定的限制,因为随着逻辑变得庞大或复杂,最好的办法往往是将逻辑拆分为多个服务类。
但无论您需要多少个服务类,整个逻辑都位于服务类中,而不是其他地方。如果我们将其与域模型进行比较,这可能会简化开发规范,其中逻辑可能会在某些不同“类型”的类中分解。
需要为域类提供 getter/setter。
该域也不对其状态及其不变规则负责。
因此任何依赖于域类的类都可以“破坏”其状态。
附带说明一下,一些框架(用于持久化、映射、序列化等)默认依赖 getter/setter。
这就是为什么该模型尽管有缺点,但在某些项目中处于领先地位。