В этом руководстве мы познакомимся с миром шаблонов дизайна. Для каждого шаблона мы дополнительно понимаем шаблон и контекст, в котором он применим, с примерами из реальной жизни.
В этом посте вы узнаете:
- Что такое шаблоны дизайна
- Почему мы используем шаблоны дизайна
- Различные типы шаблонов дизайна
- Когда мы используем шаблоны дизайна
- Как вы реализуете различные шаблоны проектирования в Java
- Реальные примеры шаблонов дизайна.
Что такое шаблоны дизайна?
Мы создавали объектно-ориентированное программное обеспечение уже более 40 лет, начиная с Smalltalk, который был первым объектно-ориентированным языком.
Мир программирования столкнулся с большим количеством проблем, и для их решения было предложено множество решений.
Группа из четырех человек, известная как «Банда Четырех» или GoF, предприняла попытку предложить ряд общих проблем и решений для них в данном контексте.
Этот каталог общих проблем и их решений помечен как GOF (Gang of Four) Design Patterns.
Зачем создавать шаблоны?
Преимущества шаблонов дизайна:
- Предоставить стандартную терминологию, понятную каждому
- Не повторять одни и те же ошибки снова и снова
Какие типы шаблонов дизайна?
Шаблоны проектирования, о которых мы здесь говорим, с точки зрения объектно-ориентированного мира. Есть в основном три различных типа шаблонов проектирования:
- Образцы творчества
- Структурные паттерны
- Поведенческие образцы
Творения
Образцы творчества имеют дело с созданием объектов.
структурная
Структурные узоры имеют дело с композицией объектов.
Это касается таких вопросов, как:
- Что содержится в классе?
- Каковы отношения класса с другими классами? Это наследство или состав?
поведенческий
Поведенческие модели сосредотачиваются больше на поведении объектов или, точнее, на взаимодействиях между объектами.
Как объект общается с другим объектом?
Изучение шаблонов креационного дизайна
Мы исследуем следующие шаблоны творческого дизайна:
Образец прототипа
Прототип представляет полностью инициализированный экземпляр для копирования или клонирования.
Давайте возьмем пример:
Давайте рассмотрим дизайн шахматной игры. Каждая игра в шахматы имеет одинаковую начальную настройку — король, королева, ладья, слон, рыцарь и пешки имеют свои определенные места. Допустим, мы хотим создать программное обеспечение для моделирования шахматной игры.
Каждый раз, когда в новую игру в шахматы играют, нам нужно создать начальный макет доски.
Вместо того чтобы каждый раз повторять создание шахматной доски,
- Мы можем создать объект, который содержит начальную настройку
- Клонировать от него — каждый раз, когда в игру играют в шахматы
Объект с начальной настройкой шахматной доски является прототипом. И мы используем образец прототипа.
Разве это не просто?
В шаблоне прототипа у вас есть полностью инициализированный экземпляр. Здесь начальная раскладка платы легко доступна.
Каждый раз, когда запускается новая игра в шахматы, например, на любом из многочисленных онлайн-порталов по шахматам, этот инициализированный экземпляр просто копируется или клонируется.
Образец Строителя
Шаблон Builder отделяет конструкцию объекта от его представления. Что это значит?
Предположим, вы идете на ужин из нескольких блюд в ресторан. У такого ужина было бы много вариантов, таких как закуски, основное блюдо и десерты. Вы, вероятно, выбрали бы два или три из представленных вариантов. Определенный клиент может захотеть поужинать только с первыми двумя вариантами, оставив опцию десертов Тем не менее, другой предпочел бы основное блюдо и десерты, пропуская закуски полностью.
Подобные ситуации могут возникнуть при разработке программного обеспечения. Вам может понадобиться построить объект, используя подмножество доступных опций. Или вам может понадобиться создать объект несколькими способами. Это где модель строителя пригодится.
Чтобы понять это далее, давайте посмотрим на небольшой фрагмент кода.
Джава
1
public class BuilderPattern {
2
static class Coffee {
3
private Coffee(Builder builder) {
4
this.type = builder.type;
5
this.sugar = builder.sugar;
6
this.milk = builder.milk;
7
this.size = builder.size;
8
}
9
private String type;
11
private boolean sugar;
12
private boolean milk;
13
private String size;
14
public static class Builder {
16
private String type;
17
private boolean sugar;
18
private boolean milk;
19
private String size;
20
public Builder(String type) {
22
this.type = type;
23
}
24
public Builder sugar(boolean value) {
26
this.sugar = value;
27
return this;
28
}
29
public Builder milk(boolean value) {
31
this.milk = value;
32
return this;
33
}
34
public Builder size(String value) {
36
this.size = value;
37
return this;
38
}
39
public Coffee build() {
41
return new Coffee(this);
42
}
43
}
44
46
public String toString() {
47
return String.format("Coffee [type=%s, sugar=%s, milk=%s, size=%s]", this.type, sugar, milk, size);
48
}
49
}
51
public static void main(String[] args) {
53
Coffee coffee = new BuilderPattern.Coffee.Builder("Mocha").milk(true).sugar(false).size("Large").build();
54
System.out.println(coffee);
55
}
57
}
Допустим, вы пишете программное обеспечение для машины, которая готовит кофе. Основными ингредиентами кофе являются кофе, молоко и сахар.
В зависимости от того, из какой части мира вы родом, вы выбираете, есть ли у вас сахар и молоко.
В план строителя входят эти варианты кофе для вас.
Посмотрите на код внутри main()
.
То, что мы имеем внутри, Coffee
- это то Builder
, к чему мы передаем обязательныйtype
кофе. Прикованные к этому вызову, мы делаем другие вызовы, добавляя в наши предпочтения другие ингредиенты.
Кто-то еще, кто хочет другой кофе, может легко сделать это. Это приводит к огромной гибкости при строительстве объектов.
Другие подходы к решению этой проблемы, такие как использование сеттеров, имеют много внутренних проблем. Это решение приводит к тому, что код становится трудным для чтения, а также ведет себя беспорядочно в многопоточных программах. Шаблон строителя решает все эти проблемы.
Преимущества использования шаблона построителя:
- Это упрощает создание объекта
- Приводит к более читабельному коду
- Не позволяет изменять значения
Синглтон
Шаблон Singleton является самым известным среди всех шаблонов дизайна. То, что делает этот шаблон, очень ясно из его названия - он допускает только один экземпляр класса для JVM в любой момент времени.
Хорошее сравнение в реальном мире, вероятно, будет президентом нации.
Однако здесь есть заявление об отказе от ответственности - для каждой JVM может быть только один экземпляр этого класса . Если у вас есть приложение Java, которое выполняется как часть кластера серверов приложений, каждый сервер запускает отдельный экземпляр JVM. Следовательно, вам разрешено иметь один экземпляр синглтона, созданного на каждом сервере приложений, в любой данный момент времени.
При создании одноэлементного класса нужно помнить несколько вещей.
- Конструктор должен быть
private
, чтобы предотвратить возможность других объектов создавать экземпляры вашего класса. - В Java создайте Singleton, используя
Enum
. - JEE 7 имеет встроенную аннотацию
@Singleton
, а также другие связанные аннотации. - Основным недостатком использования шаблона синглтона является то, что полученный код сложен для модульного тестирования. Примите четкое решение относительно того, где вам абсолютно необходимо использовать синглтон, а где нет.
- В таких средах, как Spring, управляемые объекты называются bean-компонентами, а bean-компоненты по умолчанию являются синглетонами. Что хорошо для Spring - это убедиться, что все это на заднем плане.
Шаблон фабричного метода
Цель шаблона метода фабрики - создать семейство типов объектов. Давайте посмотрим на пример кода.
Джава
xxxxxxxxxx
1
public class FactoryPattern {
2
public static class PersonFactory {
3
public static Person getPerson(String name, String gender) {
4
if(gender.equalsIgnoreCase("M")) {
5
return new Male(name);
6
} else if(gender.equalsIgnoreCase("F")) {
7
return new Female(name);
8
} // So on
9
return null;
10
}
11
}
12
}
13
static abstract class Person {
15
Person(String name) {
16
this.name = name;
17
}
18
private String name;
20
abstract String getSalutation();
22
String getNameAndSalutation() {
24
25
}
26
}
27
static class Male extends Person {
29
public Male(String name) {
30
super(name);
31
}
32
34
String getSalutation() {
35
return "Mr";
36
}
37
}
38
static class Female extends Person {
40
public Female(String name) {
41
super(name);
42
}
43
45
String getSalutation() {
46
return "Miss/Mrs";
47
}
48
}
49
public static void main(String[] args) {
51
Person male = PersonFactory.getPerson("Robinhood", "M");
52
System.out.println(male.getNameAndSalutation);
53
Person female = PersonFactory.getPerson("Mary", "F");
55
System.out.println(female.getNameAndSalutation);
56
}
Этот код реализует PersonFactory
. Этот класс имеет статический метод с именем, getPerson()
который принимает имя и пол человека в качестве параметров. В зависимости от String
переданного пола он либо возвращает объект, Male
либо Female
объект.
Если кто-то хочет создать мужчину, он вызывает getPerson()
метод PersonFactory
с аргументом пола "M"
. Точно так же вы можете создать женскую личность, вызвав getPerson()
метод PersonFactory
с аргументом пола "F"
.
Мы передаем идентификатор типа объекта мы нуждаемся, в момент создания, в то же время имея в виду общего типа, Person
.
Male
И Female
классы скрыты за PersonFactory
реализацией.
Преимущество использования шаблона абстрактного метода заключается в том, что вы можете добавлять дополнительные типы в фабрику без особых изменений в других классах, использующих этот класс. В нашем примере вы можете добавить больше типов пола, не затрагивая существующий код, который работает с другими полами, которые все используют Person
.
Как насчет сложности, связанной с созданием объекта?
Это значительно упрощает задачу создания объекта. Он PersonFactory
принимает решение о том, какой объект создать, и доставляет его нам.
Структурные шаблоны проектирования
Давайте теперь посмотрим на структурные шаблоны проектирования, которые мы хотим исследовать.
Прокси шаблон
Прокси-объект - это объект, который представляет другой объект.
Давайте посмотрим на реальный пример.
Ваша дебетовая карта является прокси для вашего банковского счета. Всякий раз, когда вы совершаете транзакцию с использованием дебетовой карты, соответствующие деньги списываются с банковского счета.
Дебетовая карта является прокси для вашего банковского счета, который является фактическим объектом.
Аналогично этому, в программировании вам, возможно, придется программировать взаимодействия с удаленными объектами. В таких ситуациях вы создаете прокси-объект, который заботится обо всех внешних коммуникациях. Вы будете общаться с прокси, как если бы он находился на вашем локальном компьютере.
Хорошим примером являются интерфейсы EJB Home и Remote.
Прокси-сервер скрывает сложность взаимодействия с реальным объектом.
Образец Декоратора
Шаблон декоратора позволяет нам динамически добавлять обязанности к объектам.
В объектно-ориентированном программировании мы обычно используем много наследования.
Пример 1
Допустим, в конкретном пиццерии есть 10 видов пиццы. Наша реализация имеет 10 классов для этих видов пиццы.
Теперь существует требование сделать эти пиццы доступными с тремя типами начинки. Если мы хотим создать индивидуальные классы для каждой комбинации пиццы и топпинга, у нас есть 30 классов для управления.
Вместо того, чтобы сделать это, можем ли мы сделать динамичные отношения между пиццами? Можем ли мы добавить начинку поверх существующей пиццы?
Нам нужно использовать топпинг в качестве декоратора поверх любой пиццы.
Пример 2
Другим примером будет добавление скидки на заказ пиццы.
Допустим, у вас есть заказ, и по некоторым критериям вы хотите предложить скидку покупателю. Там могут быть различные скидки, которые могут применяться в разное время. Если вы добавляете различные типы скидок к каждому типу заказа, то в статических отношениях вам нужно поддерживать сотни классов.
Рассмотрение скидки в качестве декоратора для заказа делает отношения динамичными.
Пример 3
Очень хороший пример, где шаблон декоратора реализован в Java, - это пакеты ввода-вывода Java. Это отражено в том, как мы создаем поток ввода в программе ввода / вывода:
Джава
xxxxxxxxxx
1
new LineNumberInputStream(new BufferedInputStream(new FileInputStream()));
У вас есть FileInputStream
. Если вы хотите сделать его буферизованным, то добавьте к нему декоратор в форме a BufferedInputStream
. Если вы хотите, чтобы буферизованные FileInputStream
номера имели дополнительно номера строк, добавьте декоратор для a LineNumberInputStream
.
Резюме
Шаблон Decorator позволяет добавлять поведение к существующим объектам во время выполнения. Это позволяет пользователю интерфейса решать, как он хочет создавать объекты.
Недостатком этого подхода является сложность создания объектов. Пользователь должен понимать множество классов и их взаимосвязи, прежде чем сможет использовать возможности декоратора.
Образец фасада
Фасад - это отдельный класс, представляющий целую подсистему.
Давайте возьмем пример менеджера событий. Менеджер событий - это человек, который хочет организовать мероприятие. Они занимаются несколькими аспектами мероприятия, такими как украшения, еда, рассылка приглашений гостям, музыкальные аранжировки и другие подобные вещи. Менеджер событий выступает в качестве фасада подсистемы организации мероприятий.
Рассмотрим случай распределенной системы. Обычно у вас есть необходимость в нескольких вызовах между слоями.
Возьмем, к примеру, систему, которая предлагает услугу онлайн-заказов на книги. Всякий раз, когда поступает заказ, необходимо позаботиться о нескольких вещах, таких как проверка запаса, резервирование заказа, принятие платежа, обновление запаса и генерация счета.
Мы можем создать единый фасад, такой как интерфейс заказа, который бы управлял всеми входящими заказами и предоставлял интерфейс клиенту.
Преимущество использования шаблона фасада состоит в том, что он уменьшает количество сетевых вызовов, а также уменьшает связь между классами.
Он успешно устанавливает границу транзакции между взаимодействующими объектами. Фасады, как и услуги, являются хорошими центрами для осуществления транзакций.
Пока интерфейс фасада остается тем же, детали реализации подсистемы могут изменяться.
Шаблон адаптера
Адаптер используется для соответствия интерфейсам разных классов.
Давайте возьмем реальный пример адаптеров питания.
Проблема : если вы покупаете мобильный телефон в Индии, он поставляется с зарядным устройством, которое работает только с розетками, используемыми в Индии. Например, если вы возьмете такое же зарядное устройство в США, оно не будет работать, так как там оно не поместится в розетки.
Решение . Решение заключается в использовании дорожного адаптера, который можно использовать с зарядным устройством во время путешествия. Вы можете подключить зарядное устройство к сетевому адаптеру, а сетевой адаптер используется для подключения к розетке в конкретной стране.
Точно так же, когда вы пытаетесь общаться с системой, которая использует другой формат сообщения или язык, вам нужен адаптер для перевода сообщений.
Интересным примером является связь между Java-программой и веб-сервисом. Перед отправкой данных в сервис нам нужно преобразовать объект в формат XML или JSON. Мы реализуем шаблон адаптера!
Образец мухи
Давайте рассмотрим несколько сценариев
- Создание объекта занимает много времени и включает в себя несколько экземпляров
- Каждый экземпляр объекта занимает много памяти
- Некоторые объекты могут использоваться несколько раз в одном приложении с одинаковыми значениями
В этих сценариях вы можете не захотеть создавать новый экземпляр каждый раз, когда это необходимо.
Как насчет кэширования экземпляра и его повторного использования при необходимости?
Flyweight представляет собой создание мелкозернистого экземпляра, который используется для эффективного совместного использования.
Пример 1
Действительно хороший пример из реальной жизни - коммутируемая телефонная сеть общего пользования (PSTN).
В КТСОП количество линий всегда ограничено, и для простоты предположим, что это число равно 10. Однако есть тысячи клиентов, которые используют эти линии. Поскольку все 1000 абонентов не будут совершать звонки в одно и то же время, можно эффективно переключать входящие звонки между 10 существующими линиями.
Пример 2
В мире программного обеспечения хорошим примером шаблона Flyweight являются соединения JDBC.
Пул соединений - это набор соединений с базой данных. Приложение может запускать много запросов, но мы не создаем новое соединение, когда приходит новый запрос. Как только запрос приходит, мы сопоставляем его с доступным соединением, и запрос запускается. Когда выполнение запроса завершено, соединение освобождается обратно в пул.
Использование такого инструмента позволяет нам избежать затрат, связанных с созданием и закрытием соединения.
Поведенческие шаблоны дизайна
Давайте теперь посмотрим на модели поведения поведения.
Схема цепочки ответственности
Шаблон цепочки ответственности представляет собой способ передачи запроса между цепочкой объектов.
Пример 1
Лучший пример этого шаблона можно увидеть в механизме обработки исключений большинства языков программирования.
Предположим, у вас есть method1()
звонок method2()
, и method2()
, в свою очередь, звонки method3()
. Предположим, что method3()
выдает исключение.
Если method3()
нет обработки исключений, то исключение передается для method2()
его обработки. Если снова method2()
нет обработки исключений внутри, то исключение передается method1()
. Если даже method1()
не может справиться с этим, он также выбрасывается method1()
.
Пример 2
Рассмотрим реальный пример процесса утверждения кредита.
У банковского клерка есть разрешение на утверждение кредитов в пределах определенной суммы. Если сумма превышает эту сумму, она отправляется руководителю. Надзорный орган имеет аналогичный, хотя и больший лимит одобрения кредита, установленный для него. Если сумма кредита превышает этот лимит, то он переходит к его руководителю и так далее.
Резюме
С помощью шаблона Chain of Responsibility у нас есть цепочка объектов, которые готовы и ожидают обработки запросов. Когда новый запрос поступает в систему, он переходит к первому объекту в цепочке, чтобы попытаться обработать. В зависимости от условий обработки запрос перемещается вверх по цепочке и полностью обрабатывается на каком-то уровне, а может и не обрабатывается вообще.
Шаблон итератора
Шаблон Iterator является одним из самых простых шаблонов проектирования. У вас есть набор элементов, расположенных в коллекции, и вы хотите последовательно получить доступ к этим элементам. Хорошим примером итератора является пульт ДУ телевизора, который имеет кнопки «следующий» и «предыдущий» для просмотра ТВ-каналов. Нажатие кнопки «следующий» переводит меня на один канал в прямом направлении, а нажатие кнопки «предыдущий» переводит меня на один канал в обратном направлении.
В работах по программированию примеры Iterator
класса и расширенного for
цикла в Java являются примерами шаблона Iterator.
Государственный Образец
Шаблон состояния используется для изменения поведения объекта при изменении его состояния.
Взгляните на этот пример Java:
Джава
xxxxxxxxxx
1
public class StatePattern {
2
static class FanWallControl {
3
private SpeedLevel current;
4
5
public FanWallControl() {
6
current = new Off();
7
}
8
public void set_state(SpeedLevel state) {
10
current = state;
11
}
12
13
public void rotate() {
14
current.rotate(this);
15
}
16
18
public String toString() {
19
return String.format("Fan Wall Control [current = %s]", current);
20
}
21
}
22
interface speedLevel {
24
void rotate(FanWallControl fanWallControl);
25
}
26
27
static class Off implements SpeedLevel {
28
public void rotate(FanWallControl fanWallControl) {
29
fanWallControl.set_state(new SpeedLevel1());
30
}
31
}
32
33
static class SpeedLevel1 implements SpeedLevel {
34
public void rotate(FanWallControl fanWallControl) {
35
fanWallControl.set_state(new SpeedLevel2());
36
}
37
}
38
39
static class SpeedLevel2 implements SpeedLevel {
40
public void rotate(FanWallControl fanWallControl) {
41
fanWallControl.set_state(new SpeedLevel3());
42
}
43
}
44
45
static class SpeedLevel3 implements SpeedLevel {
46
public void rotate(FanWallControl fanWallControl) {
47
fanWallControl.set_state(new Off());
48
}
49
}
50
}
Давайте возьмем пример настенного управления вентилятором. Настенное управление вентилятором контролирует скорость вращения вентилятора. Он имеет уровни скорости от 0 до 5. Когда он находится на уровне 0, вентилятор не вращается, и он вращается быстрее всего на уровне 5.
Когда вы поворачиваете ручку управления вентилятором, уровень изменяется, и это также приводит к изменению скорости вентилятора. Это классический случай изменения состояния (уровня), вызывающего изменение поведения (скорости).
FanwallControl
Объект состоит из SpeedLevel
объекта. SpeedLevel
это интерфейс, который имеет четыре различных реализации. Первоначально уровень находится на уровне Off
, и при нажатии кнопки поворота в это время новая скорость равна SpeedLevel1
. Происходит последовательно, и если вы поворачиваетесь на SpeedLevel3
, уровень возвращается к Off
.
Если вам нужно определить дополнительный уровень скорости, просто добавьте новый класс, который реализует SpeedLevel
интерфейс и реализует его метод rotate.
Это отличный пример, который подчеркивает преимущества расширяемого класса.
Шаблон стратегии
Задача стратегии заключается в инкапсуляции алгоритма внутри класса. Давайте посмотрим на пример кода Java:
Джава
xxxxxxxxxx
1
public class StrategyPattern {
2
interface Sortable {
3
public int[] sort(int[] numbers);
4
}
5
static class BubbleSort implements Sortable {
7
8
public int[] sort(int[] numbers) {
9
//sort using bubble sort algorithm
10
return numbers;
12
}
13
}
14
static class QuickSort implements Sortable {
16
17
public int[] sort(int[] numbers) {d
18
//sort using quicksort algorithm
19
return numbers;
21
}
22
}
23
static class ComplexClass {
25
private Sortable sorter;
26
ComplexClass(Sortable sorter) {
28
this.sorter = sorter;
29
}
30
void doAComplexThing() {
32
int[] values = null;
33
//logic...
35
sorter.sort(values);
37
//logic...
39
}
40
}
41
public static void main(String[] args) {
43
ComplexClass complexClassInstance = new ComplexClass(new BubbleSort());
44
complexClassInstance.doAComplexThing();
45
}
46
}
Класс ComplexClass
намерен выполнять много сложной логики внутри него. Одной из частей этой логики является сортировка набора значений. Одним прямым способом было бы реализовать всю логику сортировки внутри ComplexClass
. Это сделало бы его очень негибким, поскольку, если вы захотите изменить логику сортировки завтра, весь код должен измениться.
Когда мы используем шаблон стратегии, мы отделяем алгоритм того, как производится сортировка ComplexClass
.
Мы определяем интерфейс с именем Sortable
, у которого есть метод с именем sort()
. Любой фактический алгоритм сортировки является реализацией Sortable
и должен переопределять sort()
метод.
Теперь ComplexClass
задана конкретная Sortable
реализация в качестве аргумента конструктора. ComplexAlgorithm
не волнует, какой именно алгоритм сортировки используется; Он счастлив, что этот объект реализует sort()
метод Sortable
.
Большая гибкость достигается благодаря использованию шаблона стратегии. Вы можете динамически менять стратегию и передавать правильную в соответствии с контекстом.
Образец Наблюдателя
Шаблон наблюдателя - это способ уведомления об изменении количества классов.
Если вы фанат крикета, вы можете знать, когда Сачин Тендулкар забивает столетие, чтобы вы могли праздновать.
Все такие похожие люди зарегистрировались бы для случая, когда Сачин выиграл столетие. Каждый из этих людей теперь является наблюдателем этого события. Всякий раз, когда Сачин забивает столетие, централизованная программа уведомит каждого наблюдателя.
Другой пример - онлайн-торги. Группа участников аукциона регистрируется самостоятельно, чтобы получать уведомления, когда ставится более высокая ставка. Как только ставка будет выше текущей, все зарегистрированные участники узнают об этом.
Реализация шаблона проектирования Observer состоит из двух основных частей.
- Регистрация - где заинтересованные объекты регистрируются в централизованной программе для получения уведомлений.
- Уведомление - где зарегистрированные наблюдатели получают уведомления от централизованной программы
Вот простая реализация шаблона Observer:
Джава
xxxxxxxxxx
1
public class Observer Pattern {
2
static class SachinCenturyNotifier {
3
List<SachinFan> fans = new ArrayList<SachinFan>();
4
5
void register(SachinFan fan) {
6
fans.add(fan);
7
}
8
void sachinScoredACentury() {
10
for(SachinFan fan: fans) {
11
fan.announce();
12
}
13
}
14
}
15
static class SachinFan {
17
private String name;
18
19
SachinFan(String name) {
20
this.name = name;
21
}
22
void announce() {
24
System.out.println(name + " notified");
25
}
26
}
27
public static void main(String[] args) {
29
SachinCenturyNotifier notifier = new SachinCenturyNotifier();
30
notifier.register(new SachinFan("Ranga"));
31
notifier.register(new SachinFan("Ramya"));
32
notifier.register(new SachinFan("Veena"));
33
notifier.sachinScoredACentury();
35
}
36
}
Мы создали экземпляр SachinCenturyNotifier
и зарегистрировали трех фанатов.
Всякий раз, когда Сачин забивает столетие, ему звонят notifier.sachinScoredACentury()
, и все три фаната получают уведомление.
Шаблон посетителя
Шаблон посетителя позволяет нам добавлять новую операцию в класс без изменения класса.
При разработке фреймворков существует множество сценариев, когда мы не хотим, чтобы другие люди модифицировали код в фреймворке. Мы хотим, чтобы другие расширяли функциональность, не касаясь кода платформы. Им разрешено добавлять новые операции, но не изменять существующие.
Шаблон посетителя позволяет вам сделать это.
Хороший реальный пример шаблона посетителя - работа компании такси.
Как только человек звонит в компанию такси и отправляется такси, компания принимает посетителя. Когда посетитель или клиент садится в такси, он больше не контролирует, куда идет. Водитель такси теперь находится под контролем.
Если мы посмотрим на него как на объектно-ориентированный код, класс драйвера контролирует класс клиента. Класс драйвера может добавлять новые операции поверх клиента / посетителя.
Шаблон Метод Шаблон
Шаблон шаблонного метода используется для передачи точных шагов алгоритма подклассу.
Хороший пример этого шаблона - пример того, как мы создаем план дома. Любой хороший план дома состоит из плана этажа, фундамента, водопровода, каркаса и проводки. Такой план практически идентичен для каждого дома.
Если вам нужно смоделировать это в программном обеспечении, вы можете создать шаблонный класс с этим стандартным поведением Подкласс может расширить это и дать фактические реализации. Такие детали могут включать тип деревянного настила, цвета краски для стен и любые дополнительные крылья по мере необходимости.
Хороший пример шаблона метода шаблона находится в среде Spring в форме AbstractController
:
Джава
xxxxxxxxxx
1
2
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
3
checkRequest(request);
4
prepareResponse(response);
5
if(this.synchronizeOnSession) {
7
HttpSession session = request.getSession(false);
8
if(session != null) {
9
ObjectMutex mutex = WebUtils.getSessionMutex(session);
10
11
synchronized(mutex) {
12
return handleRequestInternal(request, response);
13
}
14
}
15
}
16
return handleRequestInternal(request, response);
17
}
handleRequest()
просто заботится об основных вещах. Тем не менее, он оставляет львиную долю для реализации метода handleRequestInternal()
. Этот метод определяется подклассами, где может быть реализована более конкретная логика.
Шаблонный шаблонный метод предназначен для выполнения высокоуровневых шагов и передачи низкоуровневых деталей подклассам. Подклассы могут переопределять низкие шаги и обеспечивать их собственную реализацию.
Шаблон команд
Шаблон команды инкапсулирует командный запрос как объект.
Давайте возьмем пример из реальной жизни.
Рассмотрим сценарий, когда клиент идет в ресторан и хочет сделать заказ на еду. Автор просто пишет заказ, который он получает на листе бумаги, и передает его шеф-повару. Шеф-повар выполняет заказ, а затем готовит еду. Он передает лист бумаги менеджеру.
Устный заказ от клиента теперь стал бумажным объектом. Этот листок бумаги является командным объектом. Объект команды содержит все детали, необходимые для выполнения запроса.
Точно так же в объектно-ориентированном программировании мы можем инкапсулировать все детали запроса в объект и передать этот объект для его выполнения.
В веб-приложениях, когда пользователь вводит данные в форме, эти данные фиксируются в одном объекте запроса, который затем передается.
Интерфейс java.lang.Runnable
также является хорошим примером того, как реализован этот шаблон. Мы создаем потоки в Java, расширяя Runnable
интерфейс, который имеет всю логику для выполнения в своем start()
методе. Когда мы хотим создать и запустить поток, мы передаем этот класс start()
методу.
Метод Памяти
Шаблон памятки фиксирует, а затем восстанавливает внутреннее состояние объекта.
Многие игры, в которые мы играем, предлагают возможность промежуточного сохранения. В определенный момент игры вы можете сохранить его, а затем вернуться к нему.
Для этого нам необходимо сохранить внутренние состояния игровых объектов и восстановить их в определенный момент времени.
Эта функция сохранения-возврата может быть реализована с использованием сериализации на языке, таком как Java.
Шаблон сувенира очень полезен для реализации операций отмены / возврата.
Например, если вы работаете над текстовым документом в текстовом редакторе. Если в определенный момент вы решите отменить изменения, вы можете видеть каждую отмену, пока не достигнете точки, в которой вы удовлетворены. Вы вернулись к ранее сохраненному состоянию документа.
Образец Посредника
Шаблон посредника используется для определения упрощенной связи между классами.
Возьмите пример авиадиспетчера (УВД). Допустим, в любой момент времени в Индии у нас около 500 рейсов в воздухе. Нам нужно определиться с маршрутами, которые должен пройти каждый из этих рейсов. Это также включает определение времени, в которое каждый из этих полетов взлетает и приземляется. Это была бы очень сложная ситуация, если бы каждый из этих 500 рейсов должен был поговорить друг с другом и прийти к приемлемому расписанию маршрутов.
Вот почему у нас есть концепция УВД. Полеты связываются с УВД, и, собрав информацию со всех рейсов, УВД принимает решения и передает их обратно на рейсы.
В мире программного обеспечения хорошим примером шаблона Mediator является ESB (Enterprise Service Bus). В распределенной системе вместо того, чтобы позволить приложениям общаться друг с другом, приложение отправляет сообщение в ESB. ESB направляет запрос в приложение, которое должно обработать запрос. Он действует как посредник.
Проверьте наше видео на ту же тему:
Резюме
В этой статье мы кратко рассмотрели различные шаблоны проектирования.
Шаблон проектирования - это подход к решению проблемы в данном контексте. Мы сосредоточились на понимании контекста, в котором конкретный шаблон может быть применим к реальным примерам.