Split Method with Optional Parameters

 class​ Logbook {
 
 static​ ​final​ Path CREW_LOG = Paths.get(​"/var/log/crew.log"​);
 
» List<String> readEntries(LocalDate date) ​throws​ IOException {
 final​ List<String> entries = Files.readAllLines(CREW_LOG,
  StandardCharsets.UTF_8);
 if​ (date == ​null​) {
 return​ entries;
  }
 
  List<String> result = ​new​ LinkedList<>();
 for​ (String entry : entries) {
 if​ (entry.startsWith(date.toString())) {
  result.add(entry);
  }
  }
 return​ result;
  }
 }

Booleans as you’ve seen them in Split Method with Boolean Parameters aren’t the only indicators of methods that do too much. Optional parameters have the same problem, but they’re harder to spot.

This time, we’re looking at how data can be read from the log via the method readEntries(). Log entries have a date, and we can select entries marked by the input parameter date. If you insert null instead of a concrete date value, then readEntries() returns all log entries. Consider the following usage:

 List<String> completeLog = logbook.readEntries(​null​);
 
 final​ LocalDate moonLanding = LocalDate.of(1969, Month.JULY, 20);
 List<String> moonLandingLog = logbook.readEntries(moonLanding);

The semantics of null here essentially mean that the date parameter is optional. This is really just a different version of a boolean method parameter.

As before, a method with optional parameters is a method that does more than one thing. Even worse, it’s hard to see what to expect from a method call with a null parameter.

Here’s how we can refactor this:

 class​ Logbook {
 
 static​ ​final​ Path CREW_LOG = Paths.get(​"/var/log/crew.log"​);
 
» List<String> readEntries(LocalDate date) ​throws​ IOException {
  Objects.requireNonNull(date);
 
  List<String> result = ​new​ LinkedList<>();
 for​ (String entry : readAllEntries()) {
 if​ (entry.startsWith(date.toString())) {
  result.add(entry);
  }
  }
 return​ result;
  }
 
» List<String> readAllEntries() ​throws​ IOException {
 return​ Files.readAllLines(CREW_LOG, StandardCharsets.UTF_8);
  }
 }

The solution to the problem here is essentially the same as in Split Method with Boolean Parameters: we split the method into two methods. Each of them captures one of the control-flow branches.

In the code above, the readEntries() method no longer permits an optional date parameter and throws a NullPointerException in this case instead. The semantics of the former optional parameter are now captured in the readAllEntries() method, which requires no parameters. This even makes it easier to use. Now the usage looks like this:

 List<String> completeLog = logbook.readAllEntries();
 
 final​ LocalDate moonLanding = LocalDate.of(1969, Month.JULY, 20);
 List<String> moonLandingLog = logbook.readEntries(moonLanding);

As you can see, this is much more understandable, since the method name clearly communicates that all entries will be read. Apart from getting rid of the null input value, other usages of readEntries() are unchanged.

On top of the improved readability, we’re now even avoiding explicit usages of null in our code. The more we get rid of null values, the less likely it is that we’ll trigger NullPointerExceptions unintentionally.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.184.102