31

My colleague showed me this piece of code, and we both wondered why we couldn't seem to remove duplicated code.

private List<Foo> parseResponse(Response<ByteString> response) {
    if (response.status().code() != Status.OK.code() || !response.payload().isPresent()) {
      if (response.status().code() != Status.NOT_FOUND.code() || !response.payload().isPresent()) {
        LOG.error("Cannot fetch recently played, got status code {}", response.status());
      }
      return Lists.newArrayList();
    }
    // ...
    // ...
    // ...
    doSomeLogic();
    // ...
    // ...
    // ...
    return someOtherList;
}

Here's an alternate representation, to make it a little less wordy:

private void f() {
    if (S != 200 || !P) {
        if (S != 404 || !P) {
            Log();
        }
        return;
    }
    // ...
    // ...
    // ...
    doSomeLogic();
    // ...
    // ...
    // ...
    return;
}

Is there a simpler way to write this, without duplicating the !P? If not, is there some unique property about the situation or conditions that makes it impossible to factor out the !P?

Andrew Cheong
  • 29,362
  • 15
  • 90
  • 145
  • I wouldn't think so, purely because you are returning then logging in the sub if statement. – OcelotcR Jan 05 '18 at 10:10
  • 3
    While it doesn't necessarily make it simpler you could check for !P before the other operations, which would make the code more effective in the scenario where P is not present as the other operations won't need to be checked. – meow Jan 05 '18 at 10:11
  • 2
    if you don't want to log when `S` is not 200 but is 404, that's seem like the shorter way to do it. If you would have wanted the same action for both you would have done `(S != 200 && S != 404) || !P`, but that's not the case – Kaddath Jan 05 '18 at 10:15
  • 1
    I would fully expect the branch for having no payload present to log a different error message. – Robby Cornelissen Jan 05 '18 at 10:17
  • Ah, let me clarify. It's not code length that was my concern. It just felt, intuitively, like we missed something, seeing `!P` duplicated in two places. – Andrew Cheong Jan 05 '18 at 10:17
  • However, I think that you could factor out the `!P` just making another condition like `if(!P) {Log(); return;}`, but the problem again is that you have a return in the first `if` and then the `Log()` in the second one, so those two must remain like they are – Shinra tensei Jan 05 '18 at 10:18
  • if ((S!=200 && S!=400)|| !P){Log ...} – akshaya pandey Jan 05 '18 at 10:22
  • `Lists.newArrayList()`? But why, that's been obsolete since Java 7 and the diamond operator (`<>`)? Why not just `return new ArrayList<>();?` – Alexander Jan 05 '18 at 18:54
  • As an aside, why are you comparing codes instead of statuses? If `Status` is something akin to an `enum` (and it probably can/should be!) then you should simply write `if (response.status() != Status.OK…` etc. Java enums support this syntax. – Konrad Rudolph Jan 05 '18 at 19:54
  • Pardon an ignorant question, by why do you care about the presence of payload in any other case than 200? – Blackhawk Jan 05 '18 at 23:06
  • !P doesn't have to be duplicated. imo a better version of what you have in your question can be written without duplicating the P check and preserving the logic. However, for the most readable code I think better to decompose into separate function and retain the two P checks. All of these are given in other answers to this question. – Jibbity jobby Jan 06 '18 at 16:39

17 Answers17

29

One reason it looks like a lot of code is that it is very repetitive. Use variables to store the parts that are repeated, and that will help with the readability:

private List<Foo> parseResponse(Response<ByteString> response) {
    Status status = response.status();
    int code = status.code();
    boolean payloadAbsent = !response.payload().isPresent();

    if (code != Status.OK.code() || payloadAbsent) {
      if (code != Status.NOT_FOUND.code() || payloadAbsent) {
        LOG.error("Cannot fetch recently played, got status code {}", status);
      }
      return Lists.newArrayList();
    }
    // ...
    // ...
    // ...
    return someOtherList;
}

