太多的 if 语句

huangapple go评论80阅读模式
英文:

Too many if-statements

问题

我正在使用Spring Boot和Java 11,我有一个服务变得过于臃肿且难以阅读,因为它包含了太多的if语句。

为了简单起见,假设我们有这个对象:

public class Holiday{
    private Enum type;
    private LocalDate date;
    private LocalTime begin;
    private LocalTime end;
    private boolean active;   	
}

在我的服务中,如果两个或更多假日在同一天,我需要执行一些逻辑。这就是复杂的地方,因为我必须检查不同的用例。

例如:

public class MyService{

if (holiday1 == PUBLIC && holiday2 == CUSTOM) {
  if (holiday1.getDayTime() == FULL_DAY && holiday2.getDayTime() != FULL_DAY) {
      DayTimeEnum dayTime = getDayTimeOfHoliday(holiday2.getBegin(), holiday2.getEnd());
      
      if (dayTime == FULL_DAY) {
         //doSomething...		  	  
      }
      
      if (dayTime == SECOND_HALF_OF_DAY) {
         //doSomething...		  
      }
      //....和更多的if语句....  
  }
}

上面的代码只是伪代码,用于展示我的问题的性质。在我的实际实现中,我需要进行更多的检查和验证。所以我的代码变得非常混乱,不可重用...

我做了一些研究,试图找出一些不那么硬编码的替代方案,使用更少的if语句。

想法是在它们自己的类中拥有一些验证规则,并使它们在任何地方都可重用。我希望能够根据用例链/组合这些规则。

我正在尝试以一种函数式的方式重构代码。我想拥有一系列函数/行为,我可以在必要时应用和组合。

我看过函数式接口,我喜欢组合谓词的想法,所以我希望实现一种类似于这样的方法:

Predicate<String> predicate1 = str -> str.startsWith("J");
Predicate<String> predicate2 = str -> str.length() < 4;
List<String> result = names.stream().filter(predicate1.or(predicate2)) //我也可以用predicate1.and(predicate2)组合它们
.collect(Collectors.toList());

我考虑过将所有的检查定义为类中的谓词,并在需要的不同位置使用它们,然而,谓词只能在集合上使用。

有没有更好的方法来实现这一点?

英文:

I am using Spring-Boot and Java 11 and I have a service that is becoming too bloated and hard to read because it has too many if statements.
For the sake of simplicity, let us say that we have this object:

public class Holiday{
	private Enum type;
	private LocalDate date;
	private LocalTime begin;
    private LocalTime end;
	private boolean active;   	
}

In my Service, I need to perform some logic if two or more holidays fall on the same day.
This is where it gets complicated because I have to check different use cases.

For example:

public class MyService{

if (holiday1 == PUBLIC &amp;&amp; holiday2 == CUSTOM) {
  if (holiday1.getDayTime() == FULL_DAY &amp;&amp; holiday2.getDayTime() != FULL_DAY) {
	  DayTimeEnum dayTime = getDayTimeOfHoliday(holiday2.getBegin(), holiday2.getEnd());
	  
	  if (dayTime == FULL_DAY) {
         //doSomething...		  	  
	  }
	  
	  if (dayTime == SECOND_HALF_OF_DAY) {
         //doSomething...		  
	  }
	  //....and much more if-statements....  
  }
}

The code above is only pseudocode to show the nature of my problem. In my real implementation, I need to do much more checks and validations. So my code get's very messy and not reusable...

I did some research and tried to figure out alternatives that are less hardcoded and uses less if statements.

The idea would be to have some validation-rules in their own class and make them reusable everywhere.
I would like to be able to chain/combine these rules, depending on the use-case.

I am trying to refactor the code in a functional approach. I would like to have a list of functions/behaviours, that I can apply and combine if necessary.

I've looked at the functional interfaces and I like the idea of combining predicates, so I would like to achieve a similar approach as this:

Predicate&lt;String&gt; predicate1 =  str -&gt; str.startsWith(&quot;J&quot;);
Predicate&lt;String&gt; predicate2 =  str -&gt; str.length() &lt; 4;
List&lt;String&gt; result = names.stream().filter(predicate1.or(predicate2)) //I also could combine them 
with predicate1.and(predicate2)
.collect(Collectors.toList());

I thought of defining all my checks as predicates in a class and using them in different places where needed, however, predicates can only be used on collections.

Is there a better way of achieving this?

答案1

得分: 1

因为在if语句内部没有return,这表明需要评估所有的条件。在这种情况下,可以考虑使用一个List&lt;AbstractHolidayHandler&gt;,其中每个具体实现都检查条件。例如:

class AbstractHolidayHandler {

   public final execute(...) {
      if (shouldProceed(...)) {
         proceed(...);
      }
   }

   protected abstract boolean shouldProceed(...);

