复合设计模式 - 如何创建计算器

Composite Design pattern - how to create calculator

我想探索递归方法和复合设计模式之间的区别。复合设计模式让我想起树结构。所以如果我必须写出它在 class 图表中的样子,我们可以这样:

记住这张 class 图,这是我目前在 Java 中的内容;但我不介意伪代码。

让我们创建一片叶子:

class NumericOperand extends ArithmeticExpression{

    public Float add(String:s1,String:s2){
        return s1.toFloat() + s2.toFloat()
    }

    public Float minus(String:s1,String:s2){
        return s1.toFloat() - s2.toFloat()
    }

    public Float multiple(String:s1,String:s2){
        return s1.toFloat() * s2.toFloat()
    }

    public Float divide(String:s1,String:s2){
        return s1.toFloat() / s2.toFloat()
    }
}

现在让我们定义复合材料:

public CompositeOperand extends ArithmeticExpression{

    private List<NumericOperand> operandList = new ArrayList<NumericOperand>();
    //now what ???   
    //here im a little lost what i should do ? can you help me ? 
}

在合成中我应该检查什么?显然我需要以某种方式知道这里是运算符还是整数,但我不知道如何将它们组合在一起。

在您的示例中,ArithmeticExpression 必须声明在所有类型的操作中将 ArithmeticExpression 作为操作数的方法。它可能看起来像这样:

public Float add(ArithmeticExpression:s1,ArithmeticExpression:s2){
    return s1.eval() + s2.eval();
}

这个想法允许 add 两个 ArithmeticExpression,其中一个可以是 CompositeOperand,另一个可以是 NumericOperand

您可以在下面看到简单的 Java 实现。我使用了 Operand 名称,但也可以使用 Expression

import java.util.Objects;

public class ArithmeticApp {

    public static void main(String[] args) {
        // expr =  100 / (10 + (2.5 * 4))
        Operand res = CompositeOperand.divide(
                NumericOperand.fromInt(100),
                new PlusExpression(
                        NumericOperand.fromString("10"),
                        CompositeOperand.multiply(
                                NumericOperand.fromDouble(2.5D),
                                NumericOperand.fromInt(4))));
        System.out.println(res.eval());
    }
}

@FunctionalInterface
interface Operand {
    Double eval();
}

class PlusExpression implements Operand {

    private final Operand left;
    private final Operand right;

    public PlusExpression(Operand left, Operand right) {
        this.left = Objects.requireNonNull(left);
        this.right = Objects.requireNonNull(right);
    }

    @Override
    public Double eval() {
        return left.eval() + right.eval();
    }
}

class NumericOperand implements Operand {

    private final Double value;

    private NumericOperand(Double value) {
        this.value = Objects.requireNonNull(value);
    }

    @Override
    public Double eval() {
        return value;
    }

    public static NumericOperand fromString(String value) {
        return fromDouble(Double.parseDouble(value));
    }

    public static NumericOperand fromInt(int value) {
        return fromDouble((double) value);
    }

    public static NumericOperand fromDouble(Double value) {
        return new NumericOperand(value);
    }
}

class CompositeOperand implements Operand {

    private final Operand root;

    public CompositeOperand(Operand root) {
        this.root = Objects.requireNonNull(root);
    }

    @Override
    public Double eval() {
        return root.eval();
    }

    public static CompositeOperand minus(Operand left, Operand right) {
        return new CompositeOperand(() -> left.eval() - right.eval());
    }

    public static CompositeOperand multiply(Operand left, Operand right) {
        return new CompositeOperand(() -> left.eval() * right.eval());
    }

    public static CompositeOperand divide(Operand left, Operand right) {
        return new CompositeOperand(() -> left.eval() / right.eval());
    }
}

以上代码打印:

5.0

看看 main 方法,我在其中使用不同类型的操作数构建复杂表达式。现在,您可以实现算术解析器并从 String.

构建表达式

参见:

我认为添加一个 partition 函数将遍历复合树以计算值将有助于这种方式。复合树将是这样的。

这是在 python 中编写的代码,根据计算 2 + (3*5)

实现它

#!/usr/bin/env python
# coding: utf-8

# In[34]:


class ArithmeticExpression:

    result = 0

    def __init__(self, operator):
        self.operator = operator

    def compute(self, left, right, operator):

        if operator == '+':
            result = self.add(left, right)
        elif operator == '-':
            result = self.substract(left, right)
        elif operator == '*':
            result = self.multiple(left, right)
        elif operator == '/':
            result = self.multiple(left, right)

        return result 

    def add(self, left, right):
        print('cal {} + {} = {}' .format(left, right, left + right))
        return left + right

    def substract(self, left, right):
        print('cal {} - {} = {}' .format(left, right, left - right))
        return left - right

    def multiple(self, left, right):
        print('cal {} * {} = {}' .format(left, right, left * right))
        return left*right

    def divide(self, left, right):
        print('cal {} / {} = {}' .format(left, right, left / right))
        return left / right

