1. Обзор
Структурные шаблоны проектирования – это те, которые упрощают проектирование структур больших объектов путем выявления связей между ними. Они описывают общие способы составления классов и объектов, чтобы они стали повторяемыми в качестве решений.
Банда четырех описала семь таких структурных способов или моделей. В этом кратком руководстве мы увидим примеры того, как некоторые основные библиотеки в Java приняли каждую из них .
2. Адаптер
Адаптер, как следует из названия, выступает в качестве посредника для преобразования несовместимого в противном случае интерфейса в тот, который ожидает клиент .
Это полезно в тех случаях, когда мы хотим взять существующий класс, исходный код которого не может быть изменен, и заставить его работать с другим классом.
Jdk collection framework предлагает множество примеров шаблона адаптера:
Listmusketeers = Arrays.asList("Athos", "Aramis", "Porthos");
Здесь Массивы#asList помогают нам адаптировать Массив к Списку .
Структура ввода-вывода также широко использует этот шаблон. В качестве примера рассмотрим этот фрагмент кода, который сопоставляет входной поток с объектом чтения:
InputStreamReader input = new InputStreamReader(new FileInputStream("input.txt"));
3. Мост
Шаблон моста позволяет разделить абстракции и реализации таким образом, чтобы они могли развиваться независимо друг от друга, но при этом иметь возможность сосуществовать и взаимодействовать|/.
Примером этого в Java может служить API JDBC . Он действует как связующее звено между базами данных, такими как Oracle, MySQL и PostgreSQL, и их конкретными реализациями.
API JDBC – это набор стандартных интерфейсов, таких как Драйвер , Соединение и Набор результатов, и многие другие. Это позволяет различным поставщикам баз данных иметь свои отдельные реализации.
Например, чтобы создать соединение с базой данных, мы бы сказали:
Connection connection = DriverManager.getConnection(url);
Здесь url – это строка, которая может представлять любого поставщика базы данных.
Например, для PostgreSQL мы могли бы иметь:
String url = "jdbc:postgresql://localhost/demo";
И для MySQL:
String url = "jdbc:mysql://localhost/demo";
4. Композитный
Этот шаблон имеет дело с древовидной структурой объектов. В этом дереве отдельный объект или даже вся иерархия обрабатываются одинаково. Проще говоря, этот шаблон упорядочивает объекты в иерархическом порядке, чтобы клиент мог легко работать с любой частью целого .
Вложенные контейнеры в AWT/Swing являются отличными примерами использования составного шаблона в ядре Java. Объект java.awt.Container в основном является корневым компонентом, который может содержать другие компоненты, образуя древовидную структуру вложенных компонентов.
Рассмотрим этот фрагмент кода:
JTabbedPane pane = new JTabbedPane(); pane.addTab("1", new Container()); pane.addTab("2", new JButton()); pane.addTab("3", new JCheckBox());
Все классы, используемые здесь, а именно JTabbedPane , JButton , JCheckBox и JFrame , являются потомками Контейнера . Как мы видим, этот фрагмент кода обрабатывает корень дерева или Контейнер во второй строке так же, как он обрабатывает свои дочерние элементы .
5. Декоратор
Этот паттерн вступает в игру , когда мы хотим улучшить поведение объекта, не изменяя сам исходный объект . Это достигается путем добавления оболочки того же типа к объекту, чтобы возложить на него дополнительную ответственность.
Одно из самых распространенных применений этого паттерна можно найти в java.io упаковка:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("test.txt"))); while (bis.available() > 0) { char c = (char) bis.read(); System.out.println("Char: " + c); }
Здесь BufferedInputStream украшает FileInputStream , чтобы добавить возможность буферизации входных данных . Примечательно, что оба этих класса имеют InputStream в качестве общего предка. Это означает, что и объект, который украшает, и объект, который украшается, относятся к одному и тому же типу. Это безошибочный показатель рисунка декоратора.
6. Фасад
По определению, слово фасад означает искусственный или ложный внешний вид объекта. Применительно к программированию это аналогично означает предоставление другого лица – или, скорее, интерфейса – сложному набору объектов .
Этот шаблон вступает в игру, когда мы хотим упростить или скрыть сложность подсистемы или фреймворка.
API Faces Внешний контекст является отличным примером шаблона фасада. Он использует такие классы, как HttpServletRequest , HttpServletResponse и HttpSession внутренне. По сути, это класс, который позволяет API Faces быть в блаженном неведении о своей базовой среде приложения.
Давайте посмотрим , как Primefaces использует его для написания Http-ответа , фактически не зная об этом :
protected void writePDFToResponse(ExternalContext externalContext, ByteArrayOutputStream baos, String fileName) throws IOException, DocumentException { externalContext.setResponseContentType("application/pdf"); externalContext.setResponseHeader("Expires", "0"); // set more relevant headers externalContext.setResponseContentLength(baos.size()); externalContext.addResponseCookie( Constants.DOWNLOAD_COOKIE, "true", Collections.emptyMap()); OutputStream out = externalContext.getResponseOutputStream(); baos.writeTo(out); // do cleanup }
Как мы можем видеть здесь, мы устанавливаем заголовки ответов, фактический ответ и файл cookie непосредственно, используя ExternalContext в качестве фасада. HTTP-ответ отсутствует на картинке .
7. Летный вес
Шаблон flyweight снимает вес или объем памяти с наших объектов, перерабатывая их . Другими словами, если у нас есть неизменяемые объекты, которые могут совместно использовать состояние в соответствии с этим шаблоном, мы можем кэшировать их для повышения производительности системы.
Flyweight можно обнаружить во всех классах чисел в Java.
Методы value , используемые для создания объекта класса-оболочки любого типа данных, предназначены для кэширования значений и возврата их при необходимости.
Например, Integer имеет статический класс IntegerCache, который помогает его методу valueOf всегда кэшировать значения в диапазоне от -128 до 127:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) { return IntegerCache.cache[i + (-IntegerCache.low)]; } return new Integer(i); }
8. Прокси
Этот шаблон предлагает прокси-сервер или замену другому сложному объекту . Хотя это звучит похоже на фасад, на самом деле это отличается в том смысле, что фасад предлагает клиенту другой интерфейс для взаимодействия. В случае прокси-сервера интерфейс совпадает с интерфейсом объекта, который он скрывает.
Используя этот шаблон, становится легко выполнить любую операцию с исходным объектом до или после его создания.
JDK предоставляет класс java.lang.reflect.Proxy из коробки для реализаций прокси:
Foo proxyFoo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class>[] { Foo.class }, handler);
Приведенный выше фрагмент кода создает прокси-сервер proxy Foo для интерфейса Foo .
9. Заключение
В этом коротком учебнике мы увидели практическое использование шаблонов структурного проектирования, реализованных в ядре Java .
Подводя итог, мы кратко определили, что означает каждый из семи шаблонов, а затем поняли их один за другим с помощью фрагментов кода.