Conway’s Game of Life and the Flyweight Pattern

2014-04-14

flyweightConway’s Game of Life is fascinating, both from a functional and from a technical perspective.

This may explain why it’s often used for code retreats. Code retreats are a fun way to learn.

It’s amazing how working with new pairs gives you new insights virtually every time.

At the last code retreat that I attended, one of my pairs suggested we use the Flyweight pattern for cells:

A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context — it’s indistinguishable from an instance of the objects that is not shared.

When the Design Patterns book (which contains the above quote) came out, I remember having many aha moments. It was so cool to see all these patterns that I had used before and finally have a name for them so I could discuss them with my peers more efficiently!

I did not have an aha moment when reading about flyweight, however. The example in the book, sharing character objects in a text editor, seemed a bit far fetched at the time. This example is not unlike the cells in a Game of Life grid, however, so I happily went along with my pair’s idea to explore the pattern’s applicability in this context.

After the code retreat was over, I gave this pattern some more thought. (This is usually where the code retreat really starts paying off.)

We actually use a potential flyweight all the time: booleans. A boolean is a class with only two instances, and those instances could easily be shared. In Java they are not: new Boolean(true) != new Boolean(true). However, the Boolean class does provide two constants, TRUE and FALSE, for the instances that you could use for sharing.

That got me thinking about using Enums for flyweights. Most of the time, I use enums for grouping related but mutually exclusive constants, like days of the week. However, Enums in Java can define methods:

  public enum Cell {

    ALIVE(true), DEAD(false);

    private final boolean alive;

    private Cell(boolean alive) {
      this.alive = alive;
    }

    public boolean isAlive() {
      return alive;
    }

    public Cell evolve(int numLiveNeighbors) {
      boolean aliveInNextGeneration = alive
          ? 2 <= numLiveNeighbors && numLiveNeighbors <= 3
          : numLiveNeighbors == 3;
      return aliveInNextGeneration ? ALIVE : DEAD;
    }

  }

One of the fun parts of code retreats is that in some sessions, you will have constraints on the way you work. Such constraints force you to be more creative and think beyond the techniques you would normally use.

One constraint that is interesting in this context is to not use any conditionals, like if or switch statements or ternary operators. The idea behind this constraint is to force you to replace conditionals with polymorphism, making your program more object oriented.

The only way that I see to keep the current Cell enum and not use conditionals, is to introduce a map:

  public enum Cell {

    ALIVE(true), DEAD(false);

    private final boolean alive;
    private static final Map<Boolean, Map<Integer, Cell>> 
        NEXT = new HashMap<>();

    static {
      Map<Integer, Cell> dead = new HashMap<>();
      dead.put(0, DEAD);
      dead.put(1, DEAD);
      dead.put(2, DEAD);
      dead.put(3, ALIVE);
      dead.put(4, DEAD);
      dead.put(5, DEAD);
      dead.put(6, DEAD);
      dead.put(7, DEAD);
      dead.put(8, DEAD);
      dead.put(9, DEAD);
      NEXT.put(false, dead);
      Map<Integer, Cell> alive = new HashMap<>();
      alive.put(0, DEAD);
      alive.put(1, DEAD);
      alive.put(2, ALIVE);
      alive.put(3, ALIVE);
      alive.put(4, DEAD);
      alive.put(5, DEAD);
      alive.put(6, DEAD);
      alive.put(7, DEAD);
      alive.put(8, DEAD);
      alive.put(9, DEAD);
      NEXT.put(true, alive);
    }

    private Cell(boolean alive) {
      this.alive = alive;
    }

    public boolean isAlive() {
      return alive;
    }

    public Cell evolve(int numLiveNeighbors) {
      return NEXT.get(alive).get(numLiveNeighbors);
    }

  }

This approach works, but is not very elegant and it breaks down when the number of possibilities grows. Clearly, we need a better alternative.

