Основное содержание этой статьи:
- Знакомство с комбинационными шаблонами
- Пример
- Резюме шаблонов комбинаций
- Типичное применение комбинированного режима анализа исходного кода
- Шаблоны комбинаций в java.awt
- Шаблоны композиции в коллекциях Java
- Комбинированный шаблон в Mybatis SqlNode
Больше материалов можно найти в моем личном блоге:laijianfeng.org
Рекомендуемое чтение
Шаблоны проектирования | Простые фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны фабричных методов и типичные приложения
Шаблоны проектирования | Абстрактные фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны построителей и типичные приложения
Шаблоны проектирования | Шаблоны прототипов и типовые приложения
Шаблоны проектирования | Шаблоны внешнего вида и типичные приложения
Шаблоны проектирования | Шаблоны декораторов и типичные приложения
Шаблоны проектирования | Шаблоны адаптеров и типичные приложения
Шаблоны проектирования | Легковесные шаблоны и типичные приложения
Комбинированный режим
Древовидная структура является очень распространенной структурой как в жизни, так и в разработке.Объект-контейнер (например, папка) может хранить множество различных конечных объектов или объектов-контейнеров, и различия в атрибутах между объектами-контейнерами и конечными объектами различны.Возможно, очень большой.
Из-за функциональных различий между объектами-контейнерами и конечными объектами объекты-контейнеры и конечные объекты должны обрабатываться по-разному в коде, использующем эти объекты, хотя на самом делеБольшую часть времени мы хотим обрабатывать их последовательно, потому что разная обработка этих объектов сильно усложнит программу.
Комбинированный режим был создан для решения таких проблем,Это может сделать использование листовых объектов и объектов-контейнеров согласованным..
Составной узор: объединяет несколько объектов в древовидную структуру для представления иерархии с отношением «целое-часть». Шаблон композиции согласован в использовании одиночных объектов (т. е. объектов-листов) и составных объектов (т. е. объектов-контейнеров).
Из-за существования большого количества древовидных структур в разработке программного обеспечения шаблон композиции является часто используемым структурным шаблоном проектирования.Дизайн пакетов AWT и Swing в Java SE основан на шаблоне композиции.
Кроме того, в области синтаксического анализа XML, обработки организационного дерева, проектирования файловой системы и т. д. широко используется комбинированный режим.
Роль
Компонент (абстрактный компонент): это может быть интерфейс или абстрактный класс, он объявляет интерфейсы для конечных компонентов и объектов компонентов-контейнеров и может включать объявления и реализации поведения, общего для всех подклассов в этой роли. Методы доступа и управления его подкомпонентами определяются в абстрактном компоненте, например, добавление подкомпонентов, удаление подкомпонентов, получение подкомпонентов и так далее.
Лист: он представляет объект конечного узла в составной структуре, листовой узел не имеет дочерних узлов и реализует поведение, определенное в абстрактном компоненте. Для этих методов доступа к подкомпонентам и управления ими можно использовать исключения.
Композит (контейнерный компонент): он представляет объект узла-контейнера в составной структуре. Узел-контейнер содержит дочерние узлы. Дочерние узлы могут быть конечными узлами или узлами-контейнерами. Он предоставляет коллекцию для хранения дочерних узлов и реализует поведение, определенное в абстрактном компоненте. эти методы для доступа к подкомпонентам и управления ими, а в его бизнес-методах можно рекурсивно вызывать бизнес-методы его дочерних узлов.
комбинированный режимКлючевым моментом является определение абстрактного класса компонента, который может представлять как листья, так и контейнеры., и клиентская программа для абстрактного класса компонентов может обрабатывать его единообразно, не зная, представляет ли он лист или контейнер.В то же время между объектом-контейнером и классом абстрактного компонента устанавливается отношение совокупной ассоциации., объект-контейнер может содержать как листья, так и контейнеры, чтобы реализовать рекурсивную комбинацию и сформировать древовидную структуру.
Пример
Давайте реализуем простое дерево каталогов, которое имеет два типа папок и файлов.Во-первых, нам нужен абстрактный класс компонента, который объявляет методы, требуемые классом папки и классом файла.
public abstract class Component {
public String getName() {
throw new UnsupportedOperationException("不支持获取名称操作");
}
public void add(Component component) {
throw new UnsupportedOperationException("不支持添加操作");
}
public void remove(Component component) {
throw new UnsupportedOperationException("不支持删除操作");
}
public void print() {
throw new UnsupportedOperationException("不支持打印操作");
}
public String getContent() {
throw new UnsupportedOperationException("不支持获取内容操作");
}
}
Реализуйте класс папки Folder, наследуйте Component и определитеList<Component>
Свойство componentList типа используется для хранения файлов и подпапок в папке и реализации таких методов, как getName, add, remove, print и т. д.
public class Folder extends Component {
private String name;
private List<Component> componentList = new ArrayList<Component>();
public Folder(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void add(Component component) {
this.componentList.add(component);
}
@Override
public void remove(Component component) {
this.componentList.remove(component);
}
@Override
public void print() {
System.out.println(this.getName());
for (Component component : this.componentList) {
component.print();
}
}
}
Файловый класс File наследует родительский класс Component и реализует такие методы, как getName, print и getContent.
public class File extends Component {
private String name;
private String content;
public File(String name, String content) {
this.name = name;
this.content = content;
}
@Override
public String getName() {
return this.name;
}
@Override
public void print() {
System.out.println(this.getName());
}
@Override
public String getContent() {
return this.content;
}
}
Давайте проверим это
public class Test {
public static void main(String[] args) {
Folder DSFolder = new Folder("设计模式资料");
File note1 = new File("组合模式笔记.md", "组合模式组合多个对象形成树形结构以表示具有 \"整体—部分\" 关系的层次结构");
File note2 = new File("工厂方法模式.md", "工厂方法模式定义一个用于创建对象的接口,让子类决定将哪一个类实例化。");
DSFolder.add(note1);
DSFolder.add(note2);
Folder codeFolder = new Folder("样例代码");
File readme = new File("README.md", "# 设计模式示例代码项目");
Folder srcFolder = new Folder("src");
File code1 = new File("组合模式示例.java", "这是组合模式的示例代码");
srcFolder.add(code1);
codeFolder.add(readme);
codeFolder.add(srcFolder);
DSFolder.add(codeFolder);
DSFolder.print();
}
}
выходной результат
设计模式资料
组合模式笔记.md
工厂方法模式.md
样例代码
README.md
src
组合模式示例.java
Выход нормальный, но есть небольшая проблема,Их иерархия не видна из вывода, чтобы отразить иерархическую связь между ними, нам нужно преобразовать класс Folder, добавить атрибут уровня и изменить метод печати.
public class Folder extends Component {
private String name;
private List<Component> componentList = new ArrayList<Component>();
public Integer level;
public Folder(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void add(Component component) {
this.componentList.add(component);
}
@Override
public void remove(Component component) {
this.componentList.remove(component);
}
@Override
public void print() {
System.out.println(this.getName());
if (this.level == null) {
this.level = 1;
}
String prefix = "";
for (int i = 0; i < this.level; i++) {
prefix += "\t- ";
}
for (Component component : this.componentList) {
if (component instanceof Folder){
((Folder)component).level = this.level + 1;
}
System.out.print(prefix);
component.print();
}
this.level = null;
}
}
Вывод теперь имеет соответствующую иерархию
设计模式资料
- 组合模式笔记.md
- 工厂方法模式.md
- 样例代码
- - README.md
- - src
- - - 组合模式示例.java
Мы можем нарисовать диаграмму классов между ними
родительский класс здесьComponent
это абстрактный класс компонентов,Folder
класс — это класс компонента контейнера,File
Это конечный класс компонентов Папка и файл наследуют компонент, а папка и компонент имеют отношения агрегации.
Прозрачность и безопасность
При использовании режима композиции, в соответствии с формой определения класса абстрактного компонента, мы можем разделить режим композиции на режим прозрачной композиции и безопасный режим композиции. Существует две формы полного комбинированного режима.
Прозрачный комбинированный режим
В режиме прозрачной композиции все методы, используемые для управления объектами-членами, объявляются в роли абстрактного компонента, как в примереComponent
объявленadd
,remove
метод, преимущество этого заключается в том, что все классы компонентов имеют одинаковый интерфейс. Прозрачный режим композиции также является стандартной формой режима композиции.
Недостаток режима прозрачного комбинирования в том, что он недостаточно безопасен, так как листовой объект и объект-контейнер существенно различаются, и листовой объект не может иметь следующий уровень объектов, то есть невозможно содержать объекты-члены, поэтому он обеспечиваетadd()
,remove()
и т. д. бессмысленны, что не приведет к ошибкам во время компиляции, но может привести к ошибкам во время выполнения, если вы вызовете эти методы (если не предоставлен соответствующий код обработки ошибок)
Безопасный комбинированный режим
В режиме безопасной композиции в роли абстрактного компонента не объявляется метод управления объектами-членами, а в компоненте-контейнереComposite
объявить и реализовать эти методы в классе.
Недостаток безопасного режима композиции в том, что он недостаточно прозрачен, поскольку листовой компонент и компонент-контейнер имеют разные методы, а методы, используемые для управления объектами-членами в компоненте-контейнере, не определены в классе абстрактного компонента, поэтому клиент не может полностью программировать для реферата и должен по-разному относиться к конечным компонентам и компонентам-контейнерам.
в практическом примененииjava.awt
иswing
Комбинированный режим является безопасным комбинированным режимом.
Резюме шаблонов комбинаций
комбинированный режимглавное преимуществоследующее:
- Режим композиции может четко определять иерархические сложные объекты, представляющие всю или часть иерархии объектов, что позволяет клиенту игнорировать различия в уровнях и облегчает управление всей иерархией.
- Клиенты могут последовательно использовать составную структуру или отдельный объект внутри нее, независимо от того, имеет ли дело с одним объектом или всей составной структурой, что упрощает клиентский код.
- Очень удобно добавлять новые компоненты-контейнеры и листовые компоненты в комбинированном режиме, без какой-либо модификации существующей библиотеки классов, по принципу «открыто-закрыто».
- Комбинированный шаблон обеспечивает гибкое решение для объектно-ориентированной реализации древовидной структуры.Посредством рекурсивного комбинирования листовых объектов и объектов-контейнеров может быть сформирована сложная древовидная структура, но управление древовидной структурой очень простое.
комбинированный режимглавный недостатокследующее:
- Это усложняет дизайн, и клиенту нужно тратить больше времени на выяснение иерархических отношений между классами.
- Сложно ограничить типы виджетов в контейнере при добавлении новых виджетов.
Применимая сцена:
- В иерархии с целыми и частями желательно игнорировать различия целых и частей таким образом, чтобы клиенты могли обращаться с ними последовательно.
- Древовидную структуру необходимо обрабатывать в системе, разработанной с использованием объектно-ориентированного языка.
- В системе листовые объекты и объекты-контейнеры могут быть разделены, и их типы не фиксированы, и необходимо добавить некоторые новые типы.
Типичное применение комбинированного режима анализа исходного кода
Шаблоны комбинаций в java.awt
Существует два типа графического интерфейса Java:
-
AWT (Abstract Window Toolkit): набор инструментов для работы с абстрактными окнами — это первое поколение компонентов графического пользовательского интерфейса Java. Рисование зависит от базовой операционной системы. Базовая библиотека AWT обрабатывает элементы пользовательского интерфейса, делегируя создание и поведение этих элементов собственным инструментам графического интерфейса пользователя на каждой целевой платформе (Windows, Unix, Macintosh и т. д.).
-
Swing, независимо от низкоуровневых деталей, является легковесным компонентом. Сейчас он в основном разработан на основе Swing.
Давайте рассмотрим простой пример AWT:
Примечание. Для нормального отображения китайского языка он должен быть в IDEA.
Edit Configurations -> VM Options
установить параметры в-Dfile.encoding=GB18030
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class MyFrame extends Frame {
public MyFrame(String title) {
super(title);
}
public static void main(String[] args) {
MyFrame frame = new MyFrame("这是一个 Frame");
// 定义三个构件,添加到Frame中去
Button button = new Button("按钮 A");
Label label = new Label("这是一个 AWT Label!");
TextField textField = new TextField("这是一个 AWT TextField!");
frame.add(button, BorderLayout.EAST);
frame.add(label, BorderLayout.SOUTH);
frame.add(textField, BorderLayout.NORTH);
// 定义一个 Panel,在Panel中添加三个构件,然后再把Panel添加到Frame中去
Panel panel = new Panel();
panel.setBackground(Color.pink);
Label lable1 = new Label("用户名");
TextField textField1 = new TextField("请输入用户名:", 20);
Button button1 = new Button("确定");
panel.add(lable1);
panel.add(textField1);
panel.add(button1);
frame.add(panel, BorderLayout.CENTER);
// 设置Frame的属性
frame.setSize(500, 300);
frame.setBackground(Color.orange);
// 设置点击关闭事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
}
После запуска форма выглядит следующим образом
Мы добавили три разных виджета в контейнер Frame.Button
,Label
,TextField
, также добавивPanel
контейнер,Panel
добавлено в контейнерButton
,Label
,TextField
Три компонента, почему именно контейнерыFrame
иPanel
Как насчет добавления виджетов и контейнеров разных типов?
Давайте сначала посмотрим на диаграмму классов компонента AWT.
Компоненты GUI можно разделить на два типа в зависимости от их функций: базовые компоненты и компоненты-контейнеры.
- Базовые компоненты, также известные как компоненты, представляют собой элементы графического интерфейса, такие как кнопки и текстовые поля.
- Контейнер — это особый тип компонента, который может содержать другие компоненты, такие как окна, диалоговые окна и т. д. Все классы контейнеров
java.awt.Container
прямые или косвенные подклассы
Родительский класс контейнераContainer
Часть кода выглядит следующим образом
public class Container extends Component {
/**
* The components in this container.
* @see #add
* @see #getComponents
*/
private java.util.List<Component> component = new ArrayList<>();
public Component add(Component comp) {
addImpl(comp, null, -1);
return comp;
}
// 省略...
}
Родительский класс контейнераContainer
Внутренне определяет коллекцию для храненияComponent
объекты, а компоненты контейнераContainer
и основные компоненты, такие какButton
,Label
,TextField
подождите всеComponent
Подкласс , так что вы можете ясно видеть, что здесь применяется шаблон композиции
Component
Класс инкапсулирует общие методы и свойства компонента, такие как объект компонента, размер, положение отображения, цвет переднего плана и фона, граница, видимость и т. д. графики, поэтому многие классы компонентов также наследуютComponent
Методы-члены и переменные-члены класса, соответствующие методы-члены включают:
   getComponentAt(int x, int y)
   getFont()
   getForeground()
   getName()
   getSize()
   paint(Graphics g)
   repaint()
   update()
   setVisible(boolean b)
   setSize(Dimension d)
   setName(String name)
Шаблоны композиции в коллекциях Java
HashMap
поставкаputAll
метод, вы можете преобразовать другойMap
Объект помещается в собственное пространство для хранения.Если есть такое же значение ключа, он перезапишет значение значения, соответствующее предыдущему значению ключа.
public class Test {
public static void main(String[] args) {
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("aa", 1);
map1.put("bb", 2);
map1.put("cc", 3);
System.out.println("map1: " + map1);
Map<String, Integer> map2 = new LinkedMap();
map2.put("cc", 4);
map2.put("dd", 5);
System.out.println("map2: " + map2);
map1.putAll(map2);
System.out.println("map1.putAll(map2): " + map1);
}
}
выходной результат
map1: {aa=1, bb=2, cc=3}
map2: {cc=4, dd=5}
map1.putAll(map2): {aa=1, bb=2, cc=4, dd=5}
ПроверятьputAll
исходный код
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}
putAll
Полученный параметр является родительским классомMap
типа, такHashMap
это контейнерный класс,Map
Подкласс является конечным классом, конечно, еслиMap
Другие подклассыputAll
метод, то они являются классами-контейнерами и конечными классами.
Так же,ArrayList
серединаaddAll(Collection<? extends E> c)
Этот метод также является применением комбинированного шаблона, который здесь не обсуждается.
Комбинированный шаблон в Mybatis SqlNode
Одной из мощных функций MyBatis является динамический SQL, которыйif
, choose
, when
, otherwise
, trim
, where
, set
, foreach
Теги, которые можно комбинировать в очень гибкие операторы SQL, повышая эффективность разработчика.
Вот несколько официальных примеров:
Динамический SQL -- ЕСЛИ
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
Динамический SQL - выберите, когда, иначе
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
Динамический SQL -- где
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
Динамический SQL -- foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT * FROM POST P WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
Когда Mybatis обрабатывает динамические узлы SQL, он применяет комбинированный режим проектирования.Mybatis анализирует узлы динамического SQL и текстовые узлы, определенные в файле конфигурации сопоставления, в соответствующие реализации SqlNode и формирует древовидную структуру.
SQLNode
Диаграмма классов показана ниже
нужно знать сначалаDynamicContext
Роль класса: в основном используется для записи фрагментов операторов SQL, сгенерированных после разбора динамических операторов SQL, его можно рассматривать как контейнер для записи результатов разбора динамических операторов SQL.
Абстрактный компонентSqlNode
Интерфейс, исходный код выглядит следующим образом
public interface SqlNode {
boolean apply(DynamicContext context);
}
apply
даSQLNode
Единственный метод, определенный в интерфейсе, этот метод анализирует динамический узел SQL, записанный SQLNode, в соответствии с фактическими параметрами, переданными пользователем, и вызываетDynamicContext.appendSql()
метод добавляет проанализированный фрагмент SQL кDynamicContext.sqlBuilder
сохраняется в узле SQL, когда всеSqlNode
После разбора мы можем получитьDynamicContext
Получите динамически созданный и полный оператор SQL в
Тогда см.MixedSqlNode
исходный код класса
public class MixedSqlNode implements SqlNode {
private List<SqlNode> contents;
public MixedSqlNode(List<SqlNode> contents) {
this.contents = contents;
}
@Override
public boolean apply(DynamicContext context) {
for (SqlNode sqlNode : contents) {
sqlNode.apply(context);
}
return true;
}
}
MixedSqlNode
поддерживалList<SqlNode>
список типов для храненияSqlNode
объект,apply
метод переданfor循环
перебирает содержимое и вызывает объектapply
метод, здесь тот же, что и в нашем примереFolder
в классеprint
Метод очень похож, очевидноMixedSqlNode
играет роль компонента-контейнера
Для функциональности других подклассов SqlNode краткое описание выглядит следующим образом:
-
TextSqlNode
: значит включает${}
Узел динамического SQL-заполнителя, метод применения которого будет использоватьGenericTokenParser
Разобрать${}
заполнитель и напрямую замените его фактическим значением параметра, заданным пользователем. -
IfSqlNode
: соответствует узлу динамического SQL<If>
узел, чей метод применения сначала проходит черезExpressionEvaluator.evaluateBoolean()
Метод определяет, истинно ли его тестовое выражение, а затем решает, следует ли выполнять метод apply() своих дочерних узлов в соответствии с результатом тестового выражения. -
TrimSqlNode
: соответствующий префикс или суффикс будет добавлен или удален в соответствии с результатом синтаксического анализа дочернего узла. -
WhereSqlNode
иSetSqlNode
унаследовалиTrimSqlNode
-
ForeachSqlNode
:соответствовать<foreach>
теги, перебирает коллекцию - в динамическом SQL
<choose>
,<when>
,<otherwise>
анализируется вChooseSqlNode
,IfSqlNode
,MixedSqlNode
Подводить итоги,SqlNode
Интерфейс имеет несколько классов реализации, каждый класс реализации соответствует динамическому узлу SQL, гдеSqlNode
играть роль абстрактного компонента,MixedSqlNode
Играют роль компонента контейнера, другие обычно являются листовыми компонентами.
Ссылаться на:
Лю Вэй: Шаблоны проектирования Java Edition
MOOC Java Design Patterns Интенсивный метод отладки + анализ памяти
Основы Java AWT и управление компоновкой
HashMap.putAll() [исходный код java серии «Один пояс, один путь»]
Сюй Цзюньмин: Внутри Mybatis Technology 3.2 SqlNode&SqlSource
Документация Mybatis 3.4.7: Динамический SQL