class NumericNode:

    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return "NumericNode {}".format(self.value)

class Composite(ArithmeticExpression):

    def __init__(self, operator):
        super().__init__(operator)
        self.left = None
        self.right = None

    def __repr__(self):
        return "Composite {}".format(self.operator)

    def add_node(self, left, right):
        self.left = left
        self.right = right

    def partition(self, node):

        if isinstance(node, NumericNode):
            return node.value

        return self.compute(self.partition(node.left), self.partition(node.right), node.operator)


# In[35]:


root = Composite(operator='+')
leaf1 = NumericNode(2)
leaf2 = NumericNode(3)
leaf3 = NumericNode(5)
composite2 = Composite(operator='*')
composite2.add_node(leaf2, leaf3)
root.add_node(leaf1, composite2)


# In[36]:


root.partition(root)

# Output:
# Caculate  3 * 5 = 15
# Calculate 2 + 15 = 17

这是一个使用复合设计模式实现的算术运算示例。有许多方法可以实现算术。这里的 class 设计只是为了突出模式,不一定要描述 "best" 解决方案。

该模式以 Component 接口开始,由 Leafs 和 Composites 共享。

public interface Arithmetic {
    double compute();
    default void appendChild(Arithmetic arithmetic) {}
    default void removeChild(Arithmetic arithmetic) {}
}

接下来,我们有 Leaf 个节点,每个节点代表一个单一操作。

public class Addition implements Arithmetic {
    private final double x;
    private final double y;

    public Addition(double x, double y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public double compute() {
        return x + y;
    }
}

public class Subtraction implements Arithmetic {
    private final double x;
    private final double y;

    public Subtraction(double x, double y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public double compute() {
        return x - y;
    }
}

最后,一个 Composite 节点,代表多个操作。

public class CompositeAddition implements Arithmetic {
    private final List<Arithmetic> operations = new ArrayList<>();

    public CompositeAddition(Arithmetic... arithmetics) {
        operations.addAll(Arrays.asList(arithmetics));
    }

    @Override
    public double compute() {
        return operations.stream().mapToDouble(Arithmetic::compute).sum();
    }

    @Override
    public void appendChild(Arithmetic arithmetic) {
        operations.add(arithmetic);
    }

    @Override
    public void removeChild(Arithmetic arithmetic) {
        operations.remove(arithmetic);
    }
}

我将剩余的算术类型留作 reader 的练习。这几个 class 足以进行演示。

public class Main {
    public static void main(String... args) {
        Arithmetic fivePlusTwo = new Addition(5,2);
        Arithmetic fiveMinusTwo = new Subtraction(5,2);
        Arithmetic sevenPlusThree = new CompositeAddition(fivePlusTwo, fiveMinusTwo);
        System.out.println(sevenPlusThree.compute());
    }
}

设计模式的关键点是所有的操作,无论是单一的还是多重的,都可以通过同一个界面来查看。通过这种方式,接收 Arithmetic 个对象的客户端能够 compute() 它们而不知道它们是叶子还是复合。

可能你已经解决了你的问题,但如果你仍然需要它,或者如果有人正在寻找更优雅的方法,那么有一种方法......

所以首先我们要制作 1 个接口和 2 个 类。

Evaluable.java

public interface Evaluable {
    public int evaluate();
}

Operand.java

public class Operand implements Evaluable {

    private int value;

    public Operand(int value) {
        this.value = value;
    }

    @Override
    public int evaluate() {
        return value;
    }
}

Expression.java

public class Expression implements Evaluable {

    private Evaluable leftOperand;
    private Evaluable rightOperand;
    private final char operation;

    public Expression(Evaluable leftOperand, Evaluable rightOperand, char operation) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
        this.operation = operation;
    }


    @Override
    public int evaluate() {
        int result = 0;

        switch (operation) {
            case '+':
                result = leftOperand.evaluate() + rightOperand.evaluate();
                break;
            case '-':
                result = leftOperand.evaluate() - rightOperand.evaluate();
                break;
            case '*':
                result = leftOperand.evaluate() * rightOperand.evaluate();
                break;
            case '/':
                result = leftOperand.evaluate() / rightOperand.evaluate();
                break;
        }
        return result;
    }
}

现在一切就绪,我们可以用它来做一些算术:

App.java

public static void main( String[] args ){

Evaluable evaluable = new Operand(5);
System.out.println("Result :" + evaluable.evaluate());

Evaluable expression1 = new Expression(new Operand(3),new Operand(5),'*');
System.out.println("Result :" + expression1.evaluate());

有了这个模式,我们可以组成表达式,而这正是它的重点:

Expression expression = new Expression(
        new Expression(
            new Operand(10),
            new Operand(2),
            '+'
        ), 
        new Operand(6),
        '/'
    );
System.out.println("Resultado: " + expression.getResultado());