The only way we can get rid of the conditional, is by getting rid of the boolean state of the cell. That means we need to have different classes for the two instances, so that the type implicitly embodies the state. That in turn means we need a factory to hide those classes from the client:

  public interface Cell {

    boolean isAlive();
    Cell evolve(int numLiveNeighbors);

  }

  public class CellFactory {

    private static final Map<Boolean, Cell> CELLS 
        = new HashMap<>();

    static {
      CELLS.put(false, new DeadCell());
      CELLS.put(true, new AliveCell());
    }

    public static Cell dead() {
      return cell(false);
    }

    public static Cell alive() {
      return cell(true);
    }

    static Cell cell(boolean alive) {
      return CELLS.get(alive);
    }

  }

  class DeadCell implements Cell {

    @Override
    public boolean isAlive() {
      return false;
    }

    @Override
    public Cell evolve(int numLiveNeighbors) {
      return CellFactory.cell(numLiveNeighbors == 3);
    }

  }

  class AliveCell implements Cell {

    @Override
    public boolean isAlive() {
      return true;
    }

    @Override
    public Cell evolve(int numLiveNeighbors) {
      return CellFactory.cell(numLiveNeighbors == 2 
          || numLiveNeighbors == 3);
    }

  }

Indeed, when you look up the Flyweight pattern, you’ll see that the proposed structure contains a flyweight factory that creates instances of concrete flyweight classes that implement a common flyweight interface.

Thanks to the code retreat and my partner, I now know why.


The Decorator Pattern

2014-01-06

decoratingOne design pattern that I don’t see being used very often is Decorator.

I’m not sure why this pattern isn’t more popular, as it’s quite handy.

The Decorator pattern allows one to add functionality to an object in a controlled manner. This works at runtime, even with statically typed languages!

The decorator pattern is an alternative to subclassing. Subclassing adds behavior at compile time, and the change affects all instances of the original class; decorating can provide new behavior at run-time for individual objects.

The Decorator pattern is a good tool for adhering to the open/closed principle.

Some examples may show the value of this pattern.

Example 1: HTTP Authentication

Imagine an HTTP client, for example one that talks to a RESTful service.

Some parts of the service are publicly accessible, but some require the user to log in. The RESTful service responds with a 401 Unauthorized status code when the client tries to access a protected resource.

Changing the client to handle the 401 leads to duplication, since every call could potentially require authentication. So we should extract the authentication code into one place. Where would that place be, though?

Here’s where the Decorator pattern comes in:

public class AuthenticatingHttpClient
    implements HttpClient {

  private final HttpClient wrapped;

  public AuthenticatingHttpClient(HttpClient wrapped) {
    this.wrapped = wrapped;
  }

  @Override
  public Response execute(Request request) {
    Response response = wrapped.execute(request);
    if (response.getStatusCode() == 401) {
      authenticate();
      response = wrapped.execute(request);
    }
    return response;
  }

  protected void authenticate() {
    // ...
  }

}

A REST client now never has to worry about authentication, since the AuthenticatingHttpClient handles that.

Example 2: Caching Authorization Decisions

OK, so the user has logged in, and the REST server knows her identity. It may decide to allow access to a certain resource to one person, but not to another.

IOW, it may implement authorization, perhaps using XACML. In that case, a Policy Decision Point (PDP) is responsible for deciding on access requests.

Checking permissions it often expensive, especially when the permissions become more fine-grained and the access policies more complex. Since access policies usually don’t change very often, this is a perfect candidate for caching.

This is another instance where the Decorator pattern may come in handy:

public class CachingPdp implements Pdp {

  private final Pdp wrapped;

  public CachingPdp(Pdp wrapped) {
    this.wrapped = wrapped;
  }

  @Override
  public ResponseContext decide(
      RequestContext request) {
    ResponseContext response = getCached(request);
    if (response == null) {
      response = wrapped.decide(request);
      cache(request, response);
    }
    return response;
  }

  protected ResponseContext getCached(
      RequestContext request) {
    // ...
  }

  protected void cache(RequestContext request, 
      ResponseContext response) {
    // ...
  }

}

As you can see, the code is very similar to the first example, which is why we call this a pattern.

As you may have guessed from these two examples, the Decorator pattern is really useful for implementing cross-cutting concerns, like the security features of authentication, authorization, and auditing, but that’s certainly not the only place where it shines.

If you look carefully, I’m sure you’ll be able to spot many more opportunities for putting this pattern to work.


Software Development and Lifelong Learning

2012-09-10

