Quale è meglio di questi due diagrammi di classe per il progetto di calcolatrice e perché?

3

Sono un programmatore principiante che disegno due diagrammi di classe per il progetto di calcolatrice, voglio che li controlli ognuno di loro e dimmi qual è il migliore e inoltre, lo apprezzerei se indicassi le parti che sono sbagliate o potrebbe essere migliore.

    
posta alex 07.08.2016 - 09:04
fonte

1 risposta

5

Iniziamo dall'alto. Vogliamo un Calculator che possa prendere una stringa di input e trasformarla in una Tree Syntax Tree of expressions. Non entrerò in alcun dettaglio su lexing / parsing, ma posso concludere che il pattern Composite è adatto per questo.

Per il pattern Composite, iniziamo definendo un'interfaccia Expression che è un po 'più generica della tua;

interface Expression {
    double evaluate();
}

L'idea del pattern Composite è che qualsiasi Expression può contenere più oggetti Expression . Ciò consente al lexer / parser di costruire un AST di espressioni con una singola espressione radice. Esempio:

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

Ora per consentire l'annidamento di più espressioni, BinaryExpression deve contenere due espressioni, invece di operare su due doppi:

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

Questo approccio richiede tuttavia che tu definisca le espressioni per i numeri:

class NumberExpression implements Expression {
    private double number;

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

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

Nel parser, puoi eseguire il cast degli interi per raddoppiare per riutilizzare NumberExpression .

È quindi possibile definire facilmente nuove espressioni binarie in questo modo:

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

Tuttavia, ora puoi anche definire un numero di altre espressioni come meglio credi:

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

Ignora i complessi dettagli di implementazione del lexer / parser, la tua Calcolatrice ora può assomigliare a questa:

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

Nota : cose importanti come la precedenza degli operatori possono essere gestite dal lexer / parser.

    
risposta data 07.08.2016 - 12:57
fonte

Leggi altre domande sui tag