   protected abstract void proceed(...);

   // shared methods for use by any implementation
   protected boolean isFullDay(...) {
      return ...
   }

   protected boolean isSecondHalfOfDay(...) {
      return ...
   }
}

具有一些具体实现,例如:

class FullDayHolidayHandler extends AbstractHolidayHandler {
 
    @Override
    protected boolean shouldProceed(...) {
      return isFullDay(...);
    }

    @Override
    protected void proceed(...) {
       // do something...
    }
}

class SecondHalfOfDayHolidayHandler extends AbstractHolidayHandler {
 
    @Override
    protected boolean shouldProceed(...) {
      return isSecondHalfOfDay(...);
    }

    @Override
    protected void proceed(...) {
       // do something...
    }
}

然后,MyService简化为:

class MyService {
   private List&lt;AbstractHolidayHandler&gt; holidayHandlers;

      ...
      holidayHandlers.forEach(h -&gt; h.execute(...);
}

请注意,至少有一些条件应该作为Holiday类本身的一部分。例如,替代:

if (holiday1 == PUBLIC &amp;&amp; holiday2 == CUSTOM) {

可以使用:

if (holiday1.isPublic() &amp;&amp; holiday2.isCustom()) {

而且,由于每个实现都是隔离的,单元测试变得更加容易。

英文:

Since there is no return within the if, this suggests all of the conditions need to be evaluated. In that case, perhaps use a List&lt;AbstractHolidayHandler&gt;, where each concrete implementation checks the condition. For example:

class AbstractHolidayHandler {

   public final execute(...) {
      if (shouldProceed(...)) {
         proceed(...);
      }
   }

   protected abstract boolean shouldProceed(...);

   protected abstract void proceed(...);

   // shared methods for use by any implementation
   protected boolean isFullDay(...) {
      return ...
   }

   protected boolean isSecondHalfOfDay(...) {
      return ...
   }
}

With a couple concrete implementations such as:

class FullDayHolidayHandler extends AbstractHolidayHandler {
 
    @Override
    protected boolean shouldProceed(...) {
      return isFullDay(...);
    }

    @Override
    protected void proceed(...) {
       // do something...
    }
}

and:
class SecondHalfOfDayHolidayHandler extends AbstractHolidayHandler {

    @Override
    protected boolean shouldProceed(...) {
      return isSecondHalfOfDay(...);
    }

    @Override
    protected void proceed(...) {
       // do something...
    }
}

then MyService simplifies to:

class MyService {
   private List&lt;AbstractHolidayHandler&gt; holidayHandlers;

      ...
      holidayHandlers.forEach(h -&gt; h.execute(...);
}

Note that at least some of the conditionals should probably be part of the Holiday class itself. For example, instead of:

if (holiday1 == PUBLIC &amp;&amp; holiday2 == CUSTOM) {

do

if (holiday1.isPublic() &amp;&amp; holiday2.isCustom()) {

And since each implementation is isolated, it becomes much easier to unit test.

答案2

得分: 0

你可以创建一个类似于查找表的 HashMap,其中你的键将是 FULL_DAYSECOND_HALF_DAY 等,而值将保持你的逻辑,类似于一些服务类。

英文:

You can create a HashMap similar to a look-up table, where your keys will be FULL_DAY, SECOND_HALF_DAY etc. and values will keep your logic as some service classes.

答案3

得分: 0

不一定是更好的方法或任何事情,但不要忘记你可以提前将谓词链接在一起,以便为以后的使用提供管道。这样做可以更容易地推理:

Predicate<String> predicateLongerThan4 = tested -> tested.length() > 4;
Predicate<String> predicateStartsWithUpperCase = tested -> tested.substring(0, 1).toUpperCase().equals(tested.substring(0, 1));
Predicate<String> longerThan4AndStartsWithUpperCase = predicateLongerThan4.and(predicateStartsWithUpperCase);

哦,而且不要对你可以使用的方式和位置感到太受限制。当我刚刚编写这个并想要进行合理性检查时,我使用了 Optional.of 作为实例。

英文:

Not necessarily a better way or anything, but don't forget that you can chain predicates together in advance to provide pipelines for later use. It can be easier to reason about things that way:

Predicate&lt;String&gt; predicateLongerThan4 = tested-&gt;tested.length() &gt; 4;
Predicate&lt;String&gt; predicateStartsWithUpperCase = tested-&gt;tested.substring(0,1).toUpperCase().equals(tested.substring(0,1));
Predicate&lt;String&gt; longerThan4AndStartsWithUpperCase = predicateLongerThan4.and(predicateStartsWithUpperCase);

Oh, and don't feel too constrained by how and where you can use something. When I wrote this just now and wanted to sanity check it I used Optional.of for instance.

huangapple
  • 本文由 发表于 2020年7月27日 20:18:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/63115190.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定