web-dev-qa-db-ja.com

電卓プロジェクトのこれらの2つのクラス図のどちらが優れていますか、そしてそれはなぜですか?

私は初心者のプログラマーです。電卓プロジェクト用に2つのクラス図を描きます。それらのそれぞれをチェックして、どちらが良いかを教えてください。また、間違っている、または可能性のある部分を指摘していただければ幸いです。より良い。

enter image description here

enter image description here

3
alex

上から始めましょう。入力文字列を受け取り、それを式の抽象構文ツリーに変換できるCalculatorが必要です。字句解析の詳細については触れませんが、複合パターンはこれに適していると結論付けることができます。

コンポジットパターンの場合、まず、あなたよりも少し一般的なExpressionインターフェイスを定義します。

interface Expression {
    double evaluate();
}

複合パターンの考え方は、どのExpressionもより多くのExpressionオブジェクトを保持できるということです。これにより、レクサー/パーサーは、単一のルート式を含む式のAST=を構築できます。例:

// Don't mind the shorthand notation for brevity
Expression expr = constructAst("4 + 2 * 5") // AddExpr(4, MultExpr(2, 5));

// Evalutes 2 * 5 and then 4 + that result
expr.evaluate(); 

複数の式をネストできるようにするには、BinaryExpressionが2つのdoubleを操作するのではなく、2つの式を保持する必要があります。

abstract class BinaryExpression implements Expression {
    private Expression left;
    private Expression right;

    public BinaryExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    public double evaluate() {
        evaluate(left.evaluate(), right.evaluate());
    }

    abstract protected double evaluate(double left, double right);
}

ただし、このアプローチでは、数値の式を定義する必要があります。

class NumberExpression implements Expression {
    private double number;

    public NumberExpression(double number) {
        this.number = number;
    }

    public double evaluate() {
        return (double) number;
    }
}

パーサーでは、整数をdoubleにキャストしてNumberExpressionを再利用できます。

その後、次のように新しいバイナリ式を簡単に定義できます。

class AdditionExpression extends BinaryExpression {
    protected double evaluate(double left, double right) {
        return left + right;
    }
}

ただし、必要に応じて、他の多くの式を定義することもできます。

class LogarithmExpression implements Expression [
    private Expression operand;

    public LogarithmExpression(Expression operand) {
        this.operand = operand;
    }

    public double evaluate() {
        return Math.log(operand.evaluate());
    }
}

class NegateExpression implements Expression {
    private Expression operand;

    public NegateExpression(Expression operand) {
        this.operand = operand;
    }

    public double evaluate() {
        return -operand.evaluate();
    }
}

レクサー/パーサーの複雑な実装の詳細をスキップすると、計算機は次のようになります。

class Calculator {
    public double calculate(string expression) {
        calculate(parseExpressionToAST(expression));
    }

    public double calculate(Expression expression) {
        return expression.evaluate();
    }
}

double twentyTwo = Calculator::calculate("2 + 4 * 5");

// This should be the same as the above calculation, since
// this is the AST of expressions that should be built by the
// lexer/parser
double twentyTwoAsWell = Calculator.calculate(new AdditionExpression(
    new NumberExpression(2),
    new MultiplicationExpression(
        new NumberExpression(4),
        new NumberExpression(5)
    )
));

:演算子の優先順位などの重要なものは、レクサー/パーサーで処理できます。

5
Thijs Riezebeek