我是否理解正确?如果不是,能否给个小例子让我更好地理解呢?
总体上,是的。
根据Martin Fowler的定义,领域模型是一个结合了行为和数据的领域对象模型。领域模型通常与只包含特定类来代表数据以及其他具有特定行为/处理功能的类的模型相对立。
如果以您的Income
类为例,它看起来更像是一个保存属性/数据而非具有真正行为的领域模型。
public boolean isPositive(){
return amount > 0 ? true : false;
}
这是一种与模型无关的实用函数。
您可以将其放在一个 Math
类中。
我将尝试为您提供一个领域模型示例,然后展示模型将数据和处理分开的版本。
假设在您正在建模的应用程序的领域需求中,我们需要为收入添加奖金。例如,这个奖金可能会在冬季为圣诞节而发放(但为什么不为其他事件呢)
与其使用服务类来执行此处理,我们让领域模型对象执行任务。
Incomes
,作为高级别对象,可以迭代 Income
实例并应用奖金,我们可以有一个奖金规则类来根据某些输入值定义奖金。
我引入多个类,因为想要让每个对象根据其职责进行协作。
Incomes:
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;
}
...
}
圣诞节规则:
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());
}
在将数据和逻辑分离到不同的类中的设计中,它可能看起来更像这样:
In a design where you separate data and logic in distinct classes, it could look like more like that :
public class IncomeBonusService {
....
public void applyChristmasBonus(List<Income> incomes){
for (Income income : incomes){
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)的称为
贫血领域模型。
这个例子展示了两种方法之间的差异:
领域模型:
- 对象具有实际意义。
- 行为职责在类之间被精细定义。因此,具有良好的隔离性、可测试性和可维护性。例如,添加/删除/单元测试一个 BonusRule 很容易。
- 对象负责其状态。确实,不需要提供 setter,因为对象可以与其他对象协作后自行更新其状态。我们可以在 Amount.applyBonus() 中看到这一点:
```java
float bonus = bonusRule.compute(this);
amount += bonus;
```
贫血领域模型:
- 所有逻辑都在服务类中。因此,在一个地方获取代码。对于少量行数,这很好。但请注意,这种优势有一定的限制,因为逻辑变得庞大或复杂时,最好经常将逻辑拆分成多个服务类来处理。
- 无论需要多少个服务类,整个逻辑都位于服务类中而非其他地方。如果与领域模型相比,逻辑可能在某些不同的类别中爆炸,那么这可能会简化开发规范。
- 必须为域类提供 getter/setter。域不负责自己的状态和其不变规则。因此,依赖于域类的任何类都可能“破坏”其状态。
另外,一些框架(用于持久性、映射、序列化等)默认依赖 getter/setter。这就是为什么尽管具有缺点,但在某些项目中该模型仍然颇具优势的原因。