53

I read the first chapters of Clean Code by Robert C. Martin, and it seems to me it's pretty good, but I have a doubt, in one part it is mentioned that it is good (cognitively) that the functions should have as few parameters as possible, it even suggests that 3 or more parameters is too much for a function (which I find very exaggerated and idealistic), so I started to wonder...

Both the practices of using global variables and passing many arguments on the functions would be bad programming practices, but the use of global variables can greatly reduce the number of parameters in the functions...

So I wanted to hear what you think about it, is it worth using global variables to reduce the number of parameters of the functions or not? In what cases would it be?

What I think is that it depends on several factors:

  • Source code size.
  • Number of parameters in average of the functions.
  • Number of functions.
  • Frequency in which the same variables are used.

In my opinion if the source code size is relatively small (like less than 600 lines of code), there are many functions, the same variables are passed as parameters and the functions have many parameters, then using global variables would be worth, but I would like to know...

  • Do you share my opinion?
  • What do you think of other cases where the source code is bigger, etc.?

P.S. I saw this post, the titles are very similar, but he doesn't ask what I want to know.

BlueWizard
  • 103
  • 3
OiciTrap
  • 729
  • 1
  • 7
  • 12
  • Possible duplicate of [Parametrize methods vs global variables](https://softwareengineering.stackexchange.com/questions/347199/parametrize-methods-vs-global-variables) – gnat Jul 13 '17 at 06:29
  • 148
    I don't think the alternative would be globals, but instead consolidating arguments into objects. It's probably more of a suggestion that `postLetter(string country, string town, string postcode, string streetAddress, int appartmentNumber, string careOf)` is a smelly version of `postLetter(Address address)`. Continue reading the book, it hopefully will say something like that. – Nathan Cooper Jul 13 '17 at 07:18
  • @OiciTrap: do you question the use of global variables, or Robert Martin's suggestions for using less than 3 or 4 parameters for a function, as written in the "Clean Code" book? These are two different things, see my answer below. – Doc Brown Jul 13 '17 at 09:10
  • 3
    @DocBrown I took the question to mean something more like Uncle Bob says dont use more than 3 params so I get round that problem by using global variables right? :-) I think likely author does not know there are better ways to get round this problem - like mentioned in the answers below. – bytedev Jul 13 '17 at 10:26
  • @nashwan: for me the question looks like being based on the very wrong assumption Uncle Bob suggests to use global variables to reduce function parameters, and most answers here take that wrong assumption for granted. – Doc Brown Jul 13 '17 at 10:33
  • @Nathan Cooper I agree that this is better, but you still have constructor of Address class, that has same "smelly" number of parameters. – charlie_pl Jul 13 '17 at 10:42
  • @NathanCooper Great example; an object [could possibly manage to handle at least some of these variations](https://www.mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses/) – Izkata Jul 13 '17 at 13:53
  • Anyway later he will also talk about making dependencies explicit vs implicit (global) so no don't do this – Rémi Jul 13 '17 at 13:59
  • @charlie_pl in some cases you can use composition to mitigate that (although it wouldn't work well with Nathan's example). In some other cases you can't abstract much more and I guess you have to accept that you are working with a complex idea which will lead to complex code. – Aaron Jul 13 '17 at 15:55
  • 9
    No more than n parameters is a **rule of thumb** (for any value of n), not etched on a diamond. Don't mistake good advice for a mandate. Lots of parameters is generally a code smell that you've got too much going on in one function/method. People used to avoid splitting into multiple functions to dodge the added overhead of the extra calls. Few applications are this perf-intensive anymore and a profiler can tell you when and where you need to avoid extra calls. – Jared Smith Jul 13 '17 at 16:04
  • 14
    This shows what is wrong with this sort of judgement-free rule: it opens the door to judgement-free 'solutions'. If a function has many arguments, it may indicate a suboptimal design, usually not just of the function, but of the context in which it it is being used. The solution (if one is needed) is to seek to refactor the code. I cannot give you a simple, general, judgement-free rule on how to do it, but that does not mean that 'no more than N arguments' is a good rule. – sdenham Jul 13 '17 at 16:26
  • If you have large amounts of parameters to functions you likely have a composition/abstraction problem. Introducing global variables is just fixing the symptom. – Ant P Jul 13 '17 at 16:38
  • 1
    I can see where someone who is a hardcore evangelist of the OOP paradigm would suggest that any time a function has more than 3 parameters, you should think of a new abstraction model that lets you pass a smaller number of more detailed objects or else rework the design to avoid such a call in the first place. In the real world everything is about compromise, so you'll always have to weigh the cost of adding an extra parameter or two against the cost of all that extra refactoring. One thing on which I do agree with all parties, though, is not to resort to using globals. – Darren Ringer Jul 13 '17 at 19:45
  • 4
    If you have too many parameters to a function, odds are some of them are related and should be grouped into an object which then becomes a single parameter encapsulating multiple pieces of data. There is a third option here. –  Jul 13 '17 at 20:59
  • Global parameters are like nuclear weapons; they're wretched and only to be used as an absolute last resort. If you're truly stuck, `static` singletons are a lesser evil. – Nat Jul 13 '17 at 21:48
  • 2
    This global thing is a perfect example of why inexperienced programmers probably shouldn't read stuff like that. Global "parameters" are a monstrous, nightmarish evil. An extra function parameter or two may not be a great idea, but it's not within two orders of magnitude of the awfulness of passing parameters via globals. The latter approaches professional malpractice. It's BAD. You need a fair amount of horse sense borne of hard-knocks coding experience before these grand statements about programming style can be of any value to you. A noob risks swallowing horses to catch flies. – Ed Plunkett Jul 13 '17 at 22:16
  • 2
    Reread pages 40-43 in Chapter 3. "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." – CJ Dennis Jul 14 '17 at 06:16
  • 3
    I suggest reading Michael Feathers great book "Working Effectively with Legacy Code", too. "Clean Code" tells you *what* clean code is and *why* you want it. "Working Effectively ..." tells you *how* to get there from each and every possible situation of bad code. – TobiMcNamobi Jul 14 '17 at 08:40
  • I usually allow 3 parameters but after that I consider merging parameters into a new parent object created for that purpose. The only exception is dependency injection, which I might allow to go larger in some languages like JavaScript with RequireJs or angular. – Mark Rogers Jul 15 '17 at 16:29
  • @NathanCooper And `Address` would have a builder. – Thorbjørn Ravn Andersen Jul 16 '17 at 23:53
  • @JaredSmith also additionally, modern lower level languages like rust support macros to fully eliminate method call overhead when used. – Mafii Jul 25 '17 at 11:46

15 Answers15

119

I don't share your opinion. In my opinion using global variables is a worse practice than more parameters irrespective of the qualities you described. My reasoning is that more parameters may make a method more difficult to understand, but global variables can cause many problems for the code including poor testability, concurrency bugs, and tight coupling. No matter how many parameters a function has, it won't inherently have the same problems as global variables.

...the same variables are passed as parameters

It may be a design smell. If you have the same parameters being passed to most functions in your system, there may be a cross-cutting concern that should be handled by introducing a new component. I don't think passing the same variable to many functions to be sound reasoning to introduce global variables.

In one edit of your question you indicated that introducing global variables might improve the readability of code. I disagree. Usage of global variables is hidden in the implementation code whereas function parameters are declared in the signature. Functions should ideally be pure. They should only operate on their parameters and should not have any side-effects. If you have a pure function, you can reason about the function by looking just at one function. If your function is not pure, you must consider the state of other components, and it becomes much more difficult to reason about.

Samuel
  • 9,137
  • 1
  • 25
  • 42
  • Ok, It's good to hear other opinions, but about the **design smell**, I'm programming in C problems that are solved by methods of artificial intelligence and I tend to use many functions that almost always use the same matrices, arrays or variables (which I pass as parameters to the functions), I'm saying this because I don't feel that I can put these variables inside a common concept/thing like a struct or union so I wouldn't know how to make a better design, so that's why I think the use of global variables in this case could be worth, but most likely I'm wrong. – OiciTrap Jul 13 '17 at 03:59
  • 1
    That doesn't sound like a design issue. I still think passing parameters to the functions is the best design. In an AI system like you're describing I would find it useful to write unit tests to test the pieces of the system, and the easiest way to do that is with parameters. – Samuel Jul 13 '17 at 04:18
  • Mmmm ok, I guess you are right... – OiciTrap Jul 13 '17 at 04:29
  • 103
    More parameters make a subroutine harder to understand, global variables make the whole program, i.e. *all subroutines* harder to understand. – Jörg W Mittag Jul 13 '17 at 07:26
  • 2
    I maintain a code base where some of the functions take more than a dozen parameters. It's a huge time waster - every time I need to call the function I need to open that file in another window so that I know in what order the parameters need to be specified. If I used an IDE that gave me something like intellisense or if I used a language that had named parameters then it wouldn't be so bad but who can remember what order a dozen parameters are in for all those functions? – Jerry Jeremiah Jul 13 '17 at 08:51
  • @JerryJeremiah No one, that is why we use IDEs. Your case is kinda extreme though and honestly do the amount of parameters matter? Can you rely on your memory that for random_3_paramter_function the correct order is A, B, C and not A, C, B? – kat0r Jul 13 '17 at 12:45
  • 6
    The answer is correct in what it says, nevertheless it seems it takes the OPs misunderstanding about the recommentations in "Clean Code" for granted. I am sure there is no recommendation to replace function parameters by globals in that book. – Doc Brown Jul 13 '17 at 13:46
  • @JörgWMittag: Not only is a program using global variables harder to understand, it is also much easier to have hard to reproduce errors, especially when using multiple threads. – gnasher729 Jul 13 '17 at 18:00
  • @kat0r: Use Objective-C or Swift. Parameters are named in the call by default. – gnasher729 Jul 13 '17 at 18:00
  • The biggest issue I always have with global variables is the fact that you can no longer properly test a function if it uses global state. This is a concept that is actually very simple but that a lot of developers I've worked with struggle with. For more details, I refer you to an [article by John Carmack](http://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php) that covers the subject well, especially referencing the fact that in programming, hard rules like "never use global state" aren't always practical, but they are a good goal. – AngularRat Jul 14 '17 at 15:45
69

You should avoid global variables like the plague.

I wouldn't put a hard limit to number of arguments (like 3 or 4), but you do want to keep them to a minimum, if possible.

Use structs (or objects in C++) to group together variables into a single entity and pass that (by reference) to functions. Usually a function gets a structure or object (with a few different things in it) passed to it along with a couple of other parameters that tell the function to do something to the struct.

For good-smelling, clean, modular code, try to stick to the single responsibility principle. Do that with your structs (or objects), the functions, and the source files. If you do that, the natural number of parameters passed to a function will be obvious.

robert bristow-johnson
  • 1,190
  • 1
  • 8
  • 17
  • 5
    What is the principal difference between passing tens of parameters hidden in a struct and passing them explicitly? – Ruslan Jul 13 '17 at 08:03
  • 21
    @Ruslan Cohesion. – abuzittin gillifirca Jul 13 '17 at 08:26
  • 9
    And you are more likely to refactor the function into smaller functions, since you can just pass along one parameter to the sub-functions instead of tens of parameters. And less risk of mixing up the parameters if you use positional arguments. – Hans Olsson Jul 13 '17 at 12:20
  • 8
    That's fine, but you have to ensure coherence in the Structs - make sure that the parameters grouped into a Struct are 'related'. You might use more than one Struct under some circumstances. – Andrew Dodds Jul 13 '17 at 12:22
  • 8
    @abuzittingillifirca You do not get cohesion automatically. If the only justification for putting parameters in a struct is to pass them to a particular function, then the cohesion is probably illusory. – sdenham Jul 13 '17 at 16:17
  • i totally agree @sdenham. SRP applies to structs also. – robert bristow-johnson Jul 13 '17 at 16:36
  • You can't just say, "avoid global variables" without giving a reason why. I mean, you can, but why should I believe you? – Kenneth K. Jul 13 '17 at 20:44
  • @KennethK., i might suggest asking a question to this SE or to the Stack Overflow crowd about "Why are global variables (other than *'System Globals'*) a problem for clean, bug free, and reusable code?" – robert bristow-johnson Jul 13 '17 at 20:49
  • for instance, @KennethK., using the C paradigm, i can see using `static const` for unchanging, constant "variables" used in a function like `double exp(double x);` which would be the Maclaurin or finite power series coefficients. they will be the same no matter who calls `exp()` and they will occupy the same space as globals, **but no one may write to them**. but global variables are useless if you want your functions to be callable from multiple contexts. it's the arguments passed to the function that set up the context and that may be different for different function calls. – robert bristow-johnson Jul 13 '17 at 20:55
  • 2
    You're missing the point. I understand why to not use them; the OP may not. If I told you to stop eating fruits and vegetables, would you do it simply because I told you to, or would you want to know my reasoning as to why? – Kenneth K. Jul 13 '17 at 20:55
  • no, it's more like i'm saying *"stay the hell outa the poison ivy."* – robert bristow-johnson Jul 13 '17 at 20:56
  • 4
    @sdenham, that is true. But then WTF is your function doing with ten unrelated pieces of data? Too much. – Paul Draper Jul 13 '17 at 23:42
  • I think this answer would benefit from some explanation why the answer because without it, it's effectively boiled down to 'avoid globals like the plague'. This answer offers a solution to the problem the answerer put forward but not the question OP is asking, I think. Doesn't make it an unhelpful answer though. – TankorSmash Jul 14 '17 at 05:16
  • @Ruslan The point of passing tens of parameters in a `struct` is to pass along state instead of passing tens of parameters as global variables. When you pass them as a `struct` ([which is usually referred to as a *context*](https://stackoverflow.com/questions/6120808/what-does-ctx-mean)), then it's much easier to add additional state variables without having to modify many different functions. – jamesdlin Jul 15 '17 at 23:44
  • And in some cases, if you want to add an extra parameter to a function, you can't because they're part of an API and you want to retain backward compatibility. For example, many Win32 `...Ex` functions take a pointer to a `struct` to allow for such extensibility. – jamesdlin Jul 15 '17 at 23:45
  • @PaulDraper Precisely - the point is that putting them in a struct does not make it any better. It does not fix the underlying design problem. – sdenham Jul 16 '17 at 00:10
  • 4
    @PaulDraper See my comment under the question. The response 'too many arguments? Then put them in a struct' is exactly the sort of judgment-free 'solution' that judgment-free rules can lead to - superficially, it brings the code into conformance with the rule, but stuffing arguments into a struct does not create any relatedness that was not there before. The correct solution is to refactor the design. – sdenham Jul 16 '17 at 00:56
  • BTW @sdenham, i have not suggested putting unrelated variables in a common `struct`, just to send it to a function. and then Paul is right in askng "But then WTF is your function doing with ten unrelated pieces of data?" modular design and the SRP applied to structs means that there are only related variables placed into a `struct` and that stuct have an overall **singular** purpose or function or role and these variables are there in support of that singular purpose or function or role. – robert bristow-johnson Jul 16 '17 at 18:09
  • This is a meaningless answer, not addressing the issue. Just wrapping up arguments does not fix anything. – Martin Maat Jul 16 '17 at 18:12
  • no. it's not meaningless, nor have i advocated *"just wrapping up arguments"*. you may have missed the reference to [SRP](https://en.wikipedia.org/wiki/Single_responsibility_principle). – robert bristow-johnson Jul 16 '17 at 18:55
  • My apologies for having contributed to a misinterpretation of your answer. I joined in this thread in response to Gillifirca's comment, which seems to provide a justification for a practice that would only superficially (if that) conform to your answer. (I may also be misinterpreting the intent of that comment; it is hard to tell from one word.) – sdenham Jul 17 '17 at 12:12
58

We're talking about cognitive load, not syntax. So the question is... What is a parameter in this context?

A parameter is a value which affects the behaviour of the function. The more parameters, the more possible combinations of values you get, the harder reasoning about the function gets.

In that sense, global variables that the function uses are parameters. They're parameters that don't appear in its signature, that have order-of-construction, access control and remanence issues.

Unless said parameter is what is called a cross-cutting concern, that is some program-wide state which everything uses but nothing alters (e.g. a logging object), you should not replace function parameters with global variables. They'd still be parameters, but nastier.

Quentin
  • 1,465
  • 9
  • 10
  • 12
    To you a +1, be it a toilet or a commode they reek the same. Nice job pointing out the wolf in sheep's clothing. – Jared Smith Jul 13 '17 at 14:02
  • 1
    +1 for stating that both many parms and global vars are bad. But I want to clarify something. In most languages, primitive parameters are passed by value by default (Java), and in others you can pass them by value explicitly (PL/SQL). In the other hand global primitives are always accessed by reference (so to say). So parameters, at least of primitive types, are always safer than global variables. Although of course having more than two or three parameters is a smell, and having twelve parameters is a huge smell that shoule be refactored. – Tulains Córdova Jul 13 '17 at 15:49
  • 4
    Absolutely, a global variable IS a hidden parameter. – Bill K Jul 14 '17 at 16:44
  • +1 To your point, I've seen MATLAB simulations that rely on global variables to pass data. The result was completely unreadable because it was so hard to tell which variables were parameters to which function. – Cort Ammon Jul 14 '17 at 21:00
38

Having many parameters is considered undesirable, but turning them into fields or global variables is a lot worse because it doesn't solve the actual problem but introduce new problems.

Having many parameters is not in itself the problem, but it is an symptom that you might have a problem. Consider this method:

Graphics.PaintRectangle(left, top, length, height, red, green, blue, transparency);

Having 7 parameters is a definite warning sign. The underling problem is these parameters are not independent but belongs in groups. left and top belong together as a Position-structure, length and height as a Size structure, and red, blue and green as a Color structure. And maybe Color and transparency belongs in a Brush structure? Perhaps Position and Size belongs together in a Rectangle structure, in which case we may even consider turning it into a Paint method on the Rectangle object instead? So we may end up with:

Rectangle.Paint(brush);

Mission accomplished! But the important thing is we actually have improved the overall design, and the reduction in number of parameters is a consequence of this. If we just reduce the number of parameter without tackling the underlying issues, we might do something like this:

Graphics.left = something;
Graphics.top = something;
Graphics.length = something;
...etc
Graphics.PaintRectangle();

Here we have achieved the same reduction in number of parameter, but we have actually made the design worse.

Bottom line: For any programming advice and rules-of-thumb it is really important to understand the underlying reasoning.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • 4
    +1 nice, constructive, understandable, non-theoretical answer. – AnoE Jul 14 '17 at 14:04
  • 1
    Not to mention, what the hell is your "square" doing having both a length AND a height attribute. :) – Wildcard Jul 14 '17 at 21:48
  • 1
    +1 for acknowledging that the obvious third way implied by some answers (ie. many assignments to some structure/object prior to the function call) isn't any better. – benxyzzy Jul 15 '17 at 12:15
  • @Wildcard: Thanks, changed it to "rectangle" to avoid confusing the issue! – JacquesB Jul 15 '17 at 19:09
35

IMHO your question is based on a misunderstanding. In "Clean Code", Bob Martin does not suggest to replace repeated function parameters by globals, that would be a really awful advice. He suggest to replace them by by private member variables of the class of the function. And he also proposes small, cohesive classes (typically smaller than the 600 lines of code you mentioned), so these member variables are definitely no globals.

So when you have the opinion in a context with less than 600 lines "using global variables would be worth it", then you perfectly share the opinion of Uncle Bob. Of course, it is debatable if "3 parameters at maximum" is the ideal number, and if this rule sometimes leads to too many member variables even in small classes. IMHO this is a trade-off, there is no hard-and-fast rule where to draw the line.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • 12
    I have never understood why someone would prefer to make their class stateful by stuffing parameters into a constructor instead of just living with an actual argument. This always struck me as an enormous increase in complexity. (A real life example I've seen of this is a database connection object, which made trying to track the state of the database through the program nigh impossible when combined with dependency injection.) But perhaps *Clean Code* has more to say on that particular subject. Globals are, of course, an even worse option in regards to making things stateful. – jpmc26 Jul 13 '17 at 08:50
  • 2
    @jpmc26: one only gets an "enourmous increase of complexity" if the classes become too big and get too many member variables, so the result is not cohesive any more. – Doc Brown Jul 13 '17 at 09:07
  • 4
    I think that response ignores the difficulty in managing state (which might even be mutable) that's spread out across many classes. When a function depends not only on the arguments but also on how the object was constructed (or even modified over its lifetime), understanding its current state when you make a particular call becomes more difficult. You now have to track down *another* object's construction, just to figure out what the call will do. Adding instance variables actively increases the program's amount of state, unless you construct the object and the immediately throw it away? – jpmc26 Jul 13 '17 at 10:15
  • 1
    @jpmc26, If a function does **not** depend on the state of the object, it *might aswell* be static. An argument for using constructors instead of method arguments, is a smaller interface, which in turn makes the object a lot easier to fake. – Chris Wohlert Jul 13 '17 at 12:49
  • 3
    @jpmc26 One pattern I use regularly when refactoring is that the constructor acts like setting a context, then method arguments are specific to an action. The object isn't exactly stateful, because the state it holds never changes, but moving those common actions into this container's methods significantly improves readability where this is used (context is only set once, akin to python's context managers), and reduces duplication if multiple calls are made to the object's methods. – Izkata Jul 13 '17 at 14:53
  • 4
    +1 For saying clearly that member variables are not global variables. Many people think they are. – Tulains Córdova Jul 13 '17 at 15:53
  • @ChrisWohlert An object that takes no constructor arguments is even easier to fake than one that does. Static methods are fakeable as well, but implementing it is more difficult in statically typed, compiled languages. For example, it's only available in non-free libraries in C#, while it's trivial in Python. I've also been finding that methods not attached to an object are much simpler to manage than ones that are. And I'm starting to think that maybe we fake things too much; maybe we should avoid faking except to decouple our tests from external or very complex systems. – jpmc26 Jul 13 '17 at 19:03
  • @Izkata Immutable state is certainly easier to deal with than mutable state, but as I described above, it still creates a state. In your scenario, I have to understand the context in which the object is constructed in addition to the context in which the method is called; those two contexts may not be anywhere near each other. Indeed, with the large advocacy for dependency injection, the construction context may not even be very visible. Maybe your objects have a short, well defined lifetime, though. Also, I'm not convinced that repeating an argument across a few methods is a problem. – jpmc26 Jul 13 '17 at 19:06
  • 1
    @jpmc26 I'm not really understanding what point you are trying to make, I don't see a scenario where what you are talking about makes sense. lots of parameters in a function typically mean that there is cohesion between how those parameters are used which means it is trivial to move them into classes. At the same time, if you've already made the multiparam version, you've already got the code you need for your new class(es) and it becomes much easier to read. – Krupip Jul 13 '17 at 21:01
  • @jpmc26 You really need to read clean code to understand the whole thought process on it. Your fears are typically literally answered by the author in the book, or a nonsenical in the context of the *other* practices that go along with parameter limitting. Following SOLID basically means your 10 param function is smelly no matter what "state context" you have to understand. – Krupip Jul 13 '17 at 21:08
  • @snb Of course a 10 parameter function is a problem. But a 6 parameter constructor to bring the function down to 4 doesn't really help; it just shuffles the problem around. The best way to solve it is usually to limit the work the function does. You make a new function to do some of the work separately; then you pass its return value into the function, which replaces several parameters. Then you have two shorter functions with fewer parameters and probably a shorter call stack. This has the added benefit of making them more likely to be reusable. – jpmc26 Jul 14 '17 at 01:31
  • @jpmc26 Then I'm not sure who you are arguing with, again, you clearly need to read the book because you are having too hard of a time discussing its solutions and are making up enemies right now. They advocate for just such a solution, in fact they prefer many *many* small functions. It just happens that *some times* it is better to have a class handle the data, ***NOT that it is the only, best, or first solution you should try***. Additionally in the case that a class is the solution, it would probably be 2 smaller classes, not one monolith like you suggest. – Krupip Jul 14 '17 at 02:11
  • 2
    All here, keep it easy. You are trying to discuss things here which need real code examples, not some philosophical arguments. Codereview.SE or the chat room would be a much better place for this. – Doc Brown Jul 14 '17 at 13:52
  • (2x "by" ("by by").) – Peter Mortensen Jul 16 '17 at 16:29
  • @jpmc26, You don't fake objects with constructors, you fake interfaces. And since parameters in a constructor does not change the interface, it becomes easier to fake.The rest of your arguments are valid, but for another discussion. – Chris Wohlert Jul 17 '17 at 10:14
  • @DocBrown isnt the argument about this right "private member variables" they are global in a class right ? all functions in a class can use private member as a global variable ? I dont get your point . – Shersha Fn Jul 30 '20 at 06:25
  • 1
    @ShershaFn: this is all about size scope. Globals variables in a program or library with 50K lines of code can easily become a maintenance nightmare. Private member variables in a small, cohesive class with less than 500 lines of code are usually manageable. – Doc Brown Jul 30 '20 at 07:21
  • @DocBrown okay so that means its a good practice to share the private member variables in internal scope among the member functions with in a class , since the class is really small there will be only few private member variables , and these functions will operate on these private member variables , to achieve the task . so these functions when communicate with each other its not necessary to pass the parameters around using an argument . A private member variable do the job Is that correct? – Shersha Fn Jul 30 '20 at 07:28
  • 1
    @ShershaFn: roughly speaking, yes. But be careful with overgeneralizations. Having the same parameter in many member functions of a class which is passed around, in an unchanged manner, is a sign that it could be refactored into a private member. But when changes to the value happen in between, this can easily become a tradeoff. Don't expect to make decisions just by looking at the code structure - one has always to take the semantics and flow of execution into account. – Doc Brown Jul 30 '20 at 12:02
  • @DocBrown totally agree – Shersha Fn Jul 30 '20 at 14:06
7

is it worth using global variables to reduce the number of parameters of the functions or not?

Not

I read the first chapters of this book

Did you read the rest of the book?

A global is just a hidden parameter. They cause a different pain. But it's still pain. Stop thinking of ways to get around this rule. Think about how to follow it.

What is a parameter?

It's stuff. In a nicely labeled box. Why does it matter how many boxes you have when you can put whatever stuff in it?

It's the cost of shipping and handling.

Move(1, 2, 3, 4)

Tell me you can read that. Go on, try.

Move(PointA, PointB)

That's why.

That trick is called introduce parameter object.

And yes it's just a trick if all you're doing is counting parameters. What you should be counting is IDEAS! Abstractions! How much are you making me think about at once? Keep it simple.

Now this is the same count:

Move(xyx, y)

OW! That's horrible! What went wrong here?

It's not enough to limit the number of ideas. They must be clear ideas. What the heck is a xyx?

Now this is still weak. What's a more powerful way to think about this?

Functions should be small. No smaller than that.

Uncle Bob

Move(PointB)

Why make the function do any more than it really needs to do? The single responsibility principle isn't only for classes. Seriously, it's worth changing an entire architecture just to stop one function from turning into an overloaded nightmare with 10 sometimes related parameters some of which can't be used with others.

I've seen code where the most common number of lines in a function was 1. Seriously. I'm not saying you have to write that way but sheesh, don't tell me a global is the ONLY way you can comply with this rule. Stop trying to get out of refactoring that function properly. You know you can. It might break down into a few functions. It might actually turn into a few objects. Hell you might even break part of it out into an entirely different application.

The book isn't telling you to count your parameters. It's telling you to pay attention to the pain you're causing. Anything that fixes the pain fixes the problem. Just be aware when you're simply trading one pain for another.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 3
    "Did you read the rest of the book?" Your question is answered in the first 8 words of my post... – OiciTrap Jul 14 '17 at 10:12
  • I completely agree--the point is to decompose the problem into small pieces. Objects can really help organize these pieces and group them, they also allow a single conceptual unit to be passed as a parameter rather than a bunch of unconnected pieces. I get antsy when I see a method with more than 3 parameters. 5 is an indication my design has gone to crap and I need another round of refactoring. The best way I've found to solve design problems like parameter count is to simply refactor things into smaller, simpler units (classes/methods). – Bill K Jul 14 '17 at 16:50
  • 2
    The tone of this answer could be improved. It reads as very abrupt and harsh. For example: "Tell me you can read that. Go on, try." appears very aggressive and could be re-written as "Of the two function calls above/below, which one is easier to read?" You should try to get the point across without the aggression. The OP is just trying to learn. – Kyle A Jul 15 '17 at 13:35
  • Don't forget the OP is also trying to stay awake. – candied_orange Jul 15 '17 at 13:37
4

I would never use global variables to reduce parameters. The reason is that global variables can be altered by any function/command, therefore making the function input unreliable and prone to values that are out of scope of what the function can handle. What if the variable was changed during the execution of the function and half the function had different values than the other half?

Passing parameters on the other hand, restricts the variable's scope to only its own function such that only the function can modify a parameter once its called.

If you need to pass global variables instead of a parameter, its preferable to redesign the code.

Just my two cents.

Dimos
  • 87
  • 7
2

I'm with Uncle Bob on this and agree that more than 3 params is something to be avoided (I very rarely use more than 3 params in a function). Having lots of params on a single function creates a bigger maintenance problem and is probably a smell that your function is doing too much/has too many responsibilities.

If you are using more than 3 in a method in a OO language then you should consider are the params not related to each other in some way and therefore you should really be passing in an object instead?

Also, if you create more (smaller) functions you'll also notice that functions tend to more often have 3 params or less. Extract function/method is your friend :-).

Do not use global variables as a way to get round having more params! That is swapping one bad practice for an even worse one!

bytedev
  • 431
  • 3
  • 8
  • by global variables did you mean private member variables of a class or something else ? – Shersha Fn Jul 30 '20 at 06:29
  • @ShershaFn yes, though global as in not local to the method/function. – bytedev Aug 03 '20 at 06:48
  • but what you said is contradicting here as the author of this answer is commenting that its actually not private variables of a class that we are referring to but instead some global variables https://softwareengineering.stackexchange.com/posts/comments/910698?noredirect=1 – Shersha Fn Aug 03 '20 at 17:55
  • I am taking the term "global variables" in this instance to mean not method/function local variables. The same reasoning applies. – bytedev Aug 04 '20 at 01:26
1

Using global variables always seems an easy way to code (especially in a small program), but it will make your code difficult to extend.

Yes, you can reduce the number of parameters in a function by using an array to bind the parameters in one entity.

function <functionname>(var1,var2,var3,var4.....var(n)){}

The above function will be edited and changed to [using associative array]-

data=array(var1->var1,
           var2->var2
           var3->var3..
           .....
           ); // data is an associative array

function <functionname>(data)

I agree with robert bristow-johnson’s answer: you can even use a struct to bind data in a single entity.

TRiG
  • 1,170
  • 1
  • 11
  • 21
Narender Parmar
  • 201
  • 2
  • 10
1

A valid alternative to many function parameters is to introduce a parameter object. This is useful if you have a composed method which passes (almost) all of its parameters to a bunch of other methods.

In simple cases this is a simple DTO having nothing but the old parameters as properties.

Timothy Truckle
  • 2,336
  • 9
  • 12
1

Taking an example from PHP 4, look at the function signature for mktime():

  • int mktime ([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )

Don't you find that confusing? The function is called "make time" but it takes day, month and year parameters as well as three time parameters. How easy is it to remember which order they go in? What if you see mktime(1, 2, 3, 4, 5, 2246);? Can you understand that without having to refer to anything else? Is 2246 interpreted as a 24-hour time "22:46"? What do the other parameters mean? It would be better as an object.

Moving to PHP 5, there now is a DateTime object. Amongst its methods are two called setDate() and setTime(). Their signatures are as follows:

  • public DateTime setDate ( int $year , int $month , int $day )
  • public DateTime setTime ( int $hour , int $minute [, int $second = 0 ] )

You still have to remember that the order of the parameters goes from greatest to smallest, but it's a big improvement. Note that there isn't a single method that allows you to set all six parameters at once. You have to make two separate calls to do this.

What Uncle Bob is talking about is avoiding having a flat structure. Related parameters should be grouped together into an object, and if you have more than three parameters, it's very likely you've got a pseudo object there, which gives you the opportunity of creating an appropriate object for greater separation. Although PHP doesn't have a separate Date and Time class, you could consider that DateTime really contains a Date object and a Time object.

You could possibly have the following structure:

<?php
$my_date = new Date;
$my_date->setDay(5);
$my_date->setMonth(4);
$my_date->setYear(2246);

$my_time = new Time;
$my_time->setHour(1);
$my_time->setMinute(2);
$my_time->setSecond(3);

$my_date_time = new DateTime;
$my_date_time->setTime($my_time);
$my_date_time->setDate($my_date);
?>

Is it mandatory to set two or three parameters each time? If you want to change just the hour or just the day, it's now easy to do so. Yes, the object needs to be validated to make sure each parameter works with the others, but that was the case before anyway.

The most important thing is, is it easier to understand, and therefore maintain? The code block at the bottom is bigger than a single mktime() function, but I'd argue that's much easier to understand; even a non-programmer wouldn't have much trouble working out what it does. The goal is not always shorter code or cleverer code, but more maintainable code.

Oh, and don't use globals!

CJ Dennis
  • 659
  • 5
  • 15
1

Lots of good answers here yet most are not addressing the point. Why these rules of thumb? It is about scope, it is about dependencies and it is about proper modelling.

Globals for arguments is worse because it only looks like you made it simpler but in fact you only hid the complexity. You don't see it anymore in the prototype but you still have to be aware of it (which is hard since it is not there for you to see anymore) and getting your head around the function's logic won't help you because there may be other hidden logic interfering behind your back. Your scope went all over the place and you introduced a dependency on whatever because whatever can now mess with your variable. Not good.

The main thing to maintain for a function is that you can understand what it does by looking at the prototype and call. So the name should be clear and unambiguous. But also, the more arguments there are the harder it will be to grasp what it does. It widens the scope in your head, too many things going on, this is why you want to limit the number. It matters what kind of arguments you are dealing with though, some are worse than others. An extra optional boolean that allows for case insensitive processing does not nake the function any harder to understand so you would not want to make a big deal about it. As a side note, enums make better arguments than booleans because an enum's meaning is obvious in the call.

The typical trouble is not that you write a new function with an enormous amount of arguments, you will start with only a few when you do not realize yet how complex the problem you are solving really is. As your program evolves, argument lists gradually tend to get longer. Once a model has set in your mind you want to keep it because it is a safe reference that you know. But in retrospect the model may not be that great. You missed a step or too in the logic and you failed to recognize an entity or two. "OK... I could start over and spend a day or two refactoring my code or... I could add this argument so I can make it do the right thing after all and be done with it. For now. For this scenario. To get this bug off my plate so I can move the sticky note to done."

The more often you go with the second solution the more expensive further maintenance is going to be and the harder and unattractive a refactor will become.

There is no boiler plate solution for reducing the number of arguments in an existing function. Just grouping them into compounds is not really making things easier, it is just another way of wiping complexity under the carpet. Doing it right involves looking at the entire call stack again and recognizing what is missing or has been done wrong.

Martin Maat
  • 18,218
  • 3
  • 30
  • 57
0

Several times I have found that grouping a lot of parameters that were sent together improved things.

  • It forces you to give a good name to this agrupation of concepts that are used together. If you use those parameters together it could be that they have a relationship (or that you should not use together at all).

  • Once with the object I usually find that some funtionality can be moved that this apparently stuypid object. It is usually easy to test and with great cohesion and low coupling making thing even better.

  • The next time I need to add a new parameter I have a nice place to include it without changing the signature of a lot of methods.

So it may not work 100% of the times but ask yourself if this list of parameter should be grouped in an object. And please. Do not use untyped classes like Tuple to avoid creating the object. You are using object-oriented programming, do not mind to create more objects if you need them.

Borjab
  • 1,339
  • 7
  • 16
0

With all due respect, I'm pretty sure you've completely missed the point of having a small number of parameters in a function.

The idea is that the brain can only hold so much "active information" at one time, and if you have n parameters in a function, you have n more pieces of "active information" that needs to be in your brain to easily and accurately comprehend what the piece of code is doing (Steve McConnell, in Code Complete (a much better book, IMO), says something similar about 7 variables in a method's body: rarely do we reach that, but any more and you're losing the ability keep everything straight in your head).

The point of a low number of variables is to be able to keep the cognitive requirements of working with this code small, so you can work at it (or read it) more effectively. A side cause of this is that well factored code will tend to have less and less parameters (e.g., poorly factored code tends to group a bunch of stuff into a mess).

By passing objects instead of values, perhaps you gain a level of abstraction for your brain because now it needs to realize yes, I've got a SearchContext to work with here instead of thinking about the 15 properties that might be within that search context.

By attempting to use Global variables, you've completely gone in the entire wrong direction. Now not only have you not solved the problems of having too many parameters for a function, you've taken that problem and thrown it in a much, much better problem that you now have to carry around in your mind!

Now instead of working just at the function level, you need to be considering the global scope of your project as well (gawd, how terrible! I shudder...). You don't even have all your information in front of you in the function (crap, what's that global variable name?) (I hope this hasn't been changed by something else since I called this function).

Globally scoped variables are one of the very worst things to see in a project (large or small). They denote a disability for proper scope management, which is a highly fundamental skill for programming. They. Are. Evil.

By moving parameters out of your function and putting them into globals, you've shot yourself in the foor (or the leg, or face). And God forbid you ever decide that hey, I can re-use this global for something else... my mind trembles at the thought.

The whole ideal is to keep things easy to manage, and global variables are NOT going to do that. I'd go so far as to say they're one of the worst things you can do to go in the opposite direction.

jleach
  • 2,632
  • 9
  • 27
-1

I’ve found a highly effective approach (in JavaScript) to minimize friction between interfaces: Use a uniform interface for all modules, specifically: single param functions.

When you need multiple parameters: Use a single Object/Hash or Array.

Bear with me, I promise I’m not trolling...

Before you say “what good is a single param func?” or “Is there a difference between 1 Array and multi arg funcs?”

Well, yes. It's perhaps visually subtle, but the difference is manyfold – I explore the many benefits here

Apparently some ppl think 3 is the right # of arguments. Some think it's 2. Well, that still begs the question, "which param goes in arg[0]?" Instead of picking the option which constrains the interface with a more rigid declaration.

I guess I argue for a more radical position: don’t rely on positional arguments. I just feel it’s fragile and leads to arguments about the position of the damn arguments. Skip it and move right along to the inevitable fighting about the names of functions and variables.

Seriously though, after naming settles, hopefully you end up with code like follows, which is somewhat self documenting, not position-sensitive, and allows future param changes to be handled inside to the function:

function sendMessage({toUser, fromUser, subject, body}) { }

// And call the method like so:
sendMessage({toUser: this.parentUser, fromUser: this.currentUser, subject: ‘Example Alert’})

Dan Levy
  • 119
  • 2
  • 6
    Faking named arguments isn't really passing only one argument. – JDługosz Jul 13 '17 at 14:40
  • Why is it "faking" if I achieved a goal I set out to? I'm placing a higher priority on named arguments. Because positional args aren't obvious to calling code, and with the # of named functions a dev must memorize it isn't helpful to remember which params are where and which optional. ... Eventually you'll want to add a required param after the optionals, good luck documenting & upgrading that house of cards. – Dan Levy Jul 13 '17 at 20:30
  • 2
    Named params are also a reinforcement learning trick - humans attach more meaning to collections of words. – Dan Levy Jul 13 '17 at 20:33