DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Securing Your Software Supply Chain with JFrog and Azure
Register Today

Trending

  • Using Render Log Streams to Log to Papertrail
  • Scaling Site Reliability Engineering (SRE) Teams the Right Way
  • Extending Java APIs: Add Missing Features Without the Hassle
  • How To Use Pandas and Matplotlib To Perform EDA In Python

Trending

  • Using Render Log Streams to Log to Papertrail
  • Scaling Site Reliability Engineering (SRE) Teams the Right Way
  • Extending Java APIs: Add Missing Features Without the Hassle
  • How To Use Pandas and Matplotlib To Perform EDA In Python
  1. DZone
  2. Data Engineering
  3. Data
  4. Dynamic visitor builder with closures

Dynamic visitor builder with closures

Alex Miller user avatar by
Alex Miller
·
Mar. 16, 08 · News
Like (2)
Save
Tweet
Share
6.76K Views

Join the DZone community and get the full member experience.

Join For Free

In my previous post I was fumbling with how to leverage closures to improve the visitor pattern. Neal suggested that I could leverage closures in building the visitor rather than executing it.

So, here's a hack at doing that. The Visitor part is all standard. [I'm taking the easy route of encoding navigation in the data structure and yes I know there are better ways but this is easier to show.]

public interface Visitable {  void accept(Visitor visitor);  }
public interface Node extends Visitable {}

public class ConcreteNode implements Node {
public void accept(Visitor visitor) { visitor.visit(this); }
}

public class CompositeNode implements Node {
public void accept(Visitor visitor) {
visitor.visit(this);
for(Node node : nodes) {
node.accept(visitor);
}
}
}

public interface Visitor {
void visit(ConcreteNode node);
void visit(CompositeNode node);
}

We then can create a VisitorBuilder which is just a builder to create DynamicVisitor instances:

public class VisitorBuilder {
public static VisitorBuilder visitor() {
return new VisitorBuilder();
}

private DynamicVisitor visitor = new DynamicVisitor();

public VisitorBuilder handleConcreteNode({ConcreteNode=>void} block) {
visitor.addConcreteNode(block);
return this;
}

public VisitorBuilder handleCompositeNode({CompositeNode=>void} block) {
visitor.addCompositeNode(block);
return this;
}

public Visitor build() {
return this.visitor;
}
}

public class DynamicVisitor implements Visitor {
private {ConcreteNode=>void} concreteNodeBlock;
private {CompositeNode=>void} compositeNodeBlock;

public void addConcreteNode({ConcreteNode=>void} block) {
this.concreteNodeBlock = block;
}

public void addCompositeNode({CompositeNode=>void} block) {
this.compositeNodeBlock = block;
}

public void visit(ConcreteNode node) {
if(concreteNodeBlock != null) {
concreteNodeBlock.invoke(node);
}
}

public void visit(CompositeNode node) {
if(compositeNodeBlock != null) {
compositeNodeBlock.invoke(node);
}
}
}

You then can dynamically assemble a visitor from blocks by doing something like:

Visitor visitor =
VisitorBuilder.visitor()
.handleConcreteNode({ConcreteNode node=>
System.out.println("Visiting ConcreteNode");
})
.handleCompositeNode({CompositeNode node=>
System.out.println("Visiting CompositeNode");
})
.build();

Node root = ...
root.acceptVisitor(visitor);

You'll note that this kind of sucks in the naming of the handle and add methods. Due to type erasure, all of the blocks passed to these methods have the same type signature so I can't overload the handle() and add() methods. I also don't know of any reflective interface on the block that would let me use reflection to discover the parameter type of the closure (also missing due to erasure I would think).

One alternative would be to have a single handle() method on the builder that took a class as an extra parameter and then encoded a big gross if/else for all possible types within the handle method. In the fluent style you could then have something like:

Visitor visitor =
VisitorBuilder.visitor()
.on(ConcreteNode.class).do({ConcreteNode node=>
System.out.println("Visiting ConcreteNode");
})
.on(CompositeNode.class).do({CompositeNode node=>
System.out.println("Visiting CompositeNode");
})
.build();

There are probably some interesting extensions to this that are model-specific. If you had many nodes that shared some common attribute, you could have methods on the builder that could apply the closure to a set of node types in one call. That sort of thing could let you build some kinds of visitors in a lot less code.

Another nice consequence is that since the closures can reach the local state during build time, it is easy to have the closures update local variables, such as by adding to a collection. I believe there are also some nice improvements for the cases of return values and exception handling. I'll play with those next.

Data structure Blocks Leverage (statistics) Build (game engine) Hack (falconry) Data (computing) POST (HTTP) Attribute (computing) Sort (Unix)

Opinions expressed by DZone contributors are their own.

Trending

  • Using Render Log Streams to Log to Papertrail
  • Scaling Site Reliability Engineering (SRE) Teams the Right Way
  • Extending Java APIs: Add Missing Features Without the Hassle
  • How To Use Pandas and Matplotlib To Perform EDA In Python

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: