I was reading Fowler's Refactoring Book and saw Preserve Whole Object. A different, newer opinion says that this refactoring is the exact opposite of what you should do: The Clean Code Talks - Don't Look For Things!.
Fowler does mention that you should look to see if the method can just be moved to the class which uses the large list of arguments. I think that would be the only reasonable alternative. This refactoring seems like a band-aid for a poorly defined method.
The Fowler source material is a bit dated. Is the prevailing wisdom to let this technique go the way of the dodo or is there actually a case when you'd want to do this kind of refactoring? Or have I misunderstood the test-driven style because those examples deal with object construction, not message sending?
There are many concepts in the Object Oriented Design such as Patterns, Principles and Practices that may seem to be either similar or contradictory at first. In fact, most of them are neither similar nor contradictory. And the thing that makes them different and consistent is their intent.
The seeming contradiction between the Preserve Whole Object refactoring and the Service Locator pattern mentioned in The Clean Code Talks video occurs when they are treated as a same concept, although they are different in their intent and their essence.
The Preserve Whole Object refactoring is simply a technique used to make code easier to read, understand and maintain by reducing the number of arguments to a function. The Service Locator, on the other hand, is a design pattern that is used to manage dependencies between different components in a system using the Inversion of Control concept. Unlike the Preserve Whole Object refactoring technique which has local effect on a system, that is applied to a small part of the system (a function), the Service Locator pattern has a global effect on the system and addresses a bigger architectural problem (Dependency Management).
When to use the Preserve Whole Object refactoring?
Use the Preserve Whole Object refactoring when You have two or more arguments to a function which are basically the properties of one object, so pass the object instead.
There is a similar concept called Parameter Object (aka Argument Object) (Introduce Parameter Object refactoring) which states that if You have a group of parameters that are not the properties of one object but are conceptually related to each other or naturally go together, wrap them with a class of their own and pass the instance of that class instead. It is mainly used when sending a message to an object.
A quote from Clean Code, Chapter 3: Functions, Function Arguments, page 43 (Robert C. Martin):
Argument Objects
When a function seems to need more than two or three arguments, it is likely that some of
those arguments ought to be wrapped into a class of their own. Consider, for example, the
difference between the two following declarations:
Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);
Reducing the number of arguments by creating objects out of them may seem like
cheating, but it’s not. When groups of variables are passed together, the way x and
y are in the example above, they are likely part of a concept that deserves a name of its
own.
When to use the Service Locator pattern?
Use the Service Locator pattern when Your class has dependencies that are not conceptually related and You do not want Your class to depend on concrete implementations. Actually, this is when You would want to use any of the Dependency Management approaches. Another alternative is the Dependency Injection approach which explicitly specifies all the dependencies as a separate arguments to the constructor. Whereas the Service Locator pass all the dependencies in a single container object. In fact, it is this very similarity between the Service Locator pattern and the Preserve Whole Object refactoring of combining the arguments in a single object that serves as a source of confusion. The Dependency Management techniques are mainly used in object construction.
There are pros and cons to both approaches of the Dependency Management which are discussed in the Inversion of Control Containers and the Dependency Injection pattern article by Martin Fowler.
When to use both?
Sometimes there will be situations where Your class will have two or more dependencies that are conceptually related and You might want to combine them in a single object and pass it as a dependency using the Service Locator. So, as You can see these two concepts are not mutually exclusive.
Preserve Whole Object
pros:
- makes code shorter and somewhat clearer.
cons:
makes interface more compicated:
void drawCircle(Point center, double radius);
// in order to understand this method I should study the Point class
instead of
void drawCircle(double center_x, double center_y, double radius);
// more verbose but makes explicit what you need to draw a circle
results in boilerplate code in tests because you need to create and initialize dummy parameter objects:
Point p = new Point(2,2); //boilerplate
drawCircle(p, 2);
instead of
drawCircle(2, 2, 2);
creates additional coupling, e.g. between Drawer and Point.
There is a nice metaphor which encourages not to do so: You give cashier money, not your wallet :).
summary:
I preserve object if method requires a lot of it's data. In the example with Point above I would probably do so, because Point is something quite primitive.
Otherwise I prefer passing required arguments directly or using "Introduce Parameter Object" pattern for grouping parameters.