У меня есть Java-приложение, где мне нужно представить простое арифметическое выражение как дерево. Операции, которые я буду поддерживать, включают операции унарного (!, Унарного отрицания и т.д.), Двоичные (+, -, *,/) и даже третичные (пользовательские функции) операции. DefaultMutableTreeNode
я выбрал DefaultMutableTreeNode
как структуру для представления моего математического дерева, поскольку эти деревья могут иметь дочерние узлы 0+.
Например, выражение: 3 + 4 * someFunc(2, 6, 9)
будет представлено в виде следующего дерева:
*
/ \
/ \
/ \
+ someFunc
/ \ /|\
/ \ / | \
3 4 2 6 9
Это связано с приоритетом оператора (добавление someFunc
умножения и метод someFunc
должны решаться до значения, прежде чем он может быть аргументом для умножения корня).
В любом случае, мне нужен способ программно-построить эти деревья легко, и хотел бы использовать Java DSL (Fluent Builder
) для достижения этого.
Для узлов:
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.
}
Тогда, возможно, у меня будет неплохой строитель /DSL:
// Uses DefaultMutableTreeNode for internal structure.
MathTreeBuilder treeBuilder = new MathTreeBuilder();
treeBuilder.multiply()
.add(3,4)
.someFunc(2,6,9);
MathTree tree = treeBuilder.build();
Однако мне нелегко видеть "лес" через "деревья" здесь. Нужен ли мне какой-либо строитель, или я могу просто сделать то же самое, если бы DSL был отдельно от MathTree
? Как бы выглядел DSL - я на ходу, или я неправильно понял использование свободно построенного шаблона? И, что наиболее важно, в каждом из разных методов DSL (add()
, multiply()
, someFunc()
и т.д.), Как я действительно могу изменить значение DefaultMutableTreeNode
чтобы точно представлять дерево?
Я думаю, вам стоит поработать над тем, как работает XML-построитель:
end()
который делает текущий узел родителем нового текущего узла. Затем вы можете создать свой 3 + 4 * someFunc(2, 6, 9)
следующим образом:
new MathBuilder()
.add()
.const(3)
.multiply()
.const(4)
.someFunc()
.const(2)
.const(6)
.const(9)
.end()
.get();
Все вызовы на end()
могут быть опущены. В качестве примера я оставил один.
Если вы хотите попробовать небедренное решение, это может сработать:
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();
}
Он не читается почти так же хорошо, как и свободный вариант, но если вы строите по частям, с ним должно быть проще работать. Я думаю, что это также легко провести тестирование.
Я не уверен, что шаблон-строитель будет иметь значение в этом случае. Вы строите дерево, а строители не очень подходят для строительства деревьев. Вместо этого я использовал бы вместо этого статические заводские методы:
Math.multiply(
Math.add(3, 4),
Math.someFunc(2, 6, 9)
);
Это будет эквивалентно:
new Multiply(new Add(3, 4), new SomeFunc(2, 6, 9));
PS: не используйте DefaultMutableTreeNode
: это класс среды Swing, и вы не хотите зависеть от структуры пользовательского интерфейса для создания математического выражения. И хуже всего, он изменчив. Создайте свою собственную структуру.
javax.swing
? Это оскорбительное слово swing
которое появляется в разделе импорта файла?