Suppose I have the following
Class A {
Foo getFoo();
Bar getBar();
Baz getBaz();
}
And I need to define a function doStuff
Both David and Som's answers have great information to consider. I'll add the following:
As with many design patterns, the decision of what to do lies on a continuum between options with their own pros and cons. There is not always a right answer - more often it comes down to which pros to you want to enjoy and which cons are you willing to risk.
In my experience, moving to a DTO is helpful when you have related values that always travel together. David described the pros to this approach well. An additional con I have seen to this approach is that you risk adding unnecessary dependencies to methods when the DTO grows.
For example, methods A, B, C, and D take Foo, Bar, and Baz, so it is nice to combine these arguments in to a DTO. Then methods A and B need to take on Quux - do you add Quux to the DTO forcing C and D to take on an unused dependency? When you test C and D, what value do you pass in for Quux? When a new developer uses methods C and D, does the presence of Quux generate confusion? When comparing methods A and C, is it clear how Quux should or should not be defined?
Similar situations arise when you originally need Foo, Bar, and Baz for all methods, but then some methods no longer need those values.
I observed an experience where one team was passing a DTO to the service of another team and taking great efforts to correctly populate and sync the information in that DTO, when all that was actually needed was a single value that could have been passed trivially.
Unless the values always go together, you risk generating confusion, increased test burden, and extra dev work. If the values do always go together, a DTO can provide clarity, reduce duplication, simplify consistency, etc.