129

Consider a parameterless (edit: not necessarily) function that performs a single line of code, and is called only once in the program (though it is not impossible that it'll be needed again in the future).

It could perform a query, check some values, do something involving regex... anything obscure or "hacky".

The rationale behind this would be to avoid hardly-readable evaluations:

if (getCondition()) {
    // do stuff
}

where getCondition() is the one-line function.

My question is simply: is this a good practice? It seems alright to me but I don't know about the long term...

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
deprecated
  • 3,297
  • 3
  • 20
  • 26
  • 9
    Unless your code fragment is from a OOP language with implicit receiver (e.g. this) this certainly would be a bad practice, as getCondition() most probably relies on global state ... – Ingo Sep 12 '11 at 14:38
  • 2
    He may have meant to imply that getCondition() could have arguments? – user606723 Sep 12 '11 at 16:25
  • I certainly said "parameterless" functions but now I think it, it's not mandatory for the question. That said, good point Ingo on keeping it as local as possible. – deprecated Sep 12 '11 at 17:06
  • 24
    @Ingo -- some things really have Global state. Current time, hostnames, port numbers etc. are all valid "Globals". The design error is making a Global out of something that is inherently not global. – James Anderson Sep 13 '11 at 01:42
  • 1
    Why don't you just inline `getCondition`? If it's as small and infrequently used as you say it is then giving it a name is not accomplishing anything. –  Sep 13 '11 at 04:22
  • 1
    well, that was my question! – deprecated Sep 13 '11 at 06:01
  • 12
    davidk01: Code readability. – wjl Sep 13 '11 at 06:02
  • 1
    Yes, if you can look into the future and determine that such-and-such a function will never be used elsewhere -- and that no one will ever have trouble grasping the code you have written -- go ahead and unroll the function. If, on the other hand, your clairvoyance plug-in isn't working properly... – Michael Lorton Sep 13 '11 at 11:59
  • @JamesAnderson Current time shouldn't be treated as a global. Imagine unit testing a leap-year bug where the "current time" has to be set to something specific. – Izkata Jan 09 '15 at 18:13
  • 1
    @Ingo: I think you've gone a bridge too far there. `getCondition()` could simply be a private method which separate the condition logic from the rest of the logic, e.g. for readability's sake. I don't think you can assume that `getCondition()` must invariably depend on global state (let alone James Anderson's response that some things simple are global state without being wrong) – Flater Oct 27 '21 at 11:27

12 Answers12

249

Depends on that one line. If the line is readable and concise by itself, the function may not be needed. Simplistic example:

void printNewLine() {
  System.out.println();
}

OTOH, if the function gives a good name to a line of code containing e.g. a complex, hard to read expression, it is perfectly justified (to me). Contrived example (broken into multiple lines for readability here):

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
Péter Török
  • 46,427
  • 16
  • 160
  • 185
  • 102
    +1. The magic word here is "Self-documenting code". – Konamiman Sep 12 '11 at 15:17
  • 8
    A good example of what Uncle Bob would call "encapsulating conditionals." – Anthony Pegram Sep 12 '11 at 18:07
  • 1
    Isn't it better to make this a private method of the `taxPayer` class, rather than rely on a global taxPayer variable? – Aditya Sep 12 '11 at 19:03
  • 1
    @Aditya, the example is deliberately contrived. – Winston Ewert Sep 12 '11 at 19:20
  • 13
    @Aditya: Nothing says that `taxPayer` is global in this scenario. Perhaps this class is `TaxReturn`, and `taxPayer` is an attribute. – Adam Robinson Sep 12 '11 at 22:14
  • 2
    Additionally it allows you to document the function in a way that can be picked up by e.g. javadoc and be publicly visible. –  Sep 13 '11 at 07:04
  • Private method? NO! It's a predicate and should be nonmember http://www.gotw.ca/gotw/084.htm – spraff Sep 13 '11 at 10:27
  • 1
    What is the benefit of doing this over putting a comment above the line that says: //Is tax payer eligible for tax refund? – dallin Sep 04 '13 at 02:42
  • @dallin, the above is a simplistic example. In real life, your method may contain multiple, or even lots of such chunks of code instead of a single one. Then you start to see the benefit in readability and maintainability (even if none of these chunks are needed anywhere else in your codebase - which is rarely the case in real life). Plus what you won't immediately see is that a series of short method calls is much easier to JIT optimize than one long method. – Péter Török Sep 04 '13 at 09:24
  • 1
    @PéterTörök I guess what I would really like to get my hands on is an example of a codebase that uses this design paradigm in a way that it really works. That way I think I could wrap my head around it and see its value. Every situation I've seen like this was more an example of frustration. It became a pain to follow the code and with large projects, it became an even bigger pain of organizing the code with that many functions. More often than not, I ended up thinking, "where did I put that?" It seemed to hurt my productivity just from having to keep track of and organize so many functions – dallin Sep 04 '13 at 19:26
  • 2
    @dallin, Bob Martin's Clean Code shows lots of semi-real life code examples. If you have too many functions in a single class, maybe your class is too big? – Péter Török Sep 06 '13 at 16:09
  • @dallin this has several advantages over a comment. We spend a lot more time reading code than writing it. By using a function it's much easier for a reader to not read the implementation if they aren't interested in it, and concentrate on other stuff. The point at which the function ends is as clear as when it starts, which might not be true with a comment. Multiple languages in one file make things harder to read, it's better to make the code expressive instead of sprinkling in bits of English. – bdsl Aug 10 '17 at 00:53
  • @dallin If you want to inline the code you still don't need a comment, you can put that information in a temp variable name, e.g. bool isTaxPayerEligibleForTaxRefund = (taxPayer.isFemale() ... – bdsl Aug 10 '17 at 00:56