The main constraint in software development is learning. This means that learning is a core skill for developers and we should not think we’re done learning after graduation. This post explores some different ways in which to learn.

Go To Conferences

Conferences are a great place to learn new things, but also to meet new people. New people can provide new ways of looking at things, which helps with learning as well.

You can either go to big and broad conferences, like Java One or the RSA conference, or you can attend a smaller, more focused event. Some of these smaller events may not be as well-known, but there are some real gems nonetheless.

Take XML Amsterdam, for example, a small conference here in the Netherlands with excellent international speakers and attendees (even some famous ones).

Attend Workshops

Learning is as much about doing as it is about hearing and watching. Some conferences may have hands-on sessions or labs, but they’re in the minority. So just going to conferences isn’t good enough.

A more practical variant are workshops. They are mostly organized by specific communities, like Java User Groups.

One particularly useful form for developers is the code retreat. Workshops are much more focused than conferences and still provide some of the same networking opportunities.

Get Formal Training

Lots of courses are being offered, many of them conveniently online. One great (and free) example is Cryptography from Coursera.

Some of these course lead to certifications. The world is sharply divided into those who think certifications are a must and those that feel they are evil. I’ll keep my opinion on this subject to myself for once ;) but whatever you do, focus on the learning, not on the piece of paper.

Learn On The Job

There is a lot to be learned during regular work activities as well.

You can organize that a bit better by doing something like job rotation. Good forms of job rotation for developers are collective code ownership and swarming.

Pair programming is an excellent way to learn all kinds of things, from IDE shortcuts to design patterns.

Practice in Private

Work has many distractions, though, like Getting a Story Done.

Open source is an alternative, in the sense that it takes things like deadlines away, which can help with learning.

However, that still doesn’t provide the systematic exploration that is required for the best learning. So practicing on “toy problems” is much more effective.

There are many katas that do just that, like the Roman Numerals Kata. They usually target a specific skill, like Test-Driven Development (TDD).


The Art of the Visitor Pattern (3)

2008-12-13

In the previous posts in this series I looked at the basic Visitor design pattern and a couple of variations, like eliminating recursion to make the visitor interruptable. As promised, this time I will make our visitor re-entrant.

Here’s what we have so far:

public class BaseFileSystemVisitor 
    implements FileSystemVisitor {

  public FileSystemNode visit(final FileSystemNode node) {
    int numNodesVisited = 0;
    FileSystemNode current = node;

   while (current != null && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
      }
      while (!stack.isEmpty() 
          && !stack.peek().isAncestorOf(current)) {
        stack.pop().stop(this);
      }
    }
    return current;
  }

First of all, note there is a bug in here: when start returns false, the stop method is called anyway! This is because the folder is still on the stack and therefore will be stopped when it is popped from the stack. Given this analysis, the fix is easy:

  public FileSystemNode visit(final FileSystemNode node) {
    int numNodesVisited = 0;
    FileSystemNode current = node;

    while (current != null && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
        stack.pop();
      }
      while (!stack.isEmpty()
          && !stack.peek().isAncestorOf(current)) {
        stack.pop().stop(this);
      }
    }
    return current;
  }

Now suppose we want to change the order in which children are displayed, e.g. first show all sub-folders, than the others. We could write that as follows:

public class XmlVisitor 
    extends BaseFileSystemVisitor {

  public boolean start(Folder folder) {
    out.println("<folder name='" + folder.getName() 
        + "'>");
  
    Iterator children = folder.getChildren();
    while (children.hasNext()) {
      FileSystemNode child = children.next();
      if (child instanceof Folder) {
        while ((child = visit(child)) != null) { 
          // Repeat, since the visitor may be interrupted
        }
      }
    }
    
    children = folder.getChildren();
    while (children.hasNext()) {
      FileSystemNode child = children.next();
      if (!(child instanceof Folder)) {
        while ((child = visit(child)) != null) { 
          // Repeat, since the visitor may be interrupted
        }
      }
    }

    stop(folder);
    return false;
  }

But this produces the wrong output, and an EmptyStackException on top of that. The problem is that the visit method just iterates until there are no more nodes left. This is wrong for the re-entrant case: it should iterate until the nodes are no longer descendants of the root that is passed into the method:

  public FileSystemNode visit(final FileSystemNode node) {
    final FileSystemNode root;
    FileSystemNode current;
    if (stack.isEmpty() || stack.peek() != node) {
      current = node;
      root = node;
    } else {
      current = (FileSystemNode) stack.pop();
      root = (FileSystemNode) stack.firstElement();
    }
    while (root.isAncestorOf(current) 
        && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
        stack.pop();
      }
      if (root.isAncestorOf(current)) {
        while (!stack.isEmpty() 
            && !stack.peek().isAncestorOf(current)) {
          stack.pop().stop(this);
        }
      }
    }
    
    if (numNodesVisited >= maxNodes) {
      numNodesVisited = 0;
    }

    final FileSystemNode result;
    if (root.isAncestorOf(current)) {
      stack.push(current);
      result = current;
    } else {
      result = null;
    }    
    return result;
  }

