1. Обзор
Структурные шаблоны проектирования — это те, которые упрощают проектирование структур больших объектов путем определения взаимосвязей между ними. Они описывают общие способы составления классов и объектов, чтобы их можно было повторять как решения.
« Банда четырех » описала семь таких структурных способов или паттернов. В этом кратком руководстве мы увидим примеры того, как некоторые основные библиотеки в Java адаптировали каждую из них .
2. Адаптер
Адаптер, как следует из названия, действует как посредник для преобразования несовместимого в других отношениях интерфейса в тот, который ожидает клиент .
Это полезно в тех случаях, когда мы хотим взять существующий класс, исходный код которого нельзя изменить, и заставить его работать с другим классом.
Среда сбора JDK предлагает множество примеров шаблона адаптера:
List<String> musketeers = Arrays.asList("Athos", "Aramis", "Porthos");
Здесь Arrays#asList
помогает нам адаптировать массив
к списку
.
Платформа ввода-вывода также широко использует этот шаблон. В качестве примера рассмотрим этот фрагмент, который сопоставляет InputStream
с `` объектом Reader :
InputStreamReader input = new InputStreamReader(new FileInputStream("input.txt"));
3. Мост
Шаблон моста позволяет разделить абстракции и реализации, чтобы они могли разрабатываться независимо друг от друга, но при этом иметь способ или мост для сосуществования и взаимодействия .
Примером этого в Java может служить JDBC API . Он действует как связующее звено между базами данных, такими как Oracle, MySQL и PostgreSQL, и их конкретными реализациями.
JDBC API — это набор стандартных интерфейсов, таких как Driver
, Connection
и ResultSet,
и это лишь некоторые из них. Это позволяет различным поставщикам баз данных иметь свои отдельные реализации.
Например, чтобы создать соединение с базой данных, мы бы сказали:
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
, являются потомками Container
. Как мы видим, этот фрагмент кода обрабатывает корень дерева или Container
во второй строке так же, как и его дочерние элементы .
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
использует его для написания HttpResponse
, даже не подозревая об этом :
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.<String, Object>emptyMap());
OutputStream out = externalContext.getResponseOutputStream();
baos.writeTo(out);
// do cleanup
}
Как мы видим здесь, мы устанавливаем заголовки ответа, фактический ответ и файл cookie напрямую, используя ExternalContext
в качестве фасада. HTTPResponse
на картинке нет .
7. Наилегчайший вес
Шаблон легковеса снимает вес или объем памяти с наших объектов, перерабатывая их . Другими словами, если у нас есть неизменяемые объекты, которые могут совместно использовать состояние, в соответствии с этим шаблоном, мы можем кэшировать их, чтобы повысить производительность системы.
Приспособленца можно увидеть во всех классах Number в Java.
Методы valueOf
, используемые для создания объекта класса-оболочки любого типа данных, предназначены для кэширования значений и возврата их при необходимости.
Например, у 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);
Приведенный выше фрагмент кода создает прокси-сервер proxyFoo
для интерфейса Foo
.
9. Заключение
В этом коротком руководстве мы увидели практическое использование шаблонов структурного проектирования, реализованных в ядре Java .
Подводя итог, мы кратко определили, что означает каждый из семи шаблонов, а затем разобрались с ними один за другим с помощью фрагментов кода.