Extend While-Loop with Iterator Design Pattern

The “while” loop is a common structure in programming languages, but sometimes it may bring a readability issue. This post describes how the Iterator design pattern can improve readability of some while-loop implementations. Consider a simple example where we need to print some text line by line in the while loop:

   public void print1(BufferedReader reader) throws IOException {
      String line = reader.readLine();
      while (line != null) {
         System.out.println(line);
         line = reader.readLine();
      }
   }

This code works well but there is a logic duplication. The “reader.readLine()” is repeated twice, that adds complexity and impacts readability. Let’s remove this duplication; see the code snippet bellow:

   public void print2(BufferedReader reader) throws IOException {
      while (true) {
         String line = reader.readLine();
         if (line == null)
            break;
         System.out.println(line);
      }
   }

We removed the logic duplication, but there is some ugliness out there: the “while (true)” implies infinite loop and the business logic is interrupted by the IF statement. Can we achieve better readability? Yes, we can. Let’s use the standard Iterator design pattern to encapsulate the logic. Now our while-loop appears with maximum readability:

   public void print3(final BufferedReader reader) {
      Iterator iterator = new LineIterator(reader);
      while (iterator.hasNext()) {
         System.out.println(iterator.next());
      }
   }

Below is the basic implementation of the Iterator. Please don’t use it in the production code as it lacks exception handling and may have other issues; for example, it expects the “hasNext” and “next” methods being invoked one after another, so invoking hasNext twice may lead to a record being lost. This implementation is provided just to describe the topic better.

   public class LineIterator implements Iterator {

      private BufferedReader bufferedReader;
      private String line;

      public LineIterator(BufferedReader reader) {
         bufferedReader = reader;
      }

      public boolean hasNext() {
         try {
            line = bufferedReader.readLine();
         } catch (IOException e) {
            e.printStackTrace();
         }
         return line != null;
      }

      public String next() {
         return line;
      }

      public void remove() { }
   }

Summary: the standard Iterable and Iterator interfaces greatly improve readability of Java code. Sometimes it is difficult to spot a piece of code that would benefit from using the Iterator design pattern, but anyway, try to keep an eye on this issue during your code reviews. Always justify usage of design patterns otherwise don’t use them.

Leave a Reply

Your email address will not be published. Required fields are marked *