多个域对象"告诉,不要问"

djc*_*edo 7 oop domain-driven-design tell-dont-ask

执行涉及多个对象的功能时,如何遵循"Tell,Do not Ask"原则.

示例 - 生成报告

我有以下对象(仅用于说明目的):

汽车,马,兔子

这些对象之间没有关系,但我确实希望基于这些对象生成报告:

createHtmlReport(Car car, Horse horse, Rabbit rabbit){
    Report report = new Report()

    report.setSomeField(car.getSerialNumber())
    report.setAnotherField(horse.getNumberOfLegs())
    // ...etc       
}
Run Code Online (Sandbox Code Playgroud)

这种方法的问题是它必须从每个对象"拉"数据,这违反了"告诉,不要问"规则.我宁愿隐藏每个对象的内部,让它们为我生成一个报告:

car.createHtmlReport()   
horse.createHtmlReport()
rabbit.createHtmlReport()
Run Code Online (Sandbox Code Playgroud)

...但是我得到了3份部分报告.此外,我认为Rabbit不应该知道如何生成我需要的每个报告(HTML,JMS,XML,JSON ....).

最后,在生成报告时,我可能想要打开多个项目:

if (car.getWheels() == 4 || horse.getLegs() == 4)
    // do something
Run Code Online (Sandbox Code Playgroud)

los*_*ace 8

报告应该保持创造自我的能力.

在这种情况下,每个IReportable对象都应该实现void UpdateReport(Report aReport).

Report.CreateReport(List<Reportable> aList)被调用时,它通过名单,并在其自己的实现,每一个对象迭代UpdateReport所调用:

aReport.AddCar(serialNumber)
aReport.AddHorse(horseName)
Run Code Online (Sandbox Code Playgroud)

最后CreateReport,报表对象应该生成自己的结果.


小智 6

"告诉不要问"规则的目标是帮助您确定应该对给定对象承担的责任最终在其外部实施的情况(坏事).
在您的案例中我们可以看到什么责任?我看到的是:

1)知道如何格式化报告(用xml,ascii,html等)
2)知道什么在哪个报告

上第一个显然不属于域对象(Car,Horse等).2)应该去哪里?可以建议域对象,但如果您的系统中有多个不同的报告,您最终会使用不同报告详细信息的知识来增加对象的负担,这些信息看起来和味道都很糟糕.更不用说它会违反单一责任原则:作为一个兔子是一回事,但知道哪些部分的兔子信息应该在报告X上与报告Y相对应是另一回事.因此,我将设计封装数据内容的类,这些数据内容包含在特定类型的报告上(并可能执行必要的计算).我不担心他们阅读兔子,马或汽车的数据成员.这个类实现的职责是"收集特定类型报告的数据",你有意识地决定这些数据应该位于域对象之外.