Эта статья является частью нашего курса Академии под названием « Шаблоны проектирования Java» .
В этом курсе вы изучите огромное количество шаблонов проектирования и увидите, как они реализуются и используются в Java. Вы поймете причины, почему шаблоны так важны, и узнаете, когда и как применять каждый из них. Проверьте это здесь !
Содержание
1. Введение
В этом уроке мы поговорим об очень интересном шаблоне проектирования, Composite Pattern. Английское значение слова Composite состоит из сложных и взаимосвязанных частей. «Композит» означает «собирать вместе», и в этом суть этой модели проектирования.
Есть моменты, когда вы чувствуете необходимость древовидной структуры данных в вашем коде. Существует много вариантов структуры данных дерева, но иногда возникает необходимость в дереве, в котором обе ветви, а также листья дерева должны рассматриваться как единообразные.
Составной шаблон позволяет вам объединять объекты в древовидную структуру для представления иерархии части-целого, что означает, что вы можете создать дерево объектов, состоящее из разных частей, но которое можно рассматривать как одно целое. Composite позволяет клиентам обрабатывать отдельные объекты и композиции объектов равномерно, что является целью Composite Pattern.
Может быть много практических примеров составного паттерна. Система файловых каталогов, представление html в java, синтаксический анализатор XML — все это хорошо управляемые композиты, и все они могут быть легко представлены с использованием Composite Pattern. Но прежде чем углубляться в детали примера, давайте посмотрим на некоторые детали, касающиеся составного паттерна.
2. Что такое составной шаблон
Формальное определение Composite Pattern говорит о том, что он позволяет объединять объекты в древовидные структуры для представления иерархий части-целого. Композитный позволяет клиентам обрабатывать отдельные объекты и композиции объектов равномерно.
Если вы знакомы с древовидной структурой данных, вы будете знать, что у дерева есть родители и их дети. Родителю может быть несколько детей, но только один родитель на ребенка. В составном шаблоне элементы с дочерними элементами называются узлами, а элементы без дочерних элементов называются листами.
Составной шаблон позволяет нам строить структуры объектов в форме деревьев, которые содержат как композицию объектов, так и отдельные объекты в виде узлов. Используя составную структуру, мы можем применять одинаковые операции как к составным, так и к отдельным объектам. Другими словами, в большинстве случаев мы можем игнорировать различия между композициями объектов и отдельными объектами.
Составной шаблон имеет четыре участника:
- Составная часть
- лист
- композитный
- клиент
На следующем рисунке показана типичная структура составного объекта. Как вы можете видеть, у одного родителя может быть много дочерних, то есть составных, но только один родитель на каждого ребенка.
Компонент на диаграмме классов ниже определяет интерфейс для всех объектов в композиции, как составных, так и конечных узлов. Компонент может реализовывать поведение по умолчанию для универсальных методов.
Роль Composite состоит в том, чтобы определить поведение компонентов, имеющих дочерние элементы, и сохранить дочерние компоненты. Composite также реализует операции, связанные с Leaf. Эти операции могут иметь или не иметь никакого смысла; это зависит от функциональности, реализующей использование шаблона.
Лист определяет поведение элементов в композиции. Это достигается путем реализации операций, которые поддерживает Компонент. Лист также наследует методы, которые не обязательно имеют большой смысл для узла листа.
Клиент управляет объектами в композиции через интерфейс Компонента.
3. Пример составного паттерна
Составной шаблон может быть реализован везде, где у вас есть иерархическая природа системы или подсистемы, и вы хотите обрабатывать отдельные объекты и составы объектов единообразно. Файловая система, XML, Html или иерархия офиса (от президента до сотрудников) могут быть реализованы с использованием составного шаблона.
Давайте посмотрим на простой пример, где мы реализуем представление HTML в Java с использованием Composite Pattern. HTML является иерархическим по своей природе, он начинается с тега <html>, который является родительским или корневым тегом, и содержит другие теги, которые могут быть родительским или дочерним тегом.
Составной шаблон в Java может быть реализован с использованием класса Component в качестве абстрактного класса или интерфейса. В этом примере мы будем использовать абстрактный класс, который содержит все важные методы, используемые в составном классе и листовом классе.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package com.javacodegeeks.patterns.compositepattern;import java.util.List;public abstract class HtmlTag { public abstract String getTagName(); public abstract void setStartTag(String tag); public abstract void setEndTag(String tag); public void setTagBody(String tagBody){ throw new UnsupportedOperationException("Current operation is not support for this object"); } public void addChildTag(HtmlTag htmlTag){ throw new UnsupportedOperationException("Current operation is not support for this object"); } public void removeChildTag(HtmlTag htmlTag){ throw new UnsupportedOperationException("Current operation is not support for this object"); } public List<HtmlTag>getChildren(){ throw new UnsupportedOperationException("Current operation is not support for this object"); } public abstract void generateHtml();} |
Класс HtmlTag является классом компонента, который определяет все методы, используемые композитом и листовым классом. Есть несколько методов, которые должны быть общими в обоих расширенных классах; следовательно, эти методы хранятся абстрактно в вышеприведенном классе, чтобы обеспечить их реализацию в дочерних классах.
getTagName() просто возвращает имя тега и должна использоваться обоими дочерними классами, т. getTagName() классом и листовым классом.
Каждый элемент html должен иметь начальный тег и конечный тег, методы setStartTag и setEndTag используются для установки начального и конечного тега HTML-элемента и должны быть реализованы обоими дочерними классами, поэтому они сохраняются абстрактными в вышеприведенном классе. ,
Существуют методы, которые полезны только для составного класса и бесполезны для конечного класса. Просто предоставьте реализацию этих методов по умолчанию, и исключение является хорошей реализацией этих методов, чтобы избежать случайного вызова этих методов объектом, который не должен их поддерживать.
Метод generatHtml() — это операция, которая должна поддерживаться обоими расширенными классами. Для простоты он просто печатает тег на консоль.
Теперь давайте посмотрим на класс Composite.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
package com.javacodegeeks.patterns.compositepattern;import java.util.ArrayList;import java.util.List;public class HtmlParentElement extends HtmlTag { private String tagName; private String startTag; private String endTag; private List<HtmlTag>childrenTag; public HtmlParentElement(String tagName){ this.tagName = tagName; this.startTag = ""; this.endTag = ""; this.childrenTag = new ArrayList<>(); } @Override public String getTagName() { return tagName; } @Override public void setStartTag(String tag) { this.startTag = tag; } @Override public void setEndTag(String tag) { this.endTag = tag; } @Override public void addChildTag(HtmlTag htmlTag){ childrenTag.add(htmlTag); } @Override public void removeChildTag(HtmlTag htmlTag){ childrenTag.remove(htmlTag); } @Override public List<HtmlTag>getChildren(){ return childrenTag; } @Override public void generateHtml() { System.out.println(startTag); for(HtmlTag tag : childrenTag){ tag.generateHtml(); } System.out.println(endTag); }} |
Класс HtmlParentElement является составным классом, который реализует методы, такие как addChildTag , removeChildTag , getChildren которые должны быть реализованы классом, чтобы стать составной структурой. Методом операции здесь является generateHtml , который печатает тег текущего класса, а также перебирает его дочерние элементы и также вызывает их метод generateHtml .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package com.javacodegeeks.patterns.compositepattern;public class HtmlElement extends HtmlTag{ private String tagName; private String startTag; private String endTag; private String tagBody; public HtmlElement(String tagName){ this.tagName = tagName; this.tagBody = ""; this.startTag = ""; this.endTag = ""; } @Override public String getTagName() { return tagName; } @Override public void setStartTag(String tag) { this.startTag = tag; } @Override public void setEndTag(String tag) { this.endTag = tag; } @Override public void setTagBody(String tagBody){ this.tagBody = tagBody; } @Override public void generateHtml() { System.out.println(startTag+""+tagBody+""+endTag); }} |
HtmlElement является листовым классом, и его основная задача заключается в реализации метода операции, который в этом примере является методом generateHtml . Он печатает startTag , необязательно tagBody, если есть, и endTag дочернего элемента.
Давайте проверим этот пример.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.javacodegeeks.patterns.compositepattern;public class TestCompositePattern { public static void main(String[] args) { HtmlTag parentTag = new HtmlParentElement("<html>"); parentTag.setStartTag("<html>"); parentTag.setEndTag("</html>"); HtmlTag p1 = new HtmlParentElement("<body>"); p1.setStartTag("<body>"); p1.setEndTag("</body>"); parentTag.addChildTag(p1); HtmlTag child1 = new HtmlElement("<p>"); child1.setStartTag("<p>"); child1.setEndTag("</p>"); child1.setTagBody("Testing html tag library"); p1.addChildTag(child1); child1 = new HtmlElement("<p>"); child1.setStartTag("<p>"); child1.setEndTag("</p>"); child1.setTagBody("Paragraph 2"); p1.addChildTag(child1); parentTag.generateHtml(); }} |
Приведенный выше код приведет к следующему выводу:
|
1
2
3
4
5
6
|
<html><body><p>Testing html tag library</p><p>Paragraph 2</p></body></html> |
В приведенном выше примере сначала мы создали родительский тег (<html>), затем добавляем к нему дочерний элемент, который является другим составного типа (<body>), и этот объект содержит два дочерних элемента (<p>).
Обратите внимание, что вышеприведенная структура представлена как иерархия части-целого, и вызов метода generateHtml() для родительского тега позволяет клиенту одинаково обрабатывать композиции объектов. Как он генерирует HTML объекта и всех его дочерних элементов.
4. Когда использовать Composite Pattern
- Когда вы хотите представить частично-целые иерархии объектов.
- Когда вы хотите, чтобы клиенты могли игнорировать разницу между композициями объектов и отдельными объектами. Клиенты будут одинаково относиться ко всем объектам в составной структуре.
5. Загрузите исходный код
Это был урок по составному шаблону. Вы можете скачать исходный код здесь: CompositePattern-Project

