Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it bad to have many global functions?

Tags:

xcode

swift

cocoa

I'm relatively new to software development, and I'm on my way to completing my first app for the iPhone.

While learning Swift, I learned that I could add functions outside the class definition, and have it accessible across all views. After a while, I found myself making many global functions for setting app preferences (registering defaults, UIAppearance, etc).

Is this bad practice? The only alternate way I could think of was creating a custom class to encapsulate them, but then the class itself wouldn't serve any purpose and I'd have to think of ways to passing it around views.

like image 230
Skishnot Avatar asked Dec 20 '14 20:12

Skishnot


People also ask

Is it bad to have a lot of global variables?

Using global variables causes very tight coupling of code. Using global variables causes namespace pollution. This may lead to unnecessarily reassigning a global value. Testing in programs using global variables can be a huge pain as it is difficult to decouple them when testing.

Are global functions bad practice?

Functions can access global variables and modify them. Modifying global variables in a function is considered poor programming practice.

Is it better to use more number of global variables?

The value of a global variable can be changed accidently as it can be used by any function in the program. If we use a large number of global variables, then there is a high chance of error generation in the program.

Is global variables a bad practice?

Non-const global variables are evil because their value can be changed by any function. Using global variables reduces the modularity and flexibility of the program. It is suggested not to use global variables in the program. Instead of using global variables, use local variables in the program.


2 Answers

Global functions: good (IMHO anyway, though some disagree)

Global state: bad (fairly universally agreed upon)

By which I mean, it’s probably a good practice to break up your code to create lots of small utility functions, to make them general, and to re-use them. So long as they are “pure functions”

For example, suppose you find yourself checking if all the entries in an array have a certain property. You might write a for loop over the array checking them. You might even re-use the standard reduce to do it. Or you could write a re-useable function, all, that takes a closure that checks an element, and runs it against every element in the array. It’s nice and clear when you’re reading code that goes let allAboveGround = all(sprites) { $0.position.y > 0 } rather than a for…in loop that does the same thing. You can also write a separate unit test specifically for your all function, and be confident it works correctly, rather than a much more involved test for a function that includes embedded in it a version of all amongst other business logic.

Breaking up your code into smaller functions can also help avoid needing to use var so much. For example, in the above example you would probably need a var to track the result of your looping but the result of the all function can be assigned using let. Favoring immutable variables declared with let can help make your program easier to reason about and debug.

What you shouldn’t do, as @drewag points out in his answer, is write functions that change global variables (or access singletons which amount to the same thing). Any global function you write should operate only on their inputs and produce the exact same results every time regardless of when they are called. Global functions that mutate global state (i.e. make changes to global variables (or change values of variables passed to them as arguments by reference) can be incredibly confusing to debug due to unexpected side-effects they might cause.

There is one downside to writing pure global functions,* which is that you end up “polluting the namespace” – that is, you have all these functions lying around that might have specific relevance to a particular part of your program, but accessible everywhere. To be honest, for a medium-sized application, with well-written generic functions named sensibly, this is probably not an issue. If a function is purely of use to a specific struct or class, maybe make it a static method. If your project really is getting too big, you could perhaps factor out your most general functions into a separate framework, though this is quite a big overhead/learning exercise (and Swift frameworks aren’t entirely fully-baked yet), so if you are just starting out so I’d suggest leaving this for now until you get more confident.

* edit: ok two downsides – member functions are more discoverable (via autocomplete when you hit .)

like image 86
Airspeed Velocity Avatar answered Nov 10 '22 15:11

Airspeed Velocity


Updated after discussion with @AirspeedVelocity

Global functions can be ok and they really aren't much different than having type methods or even instance methods on a custom type that is not actually intended to contain state.

The entire thing comes down mostly to personal preference. Here are some pros and cons.

Cons:

  • They sometimes can cause unintended side effects. That is they can change some global state that you or the caller forgets about causing hard to track down bugs. As long as you are careful about not using global variables and ensure that your function always returns the same result with the same input regardless of the state of the rest of the system, you can mostly ignore this con.

  • They make code that uses them difficult to test which is important once you start unit testing (which is a definite good policy in most circumstances). It is hard to test because you can't mock out the implementation of a global function easily. For example, to change the value of a global setting. Instead your test will start to depend on your other class that sets this global setting. Being able to inject a setting into your class instead of having to fake out a global function is generally preferable.

  • They sometimes hint at poor code organization. All of your code should be separable into small, single purpose, logical units. This ensures your code will remain understandable as your code base grows in size and age. The exception to this is truly universal functions that have very high level and reusable concepts. For example, a function that lets you test all of the elements in a sequence. You can also still separate global functions into logical units by separating them into well named files.

Pros:

  • High level global functions can be very easy to test. However, you cannot ignore the need to still test their logic where they are used because your unit test should not be written with knowledge of how your code is actually implemented.

  • Easily accessible. It can often be a pain to inject many types into another class (pass objects into an initializer and probably store it as a property). Global functions can often remove this boiler plate code (even if it has the trade off of being less flexible and less testable).

In the end, every code architecture decision is a balance of trade offs each time you go to use it.

like image 38
drewag Avatar answered Nov 10 '22 14:11

drewag