От конкретного дерева выражений к общему - Java - PullRequest
0 голосов
/ 16 декабря 2018

Я хочу написать абстрактный класс «Выражение», который принимает целочисленные или логические выражения и оценивает их с помощью подклассов, таких как «Добавить», «Разделить» для целых и «И», «Или» для логических выражений.В конце концов, все сводится к написанию подклассов, которые реализуют собственный методvalu ().Я нашел реализацию в книге, но она работает только с двойными значениями.Вот оно:

abstract class Expr {
    abstract double eval();
}
abstract class BinOp extends Expr {
    Expr left;
    Expr right;
    BinOp(Expr l, Expr r) {
    }
}
class Const extends Expr {
    private double value;
    Const( double val ) {
        this.value = val;
    }
    double eval () {
        return this.value;
    }//eval
}

Теперь для класса BinOp я могу написать класс Add, который расширяет его, вызывает его конструктор и реализует eval () с умножением 2 объектов Const, которые eval () сами и просто возвращают значение, с которым они были созданы.

Как бы это работало, если бы я хотел сделать это с Expr, который не обязательно должен быть удвоен, но имеет тип int или boolean?Я перечитал дженерики, но не могу правильно спроектировать классы, такие как Expr, чтобы мой код компилировался.Вот моя попытка:

public abstract class Expression<T> {
    abstract T evaluate();
}
public class Const<T> extends Expression{
    private T n;
    public Const(T x) { n = x; }
    public Const<Integer> integerConst(Integer n) {
        return new Const<>(n);
    }
    public Const<Boolean> booleanConstConst(Boolean n) {
        return new Const<>(n);
    }
    public T evaluate() {
        return n;
    }
}

Теперь я не хочу, чтобы кто-то делал за меня домашнее задание, поэтому я просто спрашиваю, в чем заключается ошибка в моем подходе и может ли кто-нибудь указать мне правильное направление,Спасибо.

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

Я думаю, что вы должны скрыть всю логику за Expression, а вся логика конкретного типа должна быть в отдельном классе.

Чтобы скрыть конкретную реализацию, вы должны использовать параметризованный класс Expression и фабричный метод для каждой конкретной реализации:

public interface Expression<T> {

    T evaluate();

    static BooleanExpression with(boolean val) {
        return new BooleanExpression(val);
    }

    static DoubleExpression with(double val) {
        return new DoubleExpression(val);
    }

}

Реализация для "логических" значений с необходимыми операторами:

public final class BooleanExpression implements Expression<Boolean> {

    private final boolean left;

    public BooleanExpression(boolean left) {
        this.left = left;
    }

    public BooleanExpression and(boolean right) {
        return new BooleanExpression(left && right);
    }

    public BooleanExpression or(boolean right) {
        return new BooleanExpression(left || right);
    }

    @Override
    public Boolean evaluate() {
        return left;
    }
}

Реализация для integer значений с необходимыми операторами (я использую double, потому что вы должны реализовать divide):

public final class DoubleExpression implements Expression<Double> {

    private final double left;

    public DoubleExpression(double left) {
        this.left = left;
    }

    public DoubleExpression add(double right) {
        return new DoubleExpression(left + right);
    }

    public DoubleExpression divide(double right) {
        return new DoubleExpression(left / right);
    }

    @Override
    public Double evaluate() {
        return left;
    }
}

Код клиента показывает, что вся конкретная реализация скрыта и у вас есть только приемлемые операторы для каждого выражения:

boolean res1 = Expression.with(false).and(true).or(true).evaluate();   // true
double res2 = Expression.with(0.5).add(2.5).divide(2).evaluate();       // 1.5
0 голосов
/ 16 декабря 2018

Вот несколько предложений:

  • Прежде всего, вы не должны использовать необработанные типы, поэтому Const<T> должно расширяться Expression<T>.

  • Теперь ваши integerConst и booleanConstConst методы выглядят как фабричные методы, поэтому они должны быть static.

  • Тем не менее, я не уверен, что этохорошая идея иметь эти фабричные методы в классе Const, так как это заставит вас изменить тип Const, если вы хотите поддерживать третий тип выражения (в дополнение к Boolean и Integer).Вместо этого вы можете рассмотреть подклассы Const:

    public class IntegerConst extends Const<Integer> {
        public IntegerConst(Integer n) {
            super(n);
        }
    }
    
    public class BooleanConst extends Const<Boolean> {
        public BooleanConst(Boolean b) {
            super(b);
        }
    }
    

Надеюсь, вы сможете продолжить отсюда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...