Fluent Builder / DSL в Java на примере

1

У меня есть 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 чтобы точно представлять дерево?

Теги:
api-design
dsl
tree
fluent

3 ответа

1

Я думаю, вам стоит поработать над тем, как работает XML-построитель:

  • да, используйте свободный API;
  • нет необходимости строго следовать шаблону Builder: вам не нужно заканчивать строительство с помощью "экспорта" в неизменяемый объект;
  • внутренне к вашему строителю, сохраните дерево и ваше текущее положение в нем: узел, который в настоящее время принимает детей;
  • имеют явный метод 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() могут быть опущены. В качестве примера я оставил один.

  • 1
    В этом предложении я предполагаю, что все методы возвращают экземпляр MathBuilder?
  • 0
    Извините, я имел в виду, что все методы возвращают один и тот же экземпляр MathBuilder? И как предотвратить недопустимые вызовы, такие как новый MathBuilder (). Const (5) .const (6) .get ()?
Показать ещё 8 комментариев
1

Если вы хотите попробовать небедренное решение, это может сработать:

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();
}

Он не читается почти так же хорошо, как и свободный вариант, но если вы строите по частям, с ним должно быть проще работать. Я думаю, что это также легко провести тестирование.

  • 0
    Да, это был первоначально класс. Опасности кодирования в текстовом поле. :-) Я отредактирую это.
0

Я не уверен, что шаблон-строитель будет иметь значение в этом случае. Вы строите дерево, а строители не очень подходят для строительства деревьев. Вместо этого я использовал бы вместо этого статические заводские методы:

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, и вы не хотите зависеть от структуры пользовательского интерфейса для создания математического выражения. И хуже всего, он изменчив. Создайте свою собственную структуру.

  • 0
    Прекрасно, пока у вас нет требования постепенно создавать выражение, например, путем чтения некоторого источника ввода.
  • 0
    Кстати, что именно не так с зависимостью от стандартного класса JDK, не имеющего зависимостей от классов GUI, которые просто находятся в иерархии javax.swing ? Это оскорбительное слово swing которое появляется в разделе импорта файла?
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню