Skip to main content
1 of 5

While this recommendation revolves about arguments, it's not fundamentally about arguments.

The key point is this:

"it is likely that some of those arguments ought to be wrapped into a class of their own"

Why? Usually, when you have a large number of arguments, some of those arguments will be more closely related than others. E.g., they will be related to the same concept, and in the method itself, there will be some logic that manipulates those clusters of arguments to achieve something. The problem is, that concept is not made explicit, and the logic related to it is not clearly delineated. What you likely have in there is code that intermingles different concepts, or different levels of abstraction. Mixes responsibilities on a local level, if you will.

You've said:

"As far as understand things, classes are supposed to represent meaningful objects"

Yeah! A class is an explicit representation of some concept. And when you have a large number of parameters, it's worth checking if there's a meaningful/useful concept in there that's currently not represented, and if it should be.

So it's not just about bundling parameters together.

E.g., inside the method, there's probably going to be a code block that only uses a couple of parameters with a comment on top of it explaining what it does. Or maybe there will be a block comprised of one or more if-conditionals. And the details of that block are not going to be the main point of the method. You can probably do the Extract Method refactoring and get a cleaner, easier to understand code in the original method - code that more succinctly expresses what the method actually does. But then you should ask yourself, does that method really belong to the same class? Maybe it does, but maybe it would be better to relocate it.

// Takes two axis-aligned rectangles representing the bounding boxes // of two entities and returns a CollisionInfo instance. CollisionInfo ResolveColision( double left1, double top1, double right1, double bottom1, double left2, double top2, double right2, double bottom2) { // Prepare some variables to store stuff // (... omitted ...) // Compute the intersection // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // ... // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // (... a wall of code ...) // Figure out how to resolve the collision // (... omitted ...) // Create the output data structure var collisionInfo = // (... omitted ...) return collisionInfo; } 

If you look at this code, there are hints that there are some concepts in there that lack explicit representation. E.g., the description of the method mentions axis-aligned bounding rectangles. The parameter list is formatted in two rows in a way that looks deliberate, and the suffix in the names of the parameters indicates that there are really two objects there, not eight. Then (if you're lucky) there's a comment explaining what that wall of code is doing.

Well, let's create a class to represent an axis-aligned bounding rectangle, let's give it an Intersect method, and let's put that wall of code in there. Now I can be a bit more declarative - I just want to tell the code to compute the intersection; I don't care how:

// The method is now essentially self-documenting; that // documentation comment form before is now redundant CollisionInfo ResolveColision(BoundingRect rect1, BoundingRect rect2) { BoundingRect intersection = rect1.Intersect(rect2); // Use 'intersection' to figure out how to resolve the collision // (... omitted ...) return new CollisionInfo(/* ... omitted ... */); } // Sometimes, you may choose to keep the original method as an // overload, for convenience, or for backward compatibility. // But this just delegates to the previous method, so it's not a // problem in terms of readability, maintenance, etc. CollisionInfo ResolveColision( double left1, double top1, double right1, double bottom1, double left2, double top2, double right2, double bottom2) { return ResolveCollision( new BoundingRectangle(left1, top1, right1, bottom1) new BoundingRectangle(left2, top2, right2, bottom2)); } 

"Many functions in standard libraries have multiple arguments."

The prevalence of something doesn't mean that it's an example of good design or that it should be emulated. Again, sometimes a large(r) number of arguments is the way to go, but first check if that's really true. Look for those implicit concepts, consider ease of use, etc.