Java 如何简化这组if语句?(或者,是什么让它感觉如此尴尬?)

Java 如何简化这组if语句?(或者,是什么让它感觉如此尴尬?),java,if-statement,logic,refactoring,boolean-logic,Java,If Statement,Logic,Refactoring,Boolean Logic,我的同事给我看了这段代码,我们都想知道为什么我们不能删除重复的代码 private List<Foo> parseResponse(Response<ByteString> response) { if (response.status().code() != Status.OK.code() || !response.payload().isPresent()) { if (response.status().code() != Status.NOT

我的同事给我看了这段代码,我们都想知道为什么我们不能删除重复的代码

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;
}

有没有一种更简单的方法来编写它,而不必重复
!P
?如果没有,是否存在某种情况或条件的独特属性,使得无法排除<代码>!P?

如果您希望澄清条件检查并保持相同的逻辑结果,以下可能是合适的

if (!P) {
    Log();
    return A;
}
if (S != 200) {
    if (S != 404) {
        Log();
    }
    return A;
}
return B;
或者(这是OP的首选)

或者(我个人更喜欢这个,如果你不介意的话)

可以通过删除大括号并将单行正文移动到与if语句相同的行来压缩if语句。然而,单行if语句可能会令人困惑,并且是错误的做法。从我从评论中收集到的信息来看,您的偏好是反对这种使用。虽然单行if语句可以浓缩逻辑并给出更清晰的代码外观,但清晰性和代码意图应被视为“经济”代码。需要明确的是:我个人认为,在某些情况下,如果语句是单行的,那么是合适的,但是,由于原始条件非常长,我强烈建议在这种情况下不要这样做

if (S != 200 || !P) {
    if (S != 404 || !P) Log();
    return A;
}
return B;
作为侧节点:如果
Log()语句是嵌套if语句中唯一可到达的分支,您可以使用以下逻辑标识来压缩逻辑()

(S!=200 | | p)和&(S!=404 | | p)(S!=200和&S!=404)|!P


编辑重要编辑,以重新安排内容并解决评论中提到的问题。

您可以有一组要记录的代码,并且负载的常见情况不存在

Set<Code> codes = {200, 404};
if(!codes.contains(S) && !P){
    log();
}
return new ArrayList<>();
Set
code={200404};
如果(!code.contains)和&&!P){
log();
}
返回新的ArrayList();

更正条件。

请记住简洁性并不总是最佳解决方案,在大多数情况下,编译器将优化局部命名变量

我可能会使用:

    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();
    }

助手函数可以简化嵌套条件

private List<Foo> parseResponse(Response<ByteString> response) {
    if (!isGoodResponse(response)) {
        return handleBadResponse(response);
    }
    // ...
    // ...
    // ...
    return someOtherList;
}
私有列表解析响应(响应){
如果(!isGoodResponse(响应)){
返回把手应答(应答);
}
// ...
// ...
// ...
返回其他人列表;
}

您可以反转if语句以使其更清晰,如中所示:

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

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

    return;
}
然后,您可以使用有意义的方法名称(如“responseIsValid()”和“responseIsInvalid()”)重构条件。

对我来说,代码的味道是,您正在询问响应对象,而不是告诉它。问问自己,为什么解析方法是响应对象的外部方法,而不是响应对象的方法(或者更可能是响应对象的超类)。是否可以在响应对象构造函数中调用Log()方法而不是其解析方法?当在构造函数中计算属性
status().code()
payload().isPresent()
时,是否可以将默认解析对象分配给私有属性,以便只有一个简单(且单个)的
,如果。。。否则…
将保留在Parse()中


当一个人能够用面向对象的语言编写实现继承时,应该查询每个条件语句(和表达式!),看看它是否是要提升到构造函数或调用构造函数的方法中的候选语句。所有类设计所遵循的简化确实是巨大的。

最棘手的部分是当逻辑似乎需要一个单一的、一致的值时,像
response.status()
这样的getter被多次调用。这可能是因为保证getter始终返回相同的值,但它错误地表达了代码的意图,并且使其更容易受到当前假设的影响

要解决这个问题,代码应该获得
response.status()
一次

var responseStatus = response.status();
,然后使用
responseStatus
。对于在每次获取时假定为相同值的其他getter值,应重复此操作

此外,如果以后可以在更动态的上下文中将这些代码重构为线程安全的实现,那么理想情况下,所有这些获取都将在相同的顺序点完成。要点是,您的意思是在某个特定时间点获取
响应的值,因此代码的关键部分应该在一个同步过程中获取这些值


通常,正确指定预期的数据流可以使代码更具弹性和可维护性。然后,如果有人需要向getter添加副作用或将
响应
作为抽象数据类型,那么它将更有可能继续按预期工作。

它看起来像很多代码的一个原因是它非常重复。使用变量存储重复的零件,这将有助于提高可读性:

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;
}
关于如何处理
payload缺席
逻辑的复制问题,Zachary提供了一些与我所建议的一致的好主意。还有一个选择是保留基本结构,但要使检查有效负载的原因更加明确。这将使逻辑更易于遵循,并避免您在内部
if
中使用
|
。OTOH,我自己对这种方法不太感兴趣:

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;
}
导入静态Namespace.Namespace.Status;
// ...
私有列表解析响应(响应){
Status Status=response.Status();
布尔值failedRequest=状态!=正常;
布尔loggableError=failedRequest&&status!=未找到||
!response.payload().isPresent();
如果(loggableError){
错误(“无法获取最近播放的,获取状态代码{}”,状态);
}
如果(失败请求| | loggableError){
var responseStatus = response.status();
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;
}
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;
}
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;
}
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;
}
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;
}
if (P) {
    switch (S) {
        case 200: return B;
        case 404: return A;
    }
}

Log();
return A;
// 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();
}
private List<Foo> parseResponse(Response<ByteString> response) {
    var error = validateResponse(response);
    if (error.isPresent()) {
        return error.get();
    }

    // ...
    // ...
    // ...
    return someOtherList;
}
/// 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());
}
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;
}
(A || !P) && (B || !P)
A && B || A && !P || !P && B || !P && !P
A && B || (A && !P || !P && B || !P && !P)
A && B || !P && (A && true || B && true || true && true)
A && B || !P && true
-> A && B || !P
boolean statusIsGood = response.status().code() != Status.OK.code() 
  && response.status().code() != Status.NOT_FOUND.code();

if (!statusIsGood || !response.payload().isPresent()) {
  log();
}
    private void f() {
        if(!P) {
            Log();          // duplicating Log() & return but keeping conditions separate
            return;
        } else if (S != 200) {
            if (S != 404) {
                Log();
            }
            return;
        }

        // ...
        // ...
        // ...
        return;
    }
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 (not ({200,404}.contains(S) && P)){
    log();
    return;
}
if (S !=200){
    return;
}
// other stuff
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
}
LOG.error("")    // should be log.error("")
Status.OK.code() // why couldn't it be a constant like Status.OK, or Status.getOkCode()?
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;
}