67

Yes, this can be used to satisfy best practices. For instance, it is better to have a clearly-named function do some work, even if it is only one line long, than to have that line of code within a larger function and need a one-line comment explaining what it does. Also, neighbouring lines of code should perform tasks at the same abstraction level. A counterexample would be something like

startIgnition();
petrolFlag |= 0x006A;
engageChoke();

In this case it is definitely better to move the middle line into a sensibly-named function.

Kilian Foth
  • 107,706
  • 45
  • 295
  • 310
  • 15
    That 0x006A is lovely :D A well known constant of carbonized fuel with additives a, b and c. – Coder Sep 12 '11 at 15:33
  • 2
    +1 I'm all for self-documenting code :) by the way, I think I agree on keeping a constant abstraction level through blocks, but I couldn't explain why. Would you mind expanding that? – deprecated Sep 12 '11 at 17:18
  • 2
    I would say that a constant abstraction level leads to... consistency... which is a good thing. – WernerCD Sep 12 '11 at 18:31
  • 3
    If you're just saying that `petrolFlag |= 0x006A;` without any kind of decision making, it would be better to simply say `petrolFlag |= A_B_C;` _without_ an additional function. Presumably, `engageChoke()` should only be called if `petrolFlag` meets a certain criteria and that should clearly say 'I need a function here.' Just a minor nit, this answer is basically spot on otherwise :) – Tim Post Sep 12 '11 at 18:56
  • 9
    +1 for pointing out that code within a method should be in the same abstraction level. @vemv, it is good practice because it makes the code easier to understand, by avoiding the need for your mind to jump up and down between different levels of abstraction while reading the code. Tying abstraction level switches to method calls/returns (i.e. structural "jumps") is a nice way to make the code more fluent and clean. – Péter Török Sep 13 '11 at 06:55
  • Is "0x006A" a known brand in some cultural circles or is it "just" the magic constant? –  Sep 13 '11 at 07:06
  • 22
    No, it's a completely made-up value. But the fact that you wonder about it just emphasizes the point: if the code had said `mixLeadedGasoline()`, you wouldn't have to! – Kilian Foth Sep 13 '11 at 08:22
37

I think that in many cases such a function is good style, but you may consider a local boolean variable as alternative in cases when you don't need to use this condition somewhere in other places e.g.:

bool someConditionSatisfied = [complex expression];

This will give a hint to code reader and save from introducing a new function.

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
cybevnm
  • 471
  • 4
  • 5
  • 1
    The bool is particularly beneficial if the condition function name would be difficult or possibly misleading (e.g. `IsEngineReadyUnlessItIsOffOrBusyOrOutOfService`). – dbkk Sep 12 '11 at 20:01
  • 2
    I experienced this advice a bad idea: the bool **and** the condition distract attention from the core business of the function. Also, a style preferring local variables make refactoring harder. – Wolf Jan 09 '15 at 09:22
  • 1
    @Wolf OTOH, I prefer this to a function call in order to reduce the "abstraction depth" of code. IMO, jumping into a function is a larger context switch than an explicit and direct couple lines of code, especially if it just returns a boolean. – Kache Oct 19 '16 at 04:44
  • @Kache I think it depends on whether you code object-oriented or not, in the OOP case, the use of member function keeps the design much more flexible. It really depends on the context... – Wolf Oct 19 '16 at 06:03
22

In addition to Peter's answer, if that condition may need to be updated at some point in the future, by having it encapsulated the way you suggest you would only have a single edit point.

Following Peter's example, if this

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