Edit: As Stewart points out in the comments below, if it's possible to compare response.status() and Status.OK directly, then you can eliminate the extraneous calls to .code() and use import static to access the statuses directly:

import static Namespace.Namespace.Status;

// ...

private List<Foo> parseResponse(Response<ByteString> response) {
    Status status = response.status();
    boolean payloadAbsent = !response.payload().isPresent();

    if (status != OK || payloadAbsent) {
      if (status!= NOT_FOUND || payloadAbsent) {
        LOG.error("Cannot fetch recently played, got status code {}", status);
      }
      return Lists.newArrayList();
    }
    // ...
    // ...
    // ...
    return someOtherList;
}

Regarding the question of what to do with the duplication of the payloadAbsent logic, Zachary has provided some good ideas that are compatible with what I have suggested. One more option is to keep the basic structure, but make the reason for checking the payload more explicit. This would make the logic easier to follow and save you from having to use || in the inner if. OTOH, I'm not very keen on this approach myself:

import static Namespace.Namespace.Status;

// ...

private List<Foo> parseResponse(Response<ByteString> response) {
    Status status = response.status();
    boolean failedRequest = status != OK;
    boolean loggableError = failedRequest && status!= NOT_FOUND ||
        !response.payload().isPresent();

    if (loggableError) {
      LOG.error("Cannot fetch recently played, got status code {}", status);
    }
    if (failedRequest || loggableError) {
      return Lists.newArrayList();
    }
    // ...
    // ...
    // ...
    return someOtherList;
}
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • 9
    This does not seems to address what I thought was the main jist of the question: the logical structure of the function and that fact that !P is duplicated. – Jibbity jobby Jan 05 '18 at 21:59
  • If `Status` is an `enum` then you can further shorten `code != Status.OK.code()` to `status != Status.OK`, and by use of `import static`, further shorten it to `status != OK` – Stewart Jan 05 '18 at 22:55
  • 1
    @PeterBingham Yes, because I don't see a good way to avoid using `!P` twice (or using `P` and `!P`, which I wouldn't consider an improvement). Un-nesting the `if` statements would make the logic a little easier to follow, but Zachary already suggested that. My answer addresses this from OP: _"we both wondered why there seemed like too much code"_. It _is_ a lot of code because it's a mess of method accesses that extends past the end of SO's code window. My answer addresses that. – JLRishe Jan 06 '18 at 02:52
  • The log call can be hoisted out since it completes normally, and loggableError already checks failedRequest. – Phil Jan 07 '18 at 22:05
21

If you wanted to clarify on the conditional checks and maintain the same logical outcome, the following may be appropriate.

if (!P) {
    Log();
    return A;
}
if (S != 200) {
    if (S != 404) {
        Log();
    }
    return A;
}
return B;

Or (This was OP preferred)

if (S == 404 && P) {
    return A;
}
if (S != 200 || !P) {
    Log();
    return A;
}
return B;

