Composite Design Pattern

    Read the description of other patterns.

    Problem


    Provide the customer with consistent access to the leaf and composite elements of the tree structure.

    Description


    There are a large number of software systems in which the tree structures of objects are applied in one way or another. In most cases, these are all kinds of designers / editors that allow you to assemble something big (composite) from something smaller (sheet) . At the same time, the client interprets both large and small as the same thing, and the system must distinguish between composite and sheet objects, respectively.

    Not only designer programs use this pattern (but I hope that they use it). A striking example of such a tree structure is the user interface (GUI). Indeed, a typical user interface window is a container for simpler widgets - panels, buttons, water fields, etc., moreover, the panel, in turn, are also container objects and so on up to elementary sheet objects. A striking example of the use of this pattern in this context is the Swing interface rendering library (I mean the javax.swing.JComponent class).

    So, back to the problem posed - to provide the client with a uniform interface to the leaf and composite elements of the tree structure. Obviously, to solve this problem, it is necessary to establish a common interface that will describe both elementary and composite objects. Moreover, since the interface also describes composite objects; it must contain container methods - add, remove, get to add, remove, and get the object from the container. Moreover, these methods should be parameterized by the same common interface. Thus, it automatically becomes possible to add not only elementary objects to the container, but also other containers.

    All objects of the tree structure (leaf and compound) must implement this uniform interface, and compound objects redefine the add, remove, get operations, and leaf objects simply ignore them.

    Practical task


    Let's write the simplest adder of expressions using the “Linker” pattern. The adder should not deal with parsing expressions, it should only describe and implement a tree structure for more convenient calculation of expressions.

    I deliberately did not take classical examples with constructors or a user interface, so that the reader did not have any impressions about the narrow direction of the pattern.

    Class diagram


    First, a few comments. In this example, I interpret the concept of a container in a slightly different way. Let's just say that I projected the classic concept of a container onto a domain - the calculation of expressions. My “container” behaves somewhat differently than the classic one. Instead of the remove () method, SubExpression has a sub () method, which actually does the removal from the container, but only in its own way. Due to the fact that this is still an adder, the sub () method, like add (), adds a subexpression to the container, but with the opposite sign, thereby implementing subtraction.



    Consider the chart. The SubExpression interface describes a uniform interface for all objects of the tree structure, which, by the way, are not many - integers (IntegetValue), real numbers (FloatValue) and expressions (Expression). Obviously, they do not implement all numbers - sheet objects and container methods, and the expression is exactly the opposite - a container.

    Java implementation


    There is no FloatValue class code in the implementation.
    // Единообразный интерфейс доступа к листовым и контейнерным объектам
    public interface SubExpression {
      
      public Number value();
      
      public void add(SubExpression expr);
      public void sub(SubExpression expr);
      public SubExpression getSubExpression(int index);
    }

    // Лист - целое число
    public class IntegerValue implements SubExpression {
      
      private Integer value;
      
      public IntegerValue(Integer value) {
        this.value = value;
      }

      @Override
      public void add(SubExpression expr) {
        throw new UnsupportedOperationException();    
      }

      @Override
      public SubExpression getSubExpression(int index) {
        throw new UnsupportedOperationException();
      }

      @Override
      public void sub(SubExpression expr) {
        throw new UnsupportedOperationException();    
      }

      @Override
      public Number value() {
        return value;
      }
    }

    import java.util.ArrayList;
    import java.util.List;

    // Выражение - контейнер
    public class Expression implements SubExpression {
      
      private List exprs;
      
      public Expression(SubExpression ... exprs) {
        this.exprs = new ArrayList();
        for (SubExpression expr: exprs) {
          this.exprs.add(expr);
        }
      }
      
      @Override
      public void add(SubExpression expr) {
        exprs.add(expr);    
      }
      
      @Override
      public void sub(SubExpression expr) {
        if (expr instanceof IntegerValue) {
          exprs.add(new IntegerValue(-1*expr.value().intValue()));
        } else {
          exprs.add(new FloatValue(-1*expr.value().floatValue()));
        }
        
      }

      @Override
      public SubExpression getSubExpression(int index) {
        return exprs.get(index);
      }

      @Override
      public Number value() {
        Number result = new Float(0);
        
        for (SubExpression expr: exprs) {
          result = result.floatValue() + expr.value().floatValue();
        }
        
        return result;
      }
    }

    // Использование
    public class Main {

      public static void main(String[] args) {
        // Вычислим выражение - 20 - (5-2) - (11+6)
        // Приведем к следующему виду 20 - a - b
        SubExpression expr = new Expression();

        SubExpression a = new Expression(new IntegerValue(5), new IntegerValue(-2));
        SubExpression b = new Expression(new IntegerValue(11), new IntegerValue(6));
        
        expr.add(new IntegerValue(20));
        expr.sub(a);
        expr.sub(b);
        
        System.out.println(expr.value());
      }
    }

    * This source code was highlighted with Source Code Highlighter.


    I hope I managed to convey to you the idea of ​​a pattern.


    Also popular now: