How many arguments is a reasonable number, big objects vs atomic arguments. OOP
i'm a novel developer and i would like to know in yours experience what is the better approach when your building class m开发者_Go百科ethods, and if there is not a better approach, how balance your decisions with respect to:
- Pass as arguments a big object that contains most of the variables needed for the method.
- Pass the more atomic individuals variables possibles, with the consequences of generate methods with big signatures.
What is better for a code that is going to evolve? and what do you think is a reasonable number of arguments?
I would argue strongly in favor of passing around an object, if the commonality in the sets pf arguments allows it.
Why?
Because X% of effort goes to maintain existing code and it's a LOT harder to add new parameters - especially in methods that chain-call each other and pass those new parameters - than to add a property to an object.
Please note that this doesn't have to be a CLASS per se, in a sense of having methods. Merely a storage container (either a heterogeneous map/dictionary, or for type safety, a struct in C-type langages that support it).
Example (I'll use pseudocode, feel free which language(s) it's based on):
First, let's see old and new code using argument lists
Old code:
function f1(arg1, arg2, arg3, arg4, arg5) {
res = f2(arg1, arg2, arg3, arg4);
}
function f2(arg1, arg2, arg3, arg4) {
res = f3(arg1, arg2, arg4);
}
function f3(arg1, arg2, arg4) {
res = f4(arg1, arg4);
}
function f4(arg1, arg4) {
return arg1 + arg4;
}
New code (need to add arg6 in f4()):
function f1(arg1, arg2, arg3, arg4, arg5, arg6) { // changed line
res = f2(arg1, arg2, arg3, arg4, arg6); // changed line
}
function f2(arg1, arg2, arg3, arg4, arg6) { // changed line
res = f3(arg1, arg2, arg4, arg6); // changed line
}
function f3(arg1, arg2, arg4, arg6) { // changed line
res = f4(arg1, arg4, arg6); // changed line
}
function f4(arg1, arg4, arg6) { // changed line
return arg1 + arg4 + arg6; // changed line
}
As you can see, for 4-level nested calls, we changed ALL 4 functions, at the volume of at least 2 lines per function. YIKES. So for 10-level nested calls, adding 1 parameter changes all TEN functions and 20 lines.
Now, an example of the same change, except the arg list is now an object (or, for dynamic languages, a heterogeneous map would do :)
class args_class {
public:
int arg1, arg2, arg3, arg4, arg5;
}
}
args_class arg_object;
function f1(arg_object) {
res = f2(arg_object);
}
function f2(arg_object) {
res = f3(arg_object);
}
function f3(arg_object) {
res = f4(arg_object);
}
function f4(arg_object) {
return arg_object.arg1 + arg_object.arg4;
}
And what do we change to add arg6?
class args_class {
public:
int arg1, arg2, arg3, arg4, arg5, arg6; // line changed
}
}
// f1..f3 are unchanged
function f4(arg_object) {
return arg_object.arg1 + arg_object.arg4 + arg_object.arg6; // line changed
}
That's it. For 4-level nested methods, or for 10-level nested methods, you ONLY change 2 lines both.
Which one is less work to maintain?
I think it all depends on the context of the function parameters themselves. If you're relying on elements of some thing, then I'd pass a reference of that some thing as a parameter (whether it's a reference/pointer to an interface of that object or a reference/pointer to the object definition itself is an implementation detail).
If the parameter isn't derived directly from an object and there are a small number of parameters (five or less maybe? up to you really), then I'd pass atomic arguments.
If there are potentially a large number of arguments, then I'd create some sort of an init struct as a parameter, where the calling code instantiates and fills the struct and then passes a reference to it as an argument.
精彩评论