Note that numNodesVisited is now a field instead of local variable.

The code has become much more complex, but we are now successfully supporting both interruptability and re-entrancy. Next time, we will make things even more complex by adding support for modification of the data structure being visited.


The Art of the Visitor Pattern (2)

2008-10-25

In my previous post, we took a look at the basic Visitor design pattern, and a couple of simple variations. I now continue with a more ambitious variation.

This is where the fun begins.

Variation 3: Eliminate recursion

Suppose we have a huge file system tree and that processing it takes a lot of time. We’d then want to update some progress indicator, or give other threads some time to run. To do that, we’d need to interrupt the processing. The current implementation doesn’t support that, because of the recursion.

Most developers probably know that every recursive implementation can be replaced by an iterative one. In a recursive function, there is an iteration of the same method call on the program’s stack. We can replace that using an explicit stack.

But in order to be able to use iteration, we need to be able to move from one node in the file system tree to the next:

public interface FileSystemNode {

  FileSystemNode getFirstChild();
  FileSystemNode getLastChild();
  
  FileSystemNode getNextSibling();
  FileSystemNode getPrevSibling();
  
  FileSystemNode getNext();
  FileSystemNode getPrev();
  
  FileSystemNode getNextNoChild();

  boolean isAncestorOf(FileSystemNode next);

  // other methods as before ...
}

We can implement that as follows:

public abstract class AbstractFileSystemNode
    implements FileSystemNode {

  public FileSystemNode getFirstChild() {
    return children.isEmpty() ? null : children.get(0);
  }

  public FileSystemNode getLastChild() {
    return children.isEmpty() ? null
        : children.get(children.size() - 1);
  }

  public FileSystemNode getNextSibling() {
    FileSystemNode result = null;
    if (parent != null) {
      final int index = 
          parent.children.indexOf(this) + 1;
      if (index < parent.children.size()) {
        result = parent.children.get(index);
      }
    }
    return result;
  }
  
  public FileSystemNode getPrevSibling() {
    FileSystemNode result = null;
    if (parent != null) {
      final int index = 
          parent.children.indexOf(this) - 1;
      if (index >= 0) {
        result = parent.children.get(index);
      }
    }
    return result;
  }
  
  public FileSystemNode getNext() {
    FileSystemNode result = getFirstChild();
    if (result == null) {
      result = getNextNoChild();
    }
    return result;
  }

  public FileSystemNode getNextNoChild() {
    FileSystemNode result = getNextSibling();
    if (result == null) {
      FileSystemNode next = this;
      while (next.getParent() != null) {
        next = next.getParent();
        if (next.getNextSibling() != null) {
          result = next.getNextSibling();
          break;
        }
      }
    }
    return result;
  }

  public FileSystemNode getPrev() {
    FileSystemNode result = null;
    FileSystemNode prev = getPrevSibling();
    if (prev == null) {
      result = getParent();
    } else {
      while (prev.getLastChild() != null) {
        prev = prev.getLastChild();
      }
      result = prev;
    }
    return result;
  }

  public boolean isAncestorOf(
      final FileSystemNode descendant) {
    boolean result = false;
    FileSystemNode current = descendant;
    while (!result && current != null) {
      result = equals(current);
      current = current.getParent();
    }
    return result;
  }

  // other methods as before...
}

With these navigational methods in place, we can eliminate the recursion:

public class BaseFileSystemVisitor
    implements FileSystemVisitor {

  public void visit(final FileSystemNode node) {
    Stack stack = new Stack();
    FileSystemNode current = node;

    while (current != null) {
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
      }
      while (!stack.isEmpty() 



          && !stack.peek().isAncestorOf(current)) {
        stack.pop().stop(this);
      }
    }
  }

  // other methods as before...

}

Note: I use generics here (e.g. Stack<FileSystemNode>), but am not showing it, since that messes up the formatting in the code viewer thingie.

The current solution has some advantages over the recursive one, like not suffering from stack overflows with deeply nested trees, but my goal was to be able to traverse the tree in multiple calls. To make that possible, we need to be able to specify how many nodes can be visited in a single call:

public interface FileSystemVisitor {
  FileSystemNode visit(FileSystemNode node);
  void setMaxNodes(int maxNodes);
  
  // ...
}

The visit() method returns where it stopped, so you can supply that information with your next call. In our implementation, we need to make the stack a field instead of a local variable, so that its contents is preserved between calls:

public class BaseFileSystemVisitor
    implements FileSystemVisitor {

  private int maxNodes = Integer.MAX_VALUE;
  private final Stack stack = new Stack();
  
  public void setMaxNodes(int maxNodes) {
    this.maxNodes = maxNodes;
  }

  public FileSystemNode visit(final FileSystemNode node) {
    int numNodesVisited = 0;
    FileSystemNode current = node;

    while (current != null
        && numNodesVisited < maxNodes) {
      numNodesVisited++;
      stack.push(current);
      if (current.start(this)) {
        current = current.getNext();
      } else {
        current = current.getNextNoChild();
      }
      while (!stack.isEmpty()
          && !stack.peek().isAncestorOf(current)) {
        stack.pop().stop(this);
      }
    }
    return current;
  }

  // other methods as before...
}

And voila, we’re done. Next time we will look at making this code re-entrant, so that we can start visiting a node while we’re visiting another node.


The Art of the Visitor Pattern (1)

2008-10-21

The visitor design pattern is one of those relatively simple patterns that you can use in many situations. Some situations are more complex than others, though, and may require some modifications. I will first present the basic pattern, and then some variations:

  1. process before and after a node
  2. skip parts of the data structure
  3. eliminate recursion
  4. support re-entrancy
  5. manipulate the data structure during visiting

This first post in a series will set the stage by discussing the basic pattern and the first two variations.

The basic pattern

Consider the following example. We have a data structure that represents a file system, with folders, files, links, devices, etc. This structure is basically a tree:

public interface FileSystemNode {

  FileSystemNode getParent();
  void setParent(FileSystemNode node);
	
  Iterator getChildren();
  void add(FileSystemNode child);
	
  void accept(FileSystemVisitor visitor);

  String getName();

}

Note: I’m using generics here but am not showing it, since it messes up the formatting.

Each of the implementors of this interface is listed in the FileSystemVisitor, so that it can handle them differently:

public interface FileSystemVisitor {

  void visit(Folder folder);
  void visit(File file);
  void visit(Link link);
  void visit(Device device);
  // ...

}

The gist of the visitor pattern is a double dispatch where the visitor calls the accept() method of the FileSystemNode:

public class BaseFileSystemVisitor 
    implements FileSystemVisitor {

  public void visit(final FileSystemNode node) {
    node.accept(this);
	  
    Iterator children = node.getChildren();
    while (children.hasNext()) {
      visit(children.next());
    }
  }

  public void visit(Folder folder) {
    // For descendants to implement
  }

  public void visit(File file) {
    // For descendants to implement
  }

  public void visit(Link link) {
    // For descendants to implement
  }

  public void visit(Device device) {
    // For descendants to implement
  }

  // ...
	
}

…and the FileSystemNode calls the visit() method of the visitor:

public class FolderImpl extends AbstractFileSystemNode 
    implements Folder {

  public void accept(final FileSystemVisitor visitor) {
    visitor.visit(this);
  }

  // Folder implementation...

}

An implementation for listing files (like the Unix ls command) could be implemented as follows:

public class ListVisitor extends BaseFileSystemVisitor {

  private final PrintStream out;

