Перейти к основному содержимому

Структурные шаблоны в Core Java

· 5 мин. чтения

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 .

Подводя итог, мы кратко определили, что означает каждый из семи шаблонов, а затем разобрались с ними один за другим с помощью фрагментов кода.