Reputation: 1611
I have a Java app where I need to represent a simple arithmetic expression as a tree. The operations I will be supporting include unary (!, unary negation, etc.), binary (+, -, *, /) and even tertiary (custom functions) operations. As such I've selected the DefaultMutableTreeNode
as the structure to represents my math tree, because these trees can have 0+ child nodes.
For instance, the expression: 3 + 4 * someFunc(2, 6, 9)
would be represented as the following tree:
*
/ \
/ \
/ \
+ someFunc
/ \ /|\
/ \ / | \
3 4 2 6 9
This is because of operator precedence (multiplication trumps addition and the someFunc
method must resolve to a value before it can be an argument to the root multiplication).
Anyways, I need a way to programmatically-build up these trees easily, and would like to use a Java DSL (Fluent Builder
pattern) to accomplish this.
For the nodes:
public abstract class MathNode {
}
public abstract class OperatorNode extends MathNode {
}
public class Addition extends OperatorNode {
// Same for Multiplication, SomeFunc, and every other supported operator.
}
public class Number extends MathNode {
// 3, 4, 2, 6, 9, etc.
}
Then, I might be able to have a fluent builder/DSL like so:
// Uses DefaultMutableTreeNode for internal structure.
MathTreeBuilder treeBuilder = new MathTreeBuilder();
treeBuilder.multiply()
.add(3,4)
.someFunc(2,6,9);
MathTree tree = treeBuilder.build();
However I'm having a tough time seeing the "forest" through the "trees" here. Do I need a builder at all, or can I just accomplish the same by having the DSL be apart of MathTree
itself? What would the DSL look like - am I on track or have I misunderstood the use of the fluent builder pattern? And, most importantly, in each of the different DSL methods (add()
, multiply()
, someFunc()
, etc.), how do I actually go about modifying the DefaultMutableTreeNode
to accurately represent the tree?
Upvotes: 1
Views: 2091
Reputation: 3723
I'm not sure a builder pattern would be relevant in this case. You're building a tree, and builders are not very suitable for building trees. I would use instead some static factory methods instead:
Math.multiply(
Math.add(3, 4),
Math.someFunc(2, 6, 9)
);
That would be equivalent to:
new Multiply(new Add(3, 4), new SomeFunc(2, 6, 9));
PS: don't use DefaultMutableTreeNode
: it is a Swing framework class, and you don't want to depend on a UI framework for making a mathematic expression framework. And worst, it is mutable. Create your own structure.
Upvotes: 0
Reputation: 13682
If you wanted to try a non-fluent solution, this might work:
interface Expression {
public Node asTree();
}
public final class Value implements Expression {
public Value(final int value) { .. }
public Node asTree();
}
public final class Negate implements Expression {
public Negate(final Expression e) { .. }
public Node asTree();
}
public final class Multiply implements Expression {
public Multiply(final Expression e1, final Expression e2) { .. }
public Node asTree();
}
It doesn't read nearly as pretty as the fluent option, but if you're building piece-by-piece it should be easier to work with. I think it's also easy to unit test.
Upvotes: 1
Reputation: 200206
I think you should go about this the way an XML builder would work:
end()
method which makes the current node's parent the new current node.Then you can build your 3 + 4 * someFunc(2, 6, 9)
as follows:
new MathBuilder()
.add()
.const(3)
.multiply()
.const(4)
.someFunc()
.const(2)
.const(6)
.const(9)
.end()
.get();
All the trailing end()
calls may be omitted. I have left one in to serve as an example.
Upvotes: 2