Search code examples
oopdomain-driven-designtell-dont-ask

"Tell, Don't Ask" over multiple domain objects


Question

How do I adhere to the "Tell, Don't Ask" principle when performing a function involving multiple objects.

Example - Generating a Report

I have the following objects (illustrative purposes only):

Car, Horse, Rabbit

There is no relationship between these objects, but I do want to generate a Report based on these objects:

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

    report.setSomeField(car.getSerialNumber())
    report.setAnotherField(horse.getNumberOfLegs())
    // ...etc       
}

The problem with this method is that it has to "Pull" data from each object, which violates the "Tell, Don't Ask" rule. I would rather keep the insides of each object hidden, and have them generate a report for me:

car.createHtmlReport()   
horse.createHtmlReport()
rabbit.createHtmlReport()

... but then I get 3 partial reports. Furthermore, I don't think a Rabbit should have to know how to generate every single report I need (HTML, JMS, XML, JSON ....).

Finally, whilst generating the report I may want to switch on multiple items:

if (car.getWheels() == 4 || horse.getLegs() == 4)
    // do something

Solution

  • The report should maintain the ability to create its self.

    In this case, each IReportable object should Implement void UpdateReport(Report aReport).

    When Report.CreateReport(List<Reportable> aList) is invoked, it iterates through the List and each object in its own implementation of UpdateReport invokes:

    aReport.AddCar(serialNumber)
    aReport.AddHorse(horseName)
    

    At the end of CreateReport, the report object should produce its own result.