Почитать описание других паттернов.
Предоставить клиенту единообразный доступ к листовым и составным элементам древовидной структуры.
Существует большое количество программных систем, в которых так или иначе применяются древовидные структуры объектов. В большинстве случаев, это всевозможные конструкторы/редакторы, которые позволяют собрать что-то большое (составное) из чего-то более мелкого (листового). При этом, клиент трактует и большое и мелкое как одно и тоже, а система должна различать составные и листовые объекты соответственно.
Не только программы-конструкторы используют данный паттерн (а я надеюсь, что используют). Ярким примером подобной древовидной структуры является пользовательский интерфейс (GUI). Действительно, типичное окно пользовательского интерфейса — контейнер для более простых виджетов — панелей, кнопок, полей воода и т.д., причем, панель, в свою очередь тоже являются контейнерными объектами и так вплоть до элементарных листовых объектов. Ярким примером использования данного паттерна в этом контексте является библиотека отрисовки интерфейса Swing (я имею ввиду класс javax.swing.JComponent).
Итак, вернемся к поставленной проблеме — предоставить клиенту единообразный интерфейс к листовым и составным элементам древовидной структуры. Очевидно, что для решения данной проблемы необходимо завести общий интерфейс, который будет описывать и элементарные и составные объекты. Более того, т.к. интерфейс описывает и составные объекты, он должен содержать контейнерные методы — add, remove, get для добавления, удаления и получения объекта из контейнера. Причем, данные методы должны быть параметризованы тем самым общим интерфейсом. Таким образом, автоматически появляется возможность добавлять в контейнер не только элементарные объекты но и другие контейнеры.
Все объекты древовидной структуры (листовые и составные) должны реализовывать этот единообразный интерфейс, причем составные объекты переопределяют операции add, remove, get а листовые их попросту игнорируют.
Напишем простейший сумматор выражений с применением паттерна «Компоновщик». Сумматор не должен заниматься разбором выражений, он должен лишь описывать и реализовывать древовидную структуру для более удобного вычисления выражений.
Я специально не стал брать классические примеры с конструкторами или пользовательским интерфейсом, чтобы у читателя не осталось впечатлений об узкой направленности паттерна.
Для начала несколько комментариев. В данном примере я несколько иначе трактую понятие контейнера. Скажем так, я спроецировал классическое понятие контейнера на предметную область — вычисление выражений. Мой «контейнер» ведет себя несколько иначе, чем классический. Вместо метода remove(), у SubExpression есть метод sub(), который на самом деле и делает удаление из контейнера, но только по-своему. Ввиду того, что это все-таки сумматор, метод sub() аналогично add() добавляет подвыражение в контейнер, но с противоположным знаком, тем самым реализуя вычитание.

Рассмотрим диаграмму. Интерфейс подвыражения (SubExpression) описывает единообразный интерфейс для всех объектов древовидной структуры, которых, к слову сказать, не много — целые числа (IntegetValue), вещественные числа (FloatValue) и выражения (Expression). Очевидно, что все числа — листовые объекты и контейнерных методов они не реализуют, а выражение как раз наоборот — контейнер.
В реализации нет кода класса FloatValue.
Надеюсь, у меня получилось донести до Вас идею паттерна.
Проблема
Предоставить клиенту единообразный доступ к листовым и составным элементам древовидной структуры.
Описание
Существует большое количество программных систем, в которых так или иначе применяются древовидные структуры объектов. В большинстве случаев, это всевозможные конструкторы/редакторы, которые позволяют собрать что-то большое (составное) из чего-то более мелкого (листового). При этом, клиент трактует и большое и мелкое как одно и тоже, а система должна различать составные и листовые объекты соответственно.
Не только программы-конструкторы используют данный паттерн (а я надеюсь, что используют). Ярким примером подобной древовидной структуры является пользовательский интерфейс (GUI). Действительно, типичное окно пользовательского интерфейса — контейнер для более простых виджетов — панелей, кнопок, полей воода и т.д., причем, панель, в свою очередь тоже являются контейнерными объектами и так вплоть до элементарных листовых объектов. Ярким примером использования данного паттерна в этом контексте является библиотека отрисовки интерфейса Swing (я имею ввиду класс javax.swing.JComponent).
Итак, вернемся к поставленной проблеме — предоставить клиенту единообразный интерфейс к листовым и составным элементам древовидной структуры. Очевидно, что для решения данной проблемы необходимо завести общий интерфейс, который будет описывать и элементарные и составные объекты. Более того, т.к. интерфейс описывает и составные объекты, он должен содержать контейнерные методы — add, remove, get для добавления, удаления и получения объекта из контейнера. Причем, данные методы должны быть параметризованы тем самым общим интерфейсом. Таким образом, автоматически появляется возможность добавлять в контейнер не только элементарные объекты но и другие контейнеры.
Все объекты древовидной структуры (листовые и составные) должны реализовывать этот единообразный интерфейс, причем составные объекты переопределяют операции add, remove, get а листовые их попросту игнорируют.
Практическая задача
Напишем простейший сумматор выражений с применением паттерна «Компоновщик». Сумматор не должен заниматься разбором выражений, он должен лишь описывать и реализовывать древовидную структуру для более удобного вычисления выражений.
Я специально не стал брать классические примеры с конструкторами или пользовательским интерфейсом, чтобы у читателя не осталось впечатлений об узкой направленности паттерна.
Диаграмма классов
Для начала несколько комментариев. В данном примере я несколько иначе трактую понятие контейнера. Скажем так, я спроецировал классическое понятие контейнера на предметную область — вычисление выражений. Мой «контейнер» ведет себя несколько иначе, чем классический. Вместо метода remove(), у SubExpression есть метод sub(), который на самом деле и делает удаление из контейнера, но только по-своему. Ввиду того, что это все-таки сумматор, метод sub() аналогично add() добавляет подвыражение в контейнер, но с противоположным знаком, тем самым реализуя вычитание.

Рассмотрим диаграмму. Интерфейс подвыражения (SubExpression) описывает единообразный интерфейс для всех объектов древовидной структуры, которых, к слову сказать, не много — целые числа (IntegetValue), вещественные числа (FloatValue) и выражения (Expression). Очевидно, что все числа — листовые объекты и контейнерных методов они не реализуют, а выражение как раз наоборот — контейнер.
Реализация на Java
В реализации нет кода класса FloatValue.
// Единообразный интерфейс доступа к листовым и контейнерным объектам
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<SubExpression> exprs;
public Expression(SubExpression ... exprs) {
this.exprs = new ArrayList<SubExpression>();
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.
Надеюсь, у меня получилось донести до Вас идею паттерна.