4

I want to explore the difference between recursion approach and composite design pattern. Composite design pattern reminds me of a tree structure. So if I have to write up how it would look in a class diagram, we could have this:

enter image description here

Keeping this class diagram in mind, here is what I have so far in Java; but I don't mind pseudo-code.

Let's create a leaf:

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

Let's now define composite:

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 ? 
}

In the composite what should I be checking exactly? Obviously I need to somehow know if it's an operator or integer here, but I don't know exactly how to put it together.

jaco0646
  • 15,303
  • 7
  • 59
  • 83
j2emanue
  • 60,549
  • 65
  • 286
  • 456

4 Answers4

4

Here is one example of arithmetic operations implemented using the Composite design pattern. There are any number of ways to implement arithmetic. The class design here is intended simply to highlight the pattern, not necessarily to portray the "best" solution.

The pattern starts with a Component interface, which is shared by Leafs and Composites.

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

Next, we have Leaf nodes, which each represent a singular operation.

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

And finally, a Composite node, which represents multiple operations.

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

I leave the remaining arithmetic types as an exercise for the reader. These few classes are sufficient for a demonstration.

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

The critical point of the design pattern is that all operations, whether singular or multiple, can be viewed through the same interface. In this way, a client receiving Arithmetic objects is able to compute() them without knowing whether they are leafs or composites.

jaco0646
  • 15,303
  • 7
  • 59
  • 83
  • jaco0646, could you update your answer to demonstration the use of appendChild and likewise removeChild ? it looks like boilerplate if you have the constructor already taking varargs. what do you think ? – j2emanue Sep 21 '19 at 08:13
  • It just depends on whether you want to construct the tree all in one go, and then have an immutable data structure; or you want the ability to mutate the tree after constructing it. If you don't want to change the tree, you can eliminate those methods and simplify the pattern. I would strongly recommend doing so if your use-case allows: immutable data structures are much simpler to work with. – jaco0646 Sep 21 '19 at 15:30
  • Would be nice to have an example of this in `python` – user32882 Oct 19 '19 at 12:34
  • Can this be persisted? I know Hibernate/JPA has serious problems with the Composite pattern when serializing. – Ariel Mirra Jun 03 '20 at 01:05
1

In your example, ArithmeticExpression must declare methods which takes ArithmeticExpression as operands in all kind of operations. It could look like this:

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

This idea allows to add two ArithmeticExpression where one could be CompositeOperand and other could be NumericOperand.

Below you can see simple Java implementation. I used Operand name but Expression also could be used.

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

Above code prints:

5.0

Take a look at main method where I build complex expression using different kinds of operands. Now, you can implement arithmetical parser and build expression from String.

See:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • 1
    I don't see the Composite design pattern here. Specifically, the `CompositeOperand` is not abstracting a collection, as in the pattern. – jaco0646 Sep 16 '19 at 23:41
  • @jaco0646, collection is not required here. We can build many different objects and create many different operations and at the end invoke `eval` method and all operations will be computed. Composite design pattern intents from 3rd link: `1.` "Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly." `2.` "Recursive composition" `3.` "Directories contain entries, each of which could be a directory." `4.` "1-to-many "has a" up the "is a" hierarchy". – Michał Ziober Sep 17 '19 at 06:05
1

probably you already solved your problem but if you still need it or if anyone is looking for a more elegant aproach there's a way...

So to start we gonna make 1 interface and 2 classes.

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

And now with everything ready we can use it to make some arithmetics:

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

With this pattern we can compose Expressions, and that's excactly the point of it:

Expression expression = new Expression(
        new Expression(
            new Operand(10),
            new Operand(2),
            '+'
        ), 
        new Operand(6),
        '/'
    );
System.out.println("Resultado: " + expression.getResultado());
Pix
  • 31
  • 1
  • 7
0

I think add a partition function which will traverse down the composite tree to compute value will help in this way. The composite tree will be something like this.

enter image description here

And here is the code written in python which implements it given the calculation 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

Lam Nguyen
  • 186
  • 1
  • 8
  • In the Composite pattern, leafs and composites share a common abstraction. In other words, `NumericNode` must also inherit from `ArithmeticExpression`. – jaco0646 Sep 19 '19 at 17:30
  • this seems off from what composite pattern should be f rom what i read. leaf and composite should be interchangable, share a common interface. hey what site did you use to create that uml like diagram. – j2emanue Sep 21 '19 at 07:16
  • @j2emanue I use https://www.draw.io/ to create the diagram, it's simple and flexible enough to use for multiple purposes. – Lam Nguyen Sep 23 '19 at 03:02