becomes this

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isMutant() 
        && (taxPayer.getNumberOfThumbs() > 2 
        || (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}

You make a single edit and it's updated universally. Maintainability wise, this is a plus.

Performance wise, most optimizing compilers will remove the function call and inline the small code block anyway. Optimizing something like this can actually shrink the block size (by deleting the instructions needed for the function call, return, etc) so it's normally safe to do even in conditions that might otherwise prevent inlining.

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
Stephen
  • 2,121
  • 1
  • 14
  • 24
  • 2
    I'm already aware of the benefits of keeping a single edit point - that's why the question says "...called once". Nonetheless it's great to know about those compiler optimizations, I always thought they followed this kind of instructions literally. – deprecated Sep 12 '11 at 17:16
  • Splitting out a method to test a simple condition tends to imply that a condition may be changed by changing the method. Such implication is useful when it's true, but can be dangerous when it's not. For example, suppose that code needs to divide things into lightweight objects, heavy green objects, and heavy non-green objects. Objects have a quick "seemsGreen" property which is reliable for heavy objects, but may return false positives with lightweight objects; they also have a "measureSpectralResponse" function which is slow, but will work reliably for all objects. – supercat Sep 02 '14 at 19:37
  • The fact that `seemsGreen` will be unreliable with lightweight objects will be irrelevant if code never cares whether lightweight objects are green. If the definition of "lightweight" changes, however, such that some non-green objects which return true for `seemsGreen` aren't reported as "lightweight", then such a change to the definition of "lightweight" could break the code that tests for objects being "green". In some cases, testing for green-ness and weight together in the code may make the relationship between the tests clearer than if they're separate methods. – supercat Sep 02 '14 at 19:41
  • This is also a nice example where it is absolutely clear _what_ the code does (checks gender, number of children, age, employer) but that doesn't give us a clue about the meaning of the code. The function "getsChildCareVouchers()" might have very, very similar code. Or "nextToBeFired()" at the wrong company. – gnasher729 Oct 27 '21 at 12:16
6

In addition to readability (or in complement of it) this allows to write functions at the proper level of abstraction.

tylermac
  • 338
  • 2
  • 8
  • 2
    I'm afraid I don't understand what you meant... – deprecated Sep 12 '11 at 20:28
  • 2
    @vemv: I think he means by "proper level of abstraction" that you don't end up with code that has different levels of abstraction mixed (i.e. what Kilian Foth already said). You don't want your code to handle seemingly insignificant details (e.g. the formation of all bonds in fuel) when the surrounding code is considering a more high-level view of the situation at hand (e.g. running an engine). The former clutters the latter and makes your code less easy to read and maintain as you will have to consider all levels of abstraction at once at any time. – Egon Sep 12 '11 at 22:02
4

It depends. Sometimes it's better to encapsulate an expression in a function/method, even if it's only one line. If it's complicated to read or you need it in multiple places, then I consider it a good practice. In the long term it's easier to maintain, as you've introduced a single point of change and better readability.

However, sometimes it's just something you don't need. When the expression is easy to read anyway and/or just appears in one place, then don't wrap it.

Falcon
  • 19,248
  • 4
  • 78
  • 93
3

I've done this exact thing just recently in an application that I've been refactoring, to make explicit the actual meaning of the code sans comments:

protected void PaymentButton_Click(object sender, EventArgs e)
    Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;

    CheckInputs();

    if(HaveError())
        return;

    ...
}
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
joshperry
  • 195
  • 4
  • Just curious, what language is that? – deprecated Sep 12 '11 at 20:27
  • 5
    @vemv: Looks like C# to me. – Scott Mitchell Sep 12 '11 at 20:37
  • I also prefer extra identifiers over comments. But does this really differ from [introducing a local variable](http://programmers.stackexchange.com/a/107686/106929) to keep the `if` short? This local (lambda) approach makes the `PaymentButton_Click` function (as a whole) harder to read. The `lblCreditCardError` in your example seems to be a member, so also `HaveError` is a (private) predicate that is valid for the object. I'd tend to downvote this, but I'm not a C# programmer, so I resist. – Wolf Jan 09 '15 at 09:35
  • @Wolf Hey man, yeah. I wrote this quite a while ago :) I definitely do things quite differently now days. In fact, looking at the label content to see if there was an error makes me cringe... Why didn't I just return a bool from `CheckInputs()`??? – joshperry Jan 09 '15 at 17:38
3

I think if you only have a few of them then it is okay but the issue comes up when there are a lot of them in your code. And when the compiler runs or the interpitor (depending on the language you use) It is going to that function in memory. So lets say you have 3 of them I dont think the computer will notice but if you start having 100's of those little things then the system has to register functions in memory that are only called once and then not destroyed.

