开发者

How many arguments is a reasonable number, big objects vs atomic arguments. OOP

开发者 https://www.devze.com 2023-02-03 16:21 出处:网络
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 d

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消