复合设计模式 - 如何创建计算器
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
.
构建表达式
参见:
- How to evaluate a math expression given in string form?
- An algorithm to evaluate arithmetic expressions
- Composite Design Pattern
我认为添加一个 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());
我想探索递归方法和复合设计模式之间的区别。复合设计模式让我想起树结构。所以如果我必须写出它在 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
.
参见:
- How to evaluate a math expression given in string form?
- An algorithm to evaluate arithmetic expressions
- Composite Design Pattern
我认为添加一个 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());