  public ListVisitor() {
    this(System.out);  
  }
  
  public ListVisitor(final PrintStream out) {
    super();
    this.out = out;
  }

  @Override
  public void visit(final Device device) {
    out.println(device.getType() + ": " 
        + device.getName());
  }

  @Override
  public void visit(final File file) {
    out.println(file.getName());
  }

  @Override
  public void visit(final Folder folder) {
    out.println(folder.getName() + "/");
  }

  @Override
  public void visit(final Link link) {
    out.println(link.getName() + " ->" 
        + link.getTarget());
  }
  
  // ...
  
}

Variation 1: Process before and after a node

Now suppose we don’t want a simple list like the one provided by ListVisitor, but we want to export to XML. For that to work, we need to open and close the containing folder tag, e.g.:

<folder name='remon'>
  <file name='quotes.txt'/>
  <device name='wlan' type='usb'/>
  <link name='java'
      target='/usr/lib/jvm/java-6-sun/bin/java'/>
</folder>

To implement this, we replace the visit() methods with start() and stop() methods:

public interface FileSystemVisitor {
  void start(Folder folder);
  void stop(Folder folder);

  void start(File file);
  void stop(File file);


  void start(Link link);
  void stop(Link link);

  void start(Device device);
  void stop(Device device);

  // ...
}

public class BaseFileSystemVisitor 
    implements FileSystemVisitor {

  public void visit(final FileSystemNode node) {
    node.start(this);
	  
    Iterator children = node.getChildren();
    while (children.hasNext()) {
      visit(children.next());
    }
	  
    node.stop(this);      
  }

  // ...
}

This allows us to implement the XmlVisitor:

public class XmlVisitor extends BaseFileSystemVisitor {

  private final PrintStream out;

  public XmlVisitor() {
    this(System.out);  
  }
  
  public XmlVisitor(final PrintStream out) {
    this.out = out;
  }

  @Override
  public void start(Device device) {
    out.println("<device type='" + device.getType() 
        + "' name='" + device.getName() + "'/>");
  }

  @Override
  public void start(File file) {
    out.println("<file name='" + file.getName() 
        + "'/>");
  }

  @Override
  public void start(Folder folder) {
    out.println("<folder name='" + folder.getName() 
        + "'>");
  }

  @Override
  public void stop(Folder folder) {
    out.println("</folder>");
  }
  
  @Override
  public void start(Link link) {
    out.println("<link name='" + link.getName() 
        + "' target='" + link.getTarget() + "'/>");
  }

}

Variation 2: Skip parts of the data structure

Now suppose we want to skip processing a sub-tree. For instance, we may want to skip folders and files whose names start with a dot. We can achieve this by having the start() method return a boolean that indicates whether the sub-tree starting at the node is to be processed:

public interface FileSystemVisitor {
  boolean start(Folder folder);
  void stop(Folder folder);

  boolean start(File file);
  void stop(File file);

  boolean start(Link link);
  void stop(Link link);

  boolean start(Device device);
  void stop(Device device);
  
  // ...
}

public class BaseFileSystemVisitor
    implements FileSystemVisitor {

  public void visit(final FileSystemNode node) {
    if (node.start(this)) {
      Iterator children = node.getChildren();
      while (children.hasNext()) {
        visit(children.next());
      }
  	  
      node.stop(this);
    }
  }

  public boolean start(Folder folder) {
    // For descendants to implement
    return true;
  }

  // ...
}

public class FolderImpl extends AbstractFileSystemNode 
    implements Folder {

  public FolderImpl(final String name) {
    super(name);
  }

  public boolean start(final FileSystemVisitor visitor) {
    return visitor.start(this);
  }

  public void stop(final FileSystemVisitor visitor) {
    visitor.stop(this);
  }

}

public class XmlVisitor extends BaseFileSystemVisitor {
  // ...

  @Override
  public boolean start(Folder folder) {
    boolean result = !folder.getName().startsWith(".");
    if (result) {
      out.println("<folder name='" + folder.getName() 
          + "'>");
    }
    return result;
  }

  // ...
}

Next time we’ll get serious with the visitor pattern by eliminating the recursion, so stay tuned!


Follow

Get every new post delivered to your Inbox.

Join 302 other followers