Or (I personally prefer this, if you don't mind the switch)

if (P) {
    switch (S) {
        case 200: return B;
        case 404: return A;
    }
}
Log ();
return A;

You could condense the if-statement by removing braces and moving the single-lined body to the same line as the if-statement. Single-line if-statements, however, can be confusing and out-right bad practice. From what I gather from the comments, your preference would be against this use. While single-line if-statements can condense logic and give the appearance of cleaner code, clarity and code intent should be valued of 'economic' code. To be clear: I personally feel there are situations where single-line if-statements are appropriate, however, as the original conditions are very long, I would strongly advise against this in this case.

if (S != 200 || !P) {
    if (S != 404 || !P) Log();
    return A;
}
return B;

As a side node: If the Log(); statement was the only reachable branch in the nested if-statements, you could use the following logical identity to condense the logic (Distributive).

(S != 200 || !P) && (S! = 404 || !P) <=> (S != 200 && S != 404) || !P

EDIT Significant edit to rearrange content and resolve issues mentioned in comments.

Zachary
  • 1,693
  • 1
  • 9
  • 13
  • 13
    So, I didn't mean shortening code quite so literally. In most places I've worked (or know about), the single-line `if` is banned, and your operators-only remix would only be acceptable in a code golf setting (: However your last suggestion is not bad! It separates the events by overall consequence and duplicates the `return` rather than the `!P`. I like it. – Andrew Cheong Jan 05 '18 at 10:25
  • 4
    I tend to stick away from single line unless both the condition and the line are concise (Even then I'm tempted to use single-line braces). The final suggestion is only good if a return statement is the only code that follows the second if-statement (as it will become tedious to duplicate the code - At which case you could always shift it to a separate method). – Zachary Jan 05 '18 at 10:27
  • Oh, I completely misunderstood. You are saying the LHS is the same as the RHS. I thought you were suggesting that whole statement be the new logic. I'll delete my comment. – Andrew Cheong Jan 05 '18 at 10:32
  • @AndrewCheong I can see the confusion. Modified to make that explicitly clear. Thanks – Zachary Jan 05 '18 at 10:38
  • Your last block of code doesn't seem to be correct since it will not log if `P` is `false` and `S` is `404`. Also with your penultimate I'd personally consider it more readable to remove the else if in favour of just an if. Your `return A` statement makes the else redundant, I believe. – Chris Jan 05 '18 at 14:20
  • 1
    @Chris Thanks! Annoyed that I missed that first one, though it's quite late. – Zachary Jan 05 '18 at 14:29
  • 3
    Since more versions are being added, to be clear for future readers, the one I preferred was the one beginning with `if (S == 404 && P) return A;`. – Andrew Cheong Jan 05 '18 at 14:50
  • 4
    Downvoted for your first example being extremely obfuscated code. It's already hard enough to follow nested if statements without bracketing some, but not others. I think the vast majority of coders, myself included, would read the `return A` as the body of the if statement before it, without really inspecting it and noticing the `Log();`. Remember: code is read more often than it's written! Vertical space is not expensive; use those brackets! – JounceCracklePop Jan 05 '18 at 22:01
  • @CarlLeth While I agree single-line if statements are often bad-practice and can be messy, I disagree that a majority of coders would be confused with the condensed version. Correct use of indentation should have been a dead giveaway. Regardless, to me, single-line if-statements obscure no more information than the use of Lambda expressions (for example). I do believe it's very rare when a single-line if-statement is appropriate and this is not one of those. Editted the post in response to the comment, thanks. – Zachary Jan 05 '18 at 23:17
  • 1
    @Zachary my first thought was that the indentation was wrong. Took me a while to see the statement in-line with the `if`. I don't see how lambdas necessarily obscure anything but that's a different discussion. I've removed my downvote since you qualified the example. – JounceCracklePop Jan 06 '18 at 09:01
  • @CarlLeth Ah I can understand that confusion. The OP make it clear he sticks away from those too, so I made it more of a side-note (Didn't remove to avoid confusion for future readers). – Zachary Jan 06 '18 at 09:05
  • 1
    @AndrewCheong [off topic] In code golf, ternary `c?F():0` is shorter than one line if `if(c)F()`. And boolean short circuit is even shorter: `c&&F()`. – user202729 Jan 06 '18 at 16:31
14

The code smell to me is that you are asking of the Response object instead of telling it. Ask yourself why the Parse method is external to the Response object instead of being a method of it (or more likely, a super-class of it). Can the Log() method perhaps be called in the Response object constructor instead of its Parse method? At the moment when the properties status().code() and payload().isPresent() are computed in the constructor would it be possible to assign a default parsed object to a private property such that only a simple (and single) if ... else ... remains in Parse()?

When one is blessed with being able to write in an object-oriented language with implementation inheritance, every conditional statement (and expression!) should be queried, to see if it is a candidate to be lifted into either the constructor or the method(s) that invoke the constructor. The simplification that can follow for all your class designs is truly immense.

Pieter Geerkens
  • 11,775
  • 2
  • 32
  • 52
  • 2
    Yes I was thinking also that maybe a new class should be created that takes the response as a constructor parameter and can therefore contain all the logic for validating responses. – Kerri Brown Jan 05 '18 at 14:44
  • 1
    @KerriBrown: Quite possibly - but difficult to be sure looking only at the code for this single method. – Pieter Geerkens Jan 05 '18 at 14:45
  • 3
    Anyway, +1 for the "tell don't ask" – Kerri Brown Jan 05 '18 at 14:46
  • 1
    @KerriBrown: Yes - that is at the core of so much best practice in both O-O and Functional programming. – Pieter Geerkens Jan 05 '18 at 14:48
  • 1
    It would be helpful if you provided a code example of what you are describing rather than just a description of it. – JLRishe Jan 05 '18 at 16:46
  • @JLRishe: Agreed - but OP did not provide any context beyond the specific method that suggested the code smell. The *code smell* is an indication that the entire class structure is twisted around - which requires much more context to make specific recommendations on. – Pieter Geerkens Jan 05 '18 at 16:50
13

Please remember that succinctness is not always the best solution and in most cases local named variables will be optimised out by the compiler.

I would probably use:

    boolean goodPayload = response.status().code() == Status.OK.code() && response.payload().isPresent();
    if (!goodPayload) {
        // Log it if payload not found error or no payload at all.
        boolean logIt = response.status().code() == Status.NOT_FOUND.code()
                || !response.payload().isPresent();
        if (logIt) {
            LOG.error("Cannot fetch recently played, got status code {}", response.status());
        }
        // Use empty.
        return Lists.newArrayList();
    }
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • It looks like you've made the code more self-describing by adding meaningful variable names. I like how you revised `hasPayload` to `goodPayload` just as I was about to comment on it, haha. Okay, agreed that's one clear approach. However, I was more trying to understand underlying logic necessitating duplication. – Andrew Cheong Jan 05 '18 at 10:34
  • 1
    I'd like to advocate the "rule" where boolean variables names start with "is", "has", "should" or any other verb that results in a question to which the answer is yes or not (or true or false). – klaar Jan 09 '18 at 09:52
  • @klaar - I prefer the *guideline* that you should *use names that make human understanding of the meaning/purpose of the code as clear as possible*. – OldCurmudgeon Jan 09 '18 at 09:56
  • 4
    Yes, of course, but because boolean variables express a yes or no answer, shouldn't the variables be named like how a human would pose the question, with actual verbs and all? I say: yes! I like your vigour in trying to paint me as a fundamentalist, but I'm very much a pragmatist. All in good form, of course. ;-) – klaar Jan 09 '18 at 10:15
10

The main thing that is actually redundant is the !P (!payload is present). If you wrote it as a boolean expression you have:

(A || !P) && (B || !P)

As you've observed, the !P seems to be repeated, and it is needlessly. In boolean algebra you can treat AND sort of like multiplication, and OR sort of like addition. So you can expand those parentheses just like with simple algebra into:

A && B || A && !P || !P && B || !P && !P

You can group all the ANDed expressions that have !P together:

A && B || (A && !P || !P && B || !P && !P)

Since all those terms have !P in them you can take it out like multiplication. When you do, you replace it with true (like you would 1, because 1 times anything is itself, true AND anything is itself):

A && B || !P && (A && true || B && true || true && true)

Notice the "true && true" is one of the OR'd expressions, so that whole grouping is always true, and you can simplify to:

A && B || !P && true
-> A && B || !P

I'm rusty on the proper notation and terminology here, maybe. But that's the gist of it.

Back to the code, if you have some complex expressions in an if statement, as others have noted you should stick them in a meaningful variable even if you're just going to use it once and throw it away.

So putting those together you get:

boolean statusIsGood = response.status().code() != Status.OK.code() 
  && response.status().code() != Status.NOT_FOUND.code();

if (!statusIsGood || !response.payload().isPresent()) {
  log();
}

Notice the variable is named "statusIsGood" even though you'll negate it. Because variables with negated names are really confusing.

Keep in mind, you can do the above kind of simplification for really complex logic, but you shouldn't always do it. You'll end up with expressions that are technically correct but nobody can tell why by looking at it.

In this case, I think the simplification clarifies the intent.

Vectorjohn
  • 411
  • 2
  • 8
  • Good observation! I always start with trying to simplify by using logic rules. You are right that sometimes it does make it harder to understand, so it's not always useful. In this case though a simpler way to get the same transformation is to just pull the common term `!P` outside of the brackets: `(A || !P) && (B || !P) <==> (A && B) || !P`. – ivant Jan 14 '18 at 07:28
  • Yeah, I was maybe trying to be too general with that answer. It works even for really complicated logical expressions. But you're right, in this case you can basically just see it, and the key to simplifying is just giving meaningful names to A, B and P and using those. – Vectorjohn Jan 15 '18 at 19:16
5

IMHO the problem is mainly the repetition and if nesting. Others have suggested using clear variables and util functions which I recommend too but you can also try separating concerns of your validations.

Correct me if I am wrong but it seems your code is trying to validate before actually processing the response so here is an alternative way to write your validations:

private List<Foo> parseResponse(Response<ByteString> response)
{
    if (!response.payload.isPresent()) {
        LOG.error("Response payload not present");
        return Lists.newArrayList();
    }
    Status status = response.status();
    if (status != Status.OK || status != Status.NOT_FOUND) {
        LOG.error("Cannot fetch recently played, got status code {}", status);
        return Lists.newArrayList();
    }
    // ...
    // ...
    // ...
    return someOtherList;
}
emont01
  • 3,010
  • 1
  • 22
  • 18
4

You could invert the if statement to make it clearer as in:

private void f() {
    if (S == 200 && P) {
        return;
    }

    if (S != 404 || !P) {
        Log();
    }

    return;
}

You could then refactor the conditionals using meaningful method names like "responseIsValid()" and "responseIsInvalid()".

Kerri Brown
  • 1,157
  • 1
  • 8
  • 18
3

Helper functions can simplify the nested conditionals.

private List<Foo> parseResponse(Response<ByteString> response) {
    if (!isGoodResponse(response)) {
        return handleBadResponse(response);
    }
    // ...
    // ...
    // ...
    return someOtherList;
}
jxh
  • 69,070
  • 8
  • 110
  • 193
3

The most awkward part is the getters like response.status() getting called many times when the logic appears to require a single, consistent value. Presumably it works because the getters are guaranteed to always return the same value, but it misexpresses the intent of the code and makes it more fragile to the current assumptions.

To fix this, the code should get response.status() once,

var responseStatus = response.status();

, then just use responseStatus thereafter. This should be repeated for the other getter values that are assumed to be the same value on each getting.

Additionally, all of these gettings would ideally be done at the same sequential point if this code might later be refactored into a thread-safe implementation in a more dynamic context. The gist is that you mean to get the values of the response at some particular point in time, so the critical section of code should get those values in one synchronous process.

In general, correctly specifying the intended data flow makes the code more resilient and maintainable. Then if someone needs to add a side-effect to a getter or make response an abstract data type, it'll be far more likely to continue working-as-intended.

Nat
  • 1,085
  • 2
  • 18
  • 35
2

Disclaimer: I will not question the signature of the presented function, nor the functionality.

It feels awkward, to me, because the function is going a lot of work by itself, rather than delegating it.

In this case, I would suggest hoisting out the validation part:

// Returns empty if valid, or a List if invalid.
private Optional<List<Foo>> validateResponse(Response<ByteString> response) {
    var status = response.status();
    if (status.code() != Status.NOT_FOUND.code() || !response.payload().isPresent()) {
        LOG.error("Cannot fetch recently played, got status code {}", status);
        return Optional.of(Lists.newArrayList());
    }

    if (status.code() != Status.OK.code()) {
        return Optional.of(Lists.newArrayList());
    }

    return Optional.empty();
}

Note that I prefer repeating the return statement rather than nest conditions. This keeps the code flat, reducing cyclomatic complexity. Plus, there's no guarantee that you will always want to return the same result for all error codes.

Afterward, parseResponse becomes easy:

private List<Foo> parseResponse(Response<ByteString> response) {
    var error = validateResponse(response);
    if (error.isPresent()) {
        return error.get();
    }

    // ...
    // ...
    // ...
    return someOtherList;
}

You can, instead, use a functional style.

/// Returns an instance of ... if valid.
private Optional<...> isValid(Response<ByteString> response) {
    var status = response.status();
    if (status.code() != Status.NOT_FOUND.code() || !response.payload().isPresent()) {
        LOG.error("Cannot fetch recently played, got status code {}", status);
        return Optional.empty();
    }

    if (status.code() != Status.OK.code()) {
        return Optional.empty();
    }

    return Optional.of(...);
}

private List<Foo> parseResponse(Response<ByteString> response) {
    return isValid(response)
        .map((...) -> {
            // ...
            // ...
            // ...
            return someOtherList;
        })
        .orElse(Lists.newArrayList());
}

Though personally I find the extra nesting a tad annoying.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

I think the following is equivalent. But as others have pointed out, code transparency can be more important that "simple" code.

if (not ({200,404}.contains(S) && P)){
    log();
    return;
}
if (S !=200){
    return;
}
// other stuff
Acccumulation
  • 3,491
  • 1
  • 8
  • 12
1

you can have a set of code which you want to log and the common condition of payload not present

Set<Code> codes = {200, 404};
if(!codes.contains(S) && !P){
    log();
}
return new ArrayList<>();

Correction in condition.

  • Ah, I'm sorry. This is my fault. It's not super clear that more stuff happens after the `if`. Let me revise my question. (I'm not the one who downvoted you.) – Andrew Cheong Jan 05 '18 at 10:20
  • Oh. I assumed you wanted to reduce the conditions. One solution could be to extract out conditions and provide a meaningful name. But if you have lines of code executing in each block then you can follow the precedence of condition based on priority. – Java Samurai Jan 05 '18 at 10:34
1

It is possible to factor out the repeated P test. The following (pseudo code) is logically equivalent to the code in your question.

private List<Foo> f() {
    List<Foo> list(); /*whatever construction*/
    if (P) {
        if (S==200) {
            // ...
            // ...
            // ...
            list.update(/*whatever*/);
        }
        else if (S!=404) {
           Log();
        }
    }
    else {
       Log();
    }
    return list;
}

In terms of readability I would go with the following (again pseudo code):

private bool write_log() {
    return (S!=200 && S!=404) || !P
}
private bool is_good_response() {
    return S==200 && P
}
private List<Foo> f() {
    List<Foo> list(); /*whatever construction*/
    if (write_log()) {
       Log();
    }
    if (is_good_response()) {
        // ...
        // ...
        // ...
        list.update(/*whatever*/);
    }
    return list;
}

with perhaps more appropriately named functions.

Jibbity jobby
  • 1,255
  • 2
  • 12
  • 26
1

Branching on an integer by comparing it to a finite set of explicit values is best handled by a switch:

if (P) {
    switch (S) {
        case 200: return B;
        case 404: return A;
    }
}

Log();
return A;
Alexander
  • 59,041
  • 12
  • 98
  • 151
1

Just use a variable like JLRishe's answer. But I'd argue that code clarity is far more important than not duplicating a simple boolean check. You can use early return statements to make this a lot clearer:

private List<Foo> parseResponse(Response<ByteString> response) {

    if (response.status().code() == Status.NOT_FOUND.code() && !response.payload().isPresent()) // valid state, return empty list
        return Lists.newArrayList();

    if (response.status().code() != Status.OK.code()) // status code says something went wrong
    {
        LOG.error("Cannot fetch recently played, got status code {}", response.status());
        return Lists.newArrayList();
    }

    if (!response.payload().isPresent()) // we have an OK status code, but no payload! should this even be possible?
    {
        LOG.error("Cannot fetch recently played, got status code {}, but payload is not present!", response.status());
        return Lists.newArrayList();
    }

    // ... got ok and payload! do stuff!

    return someOtherList;
}
user673679
  • 1,327
  • 1
  • 16
  • 35
1

With this set of conditions, I don't think there's a way around some duplication. However, I prefer to keep my conditions separated as much as reasonable & duplicate other areas when it's necessary.

If I were writing this, keeping with the current style, it would be something like this:

    private void f() {
        if(!P) {
            Log();          // duplicating Log() & return but keeping conditions separate
            return;
        } else if (S != 200) {
            if (S != 404) {
                Log();
            }
            return;
        }

        // ...
        // ...
        // ...
        return;
    }

Simplicity of code has some subjective elements and readability is hugely subjective. Given that, if I were going to write this method from scratch, this is what I would have given my biases.

private static final String ERR_TAG = "Cannot fetch recently played, got status code {}";

private List<Foo> parseResponse(Response<ByteString> response) {
    List<Foo> list = Lists.newArrayList();

    // similar to @JLRishe keep local variables rather than fetch a duplicate value multiple times
    Status status = response.status();
    int statusCode = status.code();
    boolean hasPayload = response.payload().isPresent();

    if(!hasPayload) {
        // If we have a major error that stomps on the rest of the party no matter
        //      anything else, take care of it 1st.
        LOG.error(ERR_TAG, status);
    } else if (statusCode == Status.OK.code()){
        // Now, let's celebrate our successes early.
        // Especially in this case where success is narrowly defined (1 status code)
        // ...
        // ...
        // ...
        list = someOtherList;
    } else {
        // Now we're just left with the normal, everyday failures.
        // Log them if we can
        if(statusCode != Status.NOT_FOUND.code()) {
            LOG.error(ERR_TAG, status);
        }
    }
    return list;        // One of my biases is trying to keep 1 return statement
                        // It was fairly easy here.
                        // I won't jump through too many hoops to do it though.
}

If I remove my comments, this still almost doubles the lines of code. Some would argue that this could not make the code simpler. For me, it does.

Gary99
  • 1,750
  • 1
  • 19
  • 33
0

I'm not sure what the code tries to do: honestly, logging only 404 status and returning an empty list when code is not 200 feels like your're trying to avoid a NPE...

I think it's way better something like:

private boolean isResponseValid(Response<ByteString> response){
    if(response == null){
        LOG.error("Invalid reponse!");
        return false;
    }

    if(response.status().code() != Status.OK.code()){
        LOG.error("Invalid status: {}", response.status());
        return false;
    }

    if(!response.payload().isPresent()){
        LOG.error("No payload found for response!");
        return false;
    }
    return true;
}

private List<Foo> parseResponse(Response<ByteString> response) throws InvalidResponseException{
    if(!isResponseValid(response)){
        throw InvalidResponseException("Response is not OK!");
    }

    // logic
}

if for any reason the if logic cannot be changed, i will anyway move the validation into a separate function.

Also, try to use Java naming conventions:

LOG.error("")    // should be log.error("")
Status.OK.code() // why couldn't it be a constant like Status.OK, or Status.getOkCode()?
Matteo A
  • 358
  • 5
  • 15