WojonsTech
  • 595
  • 3
  • 10
  • According to Stephen's answer this might not always happen (although trusting blindly in compilers' magic can't be good anyway) – deprecated Sep 13 '11 at 05:59
  • 1
    yeah it should get cleared out depending on a lot of facts. If its an interpated language the system will fall for the single line functions everytime unless you install something for cache which still may not do the trick. Now when it comes to compilers it just matters onn the day of the weak and the placment of the planates if the compiler will be your friend and clear out your little mess or if its going to thinkk you really need it. I remeber that if you know the exact amoutn of times a loop will always run its some times better to just copy and paste it that many times then to loop. – WojonsTech Sep 13 '11 at 06:07
  • +1 for alignment of the planets :) but your last sentence sounds totally nuts to me, do you really do that? – deprecated Sep 13 '11 at 07:41
  • It really depends Most of the time no i do not unless i am getting the correct amount of payment to check if it does speed incress and other things like that. But in older compilers it was better to copy and paste it then to leave the compilere to figure it out 9/10. – WojonsTech Sep 14 '11 at 00:58
0

If the language supports it, I usually use labeled anonymous functions to accomplish this.

someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
    #do stuff...

IMHO this is a good compromise because it gives you the readability benefit of not having the complicated expression cluttering the if condition while avoiding cluttering the global/package namespace with small throw-away labels. It has the added benefit that the function "definition" is right where its being used making it easy to modify and read the definition.

It doesn't have to only be predicate functions. I like to encase repeated boiler-plate in small functions like this as well (It works particularly well for pythonic list generation without cluttering the bracket syntax). For example, the following oversimplified example when working with PIL in python

#goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L')) 
arr_list = [im_2_arr(image) for image in image_list]
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
crasic
  • 109
  • 7
  • Why " lambda p: True if [complicated expression involving p] else False " instead of " lambda p: [complicated expression involving p] " ? 8-) – Dr. Hans-Peter Störr Sep 13 '11 at 06:42
  • @hstoerr its in the comment right below that line. I like to make it explicitly known that we someCondition is a predicate. While its strictly unnecessary, I write a lot of scientific scripts read by people who don't code that much, I personally think its more appropriate to have the extra terseness rather than have my colleagues confused because they don't know that `[] == False` or some other similar pythonic equivalence that isn't 'always' intuitive. Its basically a way to flag that someCondition is, in fact, a predicate. – crasic Sep 13 '11 at 06:56
  • Just to clear my obvious mistake `[] != False` but `[]` is False as a when cast to a bool – crasic Sep 13 '11 at 07:03
  • @crasic: when I don't expect my readers to know that [] evaluates to False, I prefer doing `len([complicated expression producing a list]) == 0`, rather than using `True if [blah] else False` which still requires the reader to know that [] evaluates to False. – Lie Ryan Sep 13 '11 at 07:33
0

Moving that one line into a well named method makes your code easier to read. Many others have already mentioned that ("self-documenting code"). The other advantage of moving it into a method is that it makes it easier to unit test. When it's isolated into it's own method, and unit tested, you can be sure that if/when a bug is found, it won't be in this method.

Andrew
  • 213
  • 1
  • 3
0

There are already a lot of good answers, but there is a special case worth mentioning.

If your one-line statement needs a comment, and you are able to clearly identify (which means: name) its purpose, consider extracting a function while enhancing the comment into API doc. This way you make the function call easier faster and to understand.

Interestingly, the same can be done if there is currently nothing to do, but a comment reminding of expansions needed (in the very near future1)), so this,

def sophisticatedHello():
    # todo set up
    say("hello")
    # todo tear down

could as well changed into this

def sophisticatedHello():
    setUp()
    say("hello")
    tearDown()

1) you should be really sure about this (see YAGNI principle)

Deduplicator
  • 8,591
  • 5
  • 31
  • 50
Wolf
  • 630
  • 1
  • 6
  • 24
0

Here is a pretty good rule of thumb I use:

Would it be a good (or at least reasonable) idea to add a comment explaining that line? If yes, then refactor to a function with a name that gives the same information as the comment you planned to write.

This approach works perfectly for mathematical code, like

fun distanceSquared(p1, p2) {
    return (p1.x - p2.x)**2 + (p1.y - p2.y)**2;
}

fun distance(p1, p2) {
    return sqrt(distanceSquared(p1, p2));
}

You might wonder about the first function. It's actually quite useful sometimes for performance reasons. For instance, if(distance(p1, p2) > distance(p3, p4)) gives the same result if you switch to distanceSquared, but you get to skip two sqrt calculations. So you might want to write this:

fun distanceCompare(p1, p2, p3, p4) {
    return distanceSquared(p1, p2) - distanceSquared(p3, p4);
}

That will return a negative number if the distance (p1,p2) is smaller than (p3,p4), zero if equal, and a positive number otherwise.

klutt
  • 1,428
  • 1
  • 10
  • 25