Базовое обучение: UML Четыре отношения.
Связь степени связи
Обобщение = Реализация > Композиция > Агрегация > Ассоциация > Зависимость
Зависимость
выучить java
Человек (Person) может купить машину (car) и дом (House), тогда можно сказать: Класс Person зависит от класса Car и класса House
Обратите внимание, что он отличается от следующих ассоциаций: класс Person не использует атрибуты типа Car и House, а экземпляры Car и House передаются в метод buy() в качестве параметров.
Зависимости отражаются в языке Java как локальные переменные, параметры метода или вызовы статических методов.
Ассоциация
выучить java
Это делает один класс осведомленным о свойствах и методах другого класса.
Ассоциации могут быть двунаправленными или однонаправленными.
В языке Java ассоциации обычно реализуются с использованием переменных-членов.
Агрегация
выучить java
Агрегация - это своего рода ассоциативное отношение, и это сильное ассоциативное отношение.
Агрегация есть отношение между целым и единичным, но индивидуальное может существовать без целого.
Например, отношение между классом автомобиля и классом двигателя, классом шин и классом других частей — это отношение между целым и отдельным.
Как и ассоциации, агрегации реализуются через переменные-члены. Однако два класса, участвующих в отношении ассоциации, находятся на одном уровне, в то время как в отношении агрегации два класса находятся на неравном уровне, один из которых представляет целое, а другой представляет часть.
Сочинение
выучить java
Комбинация — это своего рода ассоциативное отношение, которое сильнее, чем отношение агрегации, и также проявляется в виде переменных-членов.
В определенный момент некоторые объекты могут быть объединены только с целым объектом, и последний отвечает исключительно за жизненный цикл.
Части и целые имеют одинаковый жизненный цикл.
Целое может передать часть другому объекту, и в этом случае время жизни части контролируется новым целым, а затем старое целое может умереть.
, Режим стратегии
Что такое паттерн стратегии
Некоторое поведение в классе может измениться по мере итерации системы. Чтобы класс удовлетворял принципу открытости-закрытости (т. е. масштабируемости или отказоустойчивости), нам необходимо отделить эти будущие динамические поведения от класса и предоставить эти поведения таким образом, чтобы прогнозировать будущее развитие бизнеса Абстрактные общие черты, инкапсулировать их в абстрактных классах или интерфейсах и обеспечивают конкретное поведение через свои реализующие классы. Исходный класс должен содержать ссылку на абстрактный класс/интерфейс. При использовании вставьте конкретный объект класса реализации в ссылку на интерфейсный/абстрактный класс, хранящийся в классе.
описание диаграммы классов
выучить java
Если в классе А есть два поведения X и Y, которые будут меняться по мере развития бизнеса, то нам необходимо выделить эти два поведения из класса А и сформировать свою систему наследования (систему стратегий). Родительский класс/интерфейс верхнего уровня каждой системы наследования (системы стратегии) определяет абстрактные функции общего поведения, а каждый подкласс/класс реализации определяет конкретную реализацию системы стратегии.
Среди них каждая абстрактная система наследования называется системой стратегии, а каждый конкретный класс реализации называется стратегией.
На данный момент система стратегий построена, и теперь необходимо преобразовать класс А.
Добавьте родительский класс/интерфейс верхнего уровня требуемой стратегической системы в класс A и предоставьте общее действие функции для использования вызывающей стороной.
В проекте Spring зависимость между классом стратегии и классом A может быть реализована посредством внедрения зависимостей.
Итак, шаблон стратегии построен, давайте рассмотрим анализ преимуществ и недостатков.
Преимущества паттерна стратегии
1. Удовлетворить принцип открытого и закрытого
Если классу A необходимо изменить стратегию, ему нужно только изменить XML-файл конфигурации Spring, а все остальные коды изменять не нужно.
Например, метод замены стратегии X_1 класса A на X_2 выглядит следующим образом:
<bean id=“a” class=“类A”>
<!– 将原本策略实现类X_1修改为策略实现类X_2即可 –>
<property name=“策略接口X” class=“策略实现类X_2” />
</bean>
Кроме того, если вам нужно добавить новую стратегию, вам нужно всего лишь добавить новый класс реализации для интерфейса стратегии X, и переопределить в нем функцию commonAction. Затем измените файл XML, как указано выше.
В этом процессе, исходя из предпосылки сохранения исходного кода Java без изменений, новые функции расширяются, чтобы соответствовать принципу открытости и замкнутости.
2. Можно легко создавать объекты с разными стратегиями
Если нам нужно создать несколько объектов класса А по разным стратегиям, то этого можно легко добиться с помощью паттерна стратегии.
Например, мы хотим создать три объекта класса A, a, b и c. Среди них a использует политики X_1 и Y_1, b использует политики X_2 и Y_2, а c использует политики X_3 и Y_3.
Чтобы создать эти три объекта, нам нужно всего лишь настроить в XML следующее:
<bean id=“a” class=“类A”>
<property name=“策略接口X” class=“策略实现类X_1” />
<property name=“策略接口Y” class=“策略实现类Y_1” />
</bean>
<bean id=“b” class=“类A”>
<property name=“策略接口X” class=“策略实现类X_2” />
<property name=“策略接口Y” class=“策略实现类Y_2” />
</bean>
<bean id=“c” class=“类A”>
<property name=“策略接口X” class=“策略实现类X_3” />
<property name=“策略接口Y” class=“策略实现类Y_3” />
</bean>
вопросы и ответы
Q: Как реализовать частичное наследование? То есть класс Сын1 наследует только часть функций Отца, а Сын2 наследует другую часть функций Отца.
Это недостаток дизайна.Когда это происходит, родительский класс должен быть снова разделен на два подкласса, чтобы гарантировать, что поведение и характеристики любого родительского класса разделяются системой наследования!
выучить java
Вопрос. Что делать, если в родительский класс необходимо добавить стандартные варианты поведения по мере изменения требований? Это нарушает принцип «открыто-закрыто».
Это не нарушает принцип «открыто-закрыто»! В процессе итеративного обновления системы неизбежно изменение исходного кода, не нарушающего «принцип открытости и закрытости».
«Принцип открытости и закрытости» требует от нас: когда система находится в итеративном процессе, когда определенный тип требования появляется впервые, его разрешается модифицировать; в это время система должна быть модифицирована и спроектирована разумно, чтобы убедитесь, что повторная модификация этого типа требований является расширяемой. Когда этот тип спроса возникает снова, исходный код не должен быть изменен, и разрешены только расширения для удовлетворения спроса.
, Шаблон наблюдателя
Что такое шаблон наблюдателя
Если выполняются следующие требования сценария, необходимо использовать шаблон наблюдателя.
Если имеется ряд классов, всем им необходимо получить указанные данные из указанного класса, а при получении данных необходимо запустить соответствующую бизнес-логику. Этот сценарий можно реализовать с помощью шаблона Observer.
В шаблоне наблюдателя есть две роли, а именно: наблюдатель и наблюдаемый. Наблюдаемое лицо является поставщиком данных. У них отношение «многие к одному».
описание диаграммы классов
выучить java
-
Наблюдаемый человек является поставщиком данных, а наблюдатель — получателем данных.
-
Для обычного класса, если вы хотите стать наблюдателем и получить указанные данные, необходимо выполнить следующие действия:
- Во-первых, вам нужно реализовать интерфейс Observer и реализовать функцию обновления;
- Затем определите бизнес-логику после получения данных в этой функции;
update(Observable, Object) имеет два параметра:
Observable: наблюдаемый объект (поставщик данных)
Объект: сами данные - Наконец, внедрите наблюдателя в наблюдаемое, вызвав метод observable() addObservable() или через XML-файл конфигурации Spring. В этот момент объект-наблюдатель будет добавлен в список наблюдаемых.
-
Вызывающий является реальным поставщиком данных. Когда вызывающему объекту необходимо передать последние данные, ему нужно только вызвать функцию notidyObservers() наблюдателя, которая будет проходить по коллекции List и вызывать функцию обновления каждого наблюдателя, в свою очередь, для завершения передачи данных и запуска каждого наблюдателя для получить Бизнес-логика данных.
Два способа регистрации наблюдателей
Есть два способа зарегистрировать Observer в Observable:
1. Перед запуском через Spring XML
Перед запуском системы, если количество наблюдателей можно определить и оно не изменится в процессе работы, то Список можно заполнить в XML Внедрение объекта, таким образом код будет более лаконичным.
1. Настройте все bean-компоненты-наблюдатели
<!– 创建observerA –>
<bean name=“observerA” class=“ObservserA”>
</bean>
<!– 创建observerB–>
<bean name=“observerB” class=“ObservserB”>
</bean>
2. Настройте наблюдаемый компонент и внедрите все наблюдаемые компоненты в наблюдаемый компонент.
<!– 创建observable –>
<bean name=“observable” class=“Observable”>
<property name=“observerList”>
<list>
<ref bean=“observerA” />
<ref bean=“observerB” />
</list>
</property>
</bean>
2. Во время работы через функцию addObserver()
Когда Spring инициализируется, все объекты Observer вводятся в списокObserverList of Observable через функцию addObserver().
@Component
public class InitConfiguration implements ApplicationListener<ContextRefreshedEvent>{
@Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
if(event.getApplicationContext().getParent() == null){
Observable observable = (Observable)event.getApplicationContext().getBean(“observable”);
ObserverA observerA = (ObserverA)event.getApplicationContext().getBean(“observerA”);
ObserverB observerB = (ObserverB)event.getApplicationContext().getBean(“observerB”);
observable.setObserverList(Arrays.asList(observerA, observerB));
}
}
}
Для инициализации всех наблюдателей рекомендуется использовать первый метод, кроме того, наблюдателю по-прежнему необходимо предоставить функцию addObserver(), чтобы система динамически добавляла и удаляла объекты наблюдателя во время выполнения.
Инструментарий шаблона наблюдателя, предоставленный JDK
JDK уже предоставляет набор инструментов для шаблона Observer, включая класс Observable и интерфейс Observer. Чтобы реализовать шаблон наблюдателя, вы можете напрямую использовать эти два набора инструментов.
, Декоративный режим
когда использовать
-
Необходимо улучшить функциональность некоторых функций в объекте.
-
Необходимо динамически добавлять функции к объекту, эти функции могут быть динамически отозваны.
-
Необходимость добавления большого количества функций, возникающих в результате перестановки и комбинации некоторых основных функций, приводит к взрыву системы наследования.
описание диаграммы классов
выучить java
Различные роли в режиме украшения являются:
-
Роль абстрактного компонента: предоставляет абстрактный интерфейс для стандартизации объектов, которые готовы взять на себя дополнительные обязанности.
-
Роль конкретного компонента: определите класс, который получит дополнительные обязанности.
-
Роль декоратора: содержит экземпляр объекта Component и определяет интерфейс, соответствующий интерфейсу абстрактного компонента.
-
Роль декоратора бетона: отвечает за «прикрепление» дополнительных обязанностей к объектам-компонентам.
Decorator содержит переменные-члены Component. Каждый класс реализации Concrete Decorator должен реализовать функцию operation(). Общий процесс этой функции выглядит следующим образом:
class ConcreteDecorator {
private Component component;
返回类型 operation(){
// 执行上一层的operation(),并获取返回结果
返回结果 = component.operation();
// 拿到返回结果后,再做额外的处理
处理返回结果
return 返回结果;
}
}
Процесс использования декорированного класса выглядит следующим образом:
// 准备好所有装饰类
DecoratorA decoratorA = new DecoratorA();
DecoratorB decoratorB = new DecoratorB();
DecoratorC decoratorC = new DecoratorC();
// 准备好 被装饰的类
Component component = new Component();
// 组装装饰类
decoratorC.setComponent(decoratorB);
decoratorB.setComponent(decoratorA);
decoratorA.setComponent(component);
// 执行
decoratorC.operation();
Процесс выполнения следующий:
выучить java
преимущество
1. Целью шаблона декоратора и отношения наследования является расширение функции объекта, но декоратор может обеспечить большую гибкость, чем наследование. Наследование перезаписывает ту функцию, которую нужно расширить путем перезаписи.Конечно, можно также получить исходную функцию через super.xxx(), а затем расширить новые функции на основе этой функции, но оно может только добавить определенную функцию, если вы хотите передать наследование Чтобы добавить несколько функций, вам нужно наследовать несколько классов для достижения этого, а режим декоратора может добавлять новые функции, комбинируя исходные функции. Эти новые функции были инкапсулированы в независимые классы оформления. В течение периода вы можно выбрать декоративные элементы по строительным блокам.
2. Используя различные специфические декоративные классы, а также перестановки и комбинации этих декоративных классов, дизайнеры могут создавать множество комбинаций различных вариантов поведения.
недостаток
1. Эта функция, более гибкая и гибкая, чем наследование, также означает большую сложность.
2. Режим декорирования приведет к множеству мелких классов в дизайне, при чрезмерном его использовании программа сильно усложнится.
3, режим украшения предназначен для программирования типа абстрактного компонента (компонента). Однако, если вы программируете для определенных компонентов, вам следует переосмыслить архитектуру вашего приложения и определить, подходят ли декораторы. Конечно, вы также можете изменить интерфейс компонента, добавить новые общедоступные поведения и реализовать шаблон декоратора «полупрозрачный». Лучший выбор должен быть сделан в реальном проекте.
Принципы дизайна
- Больше используйте композицию, меньше наследование.
Поведение при использовании наследования для создания подклассов статически определяется во время компиляции, и все подклассы наследуют одно и то же поведение. Однако если поведение объекта можно расширить с помощью композиции, его можно расширить динамически во время выполнения.
, Одноэлементный шаблон
Шаблон Singleton в Java является широко используемым шаблоном проектирования. Основная функция шаблона singleton — гарантировать, что в Java-программе существует только один экземпляр класса. Некоторые менеджеры и контроллеры часто разрабатываются как синглтоны.
Одноэлементный паттерн имеет много преимуществ: он позволяет избежать повторного создания объектов-экземпляров, не только сокращает время, затрачиваемое на создание объектов каждый раз, но и экономит место в памяти, позволяет избежать логических ошибок, вызванных работой с несколькими экземплярами. Если объект может выполняться через все приложение и играть роль глобального унифицированного управления, тогда стоит рассмотреть вариант с одноэлементным шаблоном.
Существует множество способов написания одноэлементного шаблона, и большинство из них имеют те или иные недостатки. Эти методы записи будут представлены отдельно ниже.
, Режим голодного человека
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
}
- Конструктор класса определен как закрытый, чтобы гарантировать, что другие классы не смогут создать экземпляр этого класса;
- Затем предоставляется статический экземпляр, который возвращается вызывающей стороне;
- Голодный режим создает экземпляр при загрузке класса, и этот экземпляр существует на протяжении всего цикла программы.
- Преимущества: Экземпляр создается только один раз при загрузке класса, нет ситуации, когда несколько потоков создают несколько экземпляров, и устраняется проблема многопоточной синхронизации.
- Недостаток: этот синглтон будет создан, даже если он не используется, и он создается после загрузки класса, и память тратится впустую.
- Применимый сценарий: Этот способ реализации подходит для случая, когда синглтон занимает небольшой объем памяти и будет использоваться при инициализации. Однако, если память, занимаемая синглтоном, относительно велика или синглтон используется только в определенном сценарии, голодный режим не подходит, в этом случае вам нужно использовать ленивый режим для ленивой загрузки.
, Ленивый режим (имеет проблемы с безопасностью потоков)
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
- В ленивом режиме синглтон создается, когда он необходим.Если синглтон был создан, повторный вызов интерфейса get не будет воссоздавать новый объект, а напрямую вернет ранее созданный объект.
- Если синглтон используется реже, а создание синглтона потребляет много ресурсов, то необходимо создавать синглтон по требованию, в это время хорошим выбором будет использование ленивого режима.
- Однако ленивый режим здесь не рассматривает вопрос безопасности потоков. Несколько потоков могут одновременно вызывать его метод getInstance(), что приводит к созданию нескольких экземпляров. Поэтому для решения проблемы синхронизации потоков необходима блокировка. реализация следующая.
Ленивый режим (потокобезопасный, но неэффективный)
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
Заблокированный ленивый режим, похоже, решает проблему параллелизма потоков и реализует ленивую загрузку, однако он имеет проблемы с производительностью и все еще не идеален. Синхронизированный метод, измененный с помощью synchronized, намного медленнее, чем общий метод.Если getInstance() вызывается несколько раз, накопленная потеря производительности будет относительно большой.
, Ленивый режим (потокобезопасный, высокая эффективность)
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Этот метод добавляет только одну строку кода больше, чем предыдущий метод, то есть уровень суждения, если (экземпляр == ноль) добавляется поверх синхронизированного. Таким образом, при создании синглтона нет необходимости каждый раз вводить блок синхронизированного кода, что может повысить эффективность. Конечно, кроме потока ThreadA, который инициализирует объект-синглтон, может быть еще несколько потоков.После того, как ThreadA создаст синглтон, он входит в блок кода синхронизации, когда блокировка только снята, но в это время есть второй if (instance == null) суждение, что позволяет избежать создания нескольких объектов. И относительно небольшое количество потоков входит в блок синхронизированного кода.
, Статический внутренний класс (ленивый + без блокировки)
public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}
Этот метод также использует механизм загрузки классов, чтобы гарантировать создание только одного экземпляра экземпляра. Как и в режиме голодного человека, он также использует механизм загрузки классов, поэтому проблем с многопоточным параллелизмом не возникает. Разница в том, что он создает экземпляры объектов во внутренних классах. Таким образом, пока внутренний класс не используется в приложении, JVM не будет загружать одноэлементный класс и не будет создавать одноэлементный объект, таким образом реализуя ленивую ленивую загрузку. Другими словами, этот метод может одновременно гарантировать ленивую загрузку и потокобезопасность.
, перечисление
public enum Singleton{
instance;
public void whateverMethod(){}
}
Все четыре упомянутых выше способа реализации синглетонов имеют общие недостатки:
1. Для реализации сериализации требуется дополнительная работа, иначе каждый раз при десериализации сериализованного объекта будет создаваться новый экземпляр.
2. Вы можете использовать отражение, чтобы принудительно вызвать частный конструктор (если вы хотите избежать этого, вы можете изменить конструктор, чтобы он выдавал исключение при создании второго экземпляра).
Класс перечисления очень хорошо решает эти две проблемы.Помимо безопасности потоков и предотвращения вызова конструктора путем отражения, перечисление также предоставляет механизм автоматической сериализации для предотвращения создания новых объектов во время десериализации. Поэтому метод, рекомендованный авторами Effective Java. Однако в реальной работе редко можно увидеть, как люди пишут именно так.
, Шаблон метода шаблона
определение
Поток алгоритма определен в родительском классе, а некоторые неопределенные детали алгоритма реализованы в подклассе в виде абстрактных функций.
Также можно понять, что некоторые шаги набора алгоритмов могут меняться по мере развития бизнеса, тогда мы можем реализовать определенные шаги в родительском классе, а переменные шаги можно реализовать в подклассе как абстрактные функции.
- В шаблоне метода шаблона родительский класс является абстрактным классом, каждый шаг алгоритма инкапсулирован в функцию, а функция templateMethod объединяет все шаги алгоритма.
- Для неизменных шагов используйте private, чтобы предотвратить переопределение подклассов;
- Для переменных шагов, модифицированных с помощью абстрактной защиты, необходимо переопределить подклассы;
- После того как подкласс перепишет все абстрактные функции, вызовите templateMethod для выполнения алгоритма.
, Режим внешнего вида
Идея режима внешнего вида вездесуща в проекте, и она предельно проста для понимания, наверное, все ей пользовались, но до теоретического уровня она не поднялась. Эта идея представлена здесь.
Внешний вид скрывает сложность реализации системных функций и предоставляет клиенту набор предельно простых интерфейсов. Клиенту нужно только знать, какие функции предоставляет интерфейс и как его вызывать, и ему не нужно заботиться о том, как эти интерфейсы реализованы. В результате связь между клиентом и системой значительно снижается, и клиенту нужно иметь дело только с простым набором интерфейсов Facade.
, Режим адаптера
определение
Как платформа для торговли фондами, она должна предоставить набор спецификаций интерфейса для каждой компании фонда. Однако интерфейсы каждой фондовой компании разные, и нет возможности напрямую подключиться к интерфейсу платформы. В настоящее время каждая фондовая компания должна самостоятельно внедрить адаптер, и адаптер завершает преобразование различных интерфейсов, чтобы интерфейс фондовой компании можно было соединить с интерфейсом, предоставляемым платформой.
, Три адаптера
Существует три способа реализации режима адаптера, все они объясняются ниже на примере торговой платформы фонда.
- Торговый интерфейс компании фонда:
/
基金公司的交易接口
/
class FundCompanyTrade{
/
买入函数
/
public void mairu() {
// ……
}
/
卖出函数
/
public void maichu() {
// ……
}
}
- Торговый интерфейс торговой платформы фонда
/
基金交易平台的交易接口
/
interface FundPlatformTrade {
// 买入接口
void buy();
// 卖出接口
void sell();
}
- Торговая платформа фонда вызывает торговый интерфейс каждой компании фонда с помощью следующих кодов:
class Client {
@Autowired
private FundPlatformTrade fundPlatformTrade;
/
买入基金
/
public void buy() {
fundPlatformTrade.buy();
}
/
卖出基金
/
public void sell() {
fundPlatformTrade.sell();
}
}
Способ 1: Адаптер класса
Преобразование интерфейса достигается за счет наследования.
- Адаптер для торговли фондами:
class Adapter extends FundCompanyTrade implements FundPlatformTrade {
void buy() {
mairu();
}
void sell(){
maichu();
}
}
Адаптер Adapter наследует FundCompanyTrade, поэтому он может покупать и продавать FundCompanyTrade; Адаптер адаптера также реализует FundPlatformTrade, поэтому ему необходимо реализовать интерфейсы покупки и продажи.Этот процесс завершает торговый интерфейс компании фонда с платформой фонда. .
При его использовании просто вставьте адаптер в переменную-член fundPlatformTrade класса Client через Spring:
<!– 声明Adapter对象 –>
<bean name=“adapter” class=“Adapter”>
</bean>
<!– 将adapter注入给Client –>
<bean class=“Client”>
<property name=“fundPlatformTrade” ref=“adapter” />
</bean>
Способ 2: Объектный адаптер
Преобразование интерфейса достигается за счет композиции.
- Адаптер для торговли фондами:
class Adapter implements FundPlatformTrade {
@Autowired
private FundCompanyTrade fundCompanyTrade;
void buy() {
fundCompanyTrade.mairu();
}
void sell(){
fundCompanyTrade.maichu();
}
}
Таким образом, адаптер Adapter не наследует FundCompanyTrade, а внедряет объект как переменную-член, что позволяет добиться того же эффекта.
Режим 3: интерфейсный адаптер
Когда есть такой интерфейс, в котором определено более N методов, и мы хотим использовать только один или несколько методов, если мы напрямую реализуем интерфейс, тогда мы должны реализовать все методы, даже если мы только Пустые методы, которые не нужно (просто написать пару фигурных скобок, не реализовывать специфические методы) также сделает этот класс раздутым и неудобным для вызова.В это время мы можем использовать абстрактный класс в качестве промежуточного программного обеспечения, а именно адаптера, Используйте этот абстрактный класс для реализовать интерфейс, и все методы в абстрактном классе пусты, то мы создаем унаследованный класс абстрактного класса и переписываем методы, которые нам нужно использовать.
- Целевой интерфейс: А
public interface A {
void a();
void b();
void c();
void d();
void e();
void f();
}
- Адаптер: Адаптер
Реализуйте все функции, сначала оставив все функции пустыми.
public abstract class Adapter implements A {
public void a(){
throw new UnsupportedOperationException(“对象不支持这个功能”);
}
public void b(){
throw new UnsupportedOperationException(“对象不支持这个功能”);
}
public void c(){
throw new UnsupportedOperationException(“对象不支持这个功能”);
}
public void d(){
throw new UnsupportedOperationException(“对象不支持这个功能”);
}
public void e(){
throw new UnsupportedOperationException(“对象不支持这个功能”);
}
public void f(){
throw new UnsupportedOperationException(“对象不支持这个功能”);
}
}
- Класс реализации: Ашили
Наследуйте класс адаптера и при необходимости переопределите соответствующие функции.
public class Ashili extends Adapter {
public void a(){
System.out.println(“实现A方法被调用”);
}
public void d(){
System.out.println(“实现d方法被调用”);
}
}
, Режим итератора
определение
Шаблон итератора используется для перебора контейнера без знания внутренних деталей контейнера.
Контейнеры используются для хранения данных, а контейнеры имеют самые разнообразные структуры хранения. Без использования шаблона адаптера, если вы хотите перебирать элементы в контейнере, вам необходимо полностью понимать структуру хранения контейнера. Разные структуры хранения приводят к разным методам итерации для разных контейнеров. Это, несомненно, увеличивает стоимость использования нами контейнеров.
Шаблон итератора предлагает новый способ перебора элементов контейнера. Итератор задает набор интерфейсов для перебора контейнеров. Как пользователь контейнера, вам нужно использовать только этот набор итераторов. Сам контейнер должен реализовать этот интерфейс итератора и реализовать в нем функцию итерации. То есть поставщик контейнера должен предоставить реализацию итератора при предоставлении контейнера. Поскольку контейнер сам знает свою собственную структуру хранения, он очень подходит для реализации итерационной функции. Как пользователям контейнера, нам нужно только знать, как использовать итераторы, и нам не нужно понимать структуру хранения внутри контейнера.
описание диаграммы классов
выучить java
В шаблоне итератора есть две роли: итератор и контейнер.
-
Iterator Iterator: интерфейс, который инкапсулирует контейнер итераций.
-
Контейнер Контейнер: что-то, что хранит элементы
- Если у контейнера есть возможность выполнять итерацию, он должен иметь функцию getIterator(), которая вернет объект итератора.
- У каждого контейнера есть собственный внутренний класс итератора, который реализует интерфейс Iterator и реализует две функции hasNext() и next() для итерации.
- boolean hasNext(): используется для определения того, есть ли в текущем контейнере элементы, которые еще не были итерированы.
- Объект next(): используется для получения следующего элемента
Код
- Интерфейс итератора:
public interface Iterator {
public boolean hasNext();
public Object next();
}
- Интерфейс контейнера:
public interface Iterator {
public boolean hasNext();
public Object next();
}
- Конкретный контейнер (должен реализовать интерфейс Container):
public class NameRepository implements Container {
public String names[] = {“Robert” , “John” ,“Julie” , “Lora”};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
- Конкретный контейнер реализует интерфейс Container и реализует функцию getIterator(), которая используется для возврата объекта итератора контейнера.
- Контейнер должен реализовать свой собственный внутренний класс итератора, который реализует интерфейс Iterator и реализует функции hasNext() и next().
Когда контейнер и итератор контейнера созданы, наступает очередь пользователя использовать его, что очень просто:
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println(“Name : “ + name);
}
}
}
- Для пользователей, пока они знают интерфейс Iterator, они могут перебирать все типы контейнеров.
, Комбинированный режим
определение
Режим композиции определяет, как физически хранится древовидная структура.
Вещи с древовидной структурой в реальном мире могут быть представлены шаблоном комбинации в реализации кода.
Например: многоуровневое меню, организационная структура компании и т.д.
Ниже в качестве примера используется многоуровневое меню для введения в комбинированный режим.
пример
Предположим, мы хотим реализовать многоуровневое меню и реализовать операции добавления, удаления, изменения и запроса многоуровневого меню. Меню выглядит следующим образом:
一级菜单A
二级菜单A_1
三级菜单A_1_1
三级菜单A_1_2
三级菜单A_1_3
二级菜单A_2
一级菜单B
二级菜单B_1
二级菜单B_2
二级菜单B_3
二级菜单B_4
三级菜单B_4_1
三级菜单B_4_2
三级菜单B_4_3
一级菜单C
二级菜单C_1
二级菜单C_2
二级菜单C_3
Особенности меню следующие:
- Неограниченная глубина, может иметь меню неограниченного уровня
- Неограниченное количество меню на слой
описание диаграммы классов
- Item представляет узел в дереве;
- Элемент содержит две переменные-члены:
- parent: указать на родительский узел текущего узла
- childList: Список дочерних узлов текущего узла
- Это отношение, в котором Предмет содержит Предмет, составляет режим комбинирования.
Примечание: циклические ссылки
В процессе построения дерева могут возникать циклические ссылки, а при обходе дерева может возникать бесконечный цикл. Поэтому нам нужно избегать циклических ссылок при добавлении узлов.
Мы можем добавить еще одну переменную-член List в Item, чтобы записать путь от корневого узла к текущему узлу. Путь может быть представлен идентификатором каждого узла. Как только новый добавленный идентификатор узла появился в текущем пути, это означает, что существует циклическая ссылка, и в это время должно быть выдано приглашение.
, Режим состояния
сцены, которые будут использоваться
Если в функции появляется большое количество сложных суждений if-else, пора подумать об использовании шаблона состояния.
Поскольку многие операторы if-else часто содержат много бизнес-логики, они, вероятно, будут меняться по мере развития бизнеса. Если эта бизнес-логика написана в классе, то при изменении бизнес-логики класс необходимо модифицировать, тем самым нарушая принцип открытости и закрытости. Шаблон состояния может очень хорошо решить эту проблему.
Шаблон состояния инкапсулирует каждую ветвь суждения в независимый класс, и каждая ветвь суждения становится «состоянием», поэтому каждый независимый класс становится «классом состояния». Текущее состояние поддерживается глобальным диспетчером состояний Context.
описание диаграммы классов
- В режиме состояния каждая ветвь суждения становится состоянием, и каждое состояние инкапсулируется в отдельный класс состояния;
- Все классы состояний имеют общий интерфейс — State
- В интерфейсе State есть функция doAction, и в этой функции завершается логика обработки состояния каждого класса состояния; объект Context должен быть передан в качестве параметра функции doAction. Структура функции следующая:
class StateA implements State{
public void doAction(Context context){
if (满足条件) {
// 执行相应的业务逻辑
}
else {
// 设置下一跳状态
context.setState(new StateB());
// 执行下一跳状态
context.doCurState();
}
}
}
-
Функция doAction каждого класса состояния имеет одну и только одну пару if-else.if-else заполняется бизнес-логикой, когда условия выполняются, а else заполняется бизнес-логикой, когда условия не выполняются.
-
Код в else такой же, всего два шага:
Сначала установите состояние контекста на следующий объект состояния;
Затем вызовите doCurState() контекста для выполнения; -
Класс Context на самом деле является классом, который изначально содержал это огромное и сложное условие if-else. Этот класс содержит объект State, который представляет текущий объект состояния для выполнения.
-
Класс Context должен иметь функцию doCurState, код которой тот же: state.doAction()
-
Код для запуска процесса вынесения решения штата выглядит следующим образом:
// 准备好第一个状态
StateA stateA = new StateA();
// 设置第一个状态
context.setState(stateA);
// 开始执行
context.doCurState();
преимущество
Паттерн состояния разбивает огромный if-else изначально в классе на отдельные классы состояния. Первоначально этот класс, содержащий огромное if-else, стал Context, в котором содержалось текущее состояние. Контекст должен знать только начальный класс состояния и не должен знать о существовании других классов состояния. То есть Context связан только с первым классом состояния. Каждый класс состояния связан только со следующим классом состояния, таким образом образуя цепочку оценки состояния. Связь между классами состояний настраивается через XML-файлы Spring. Таким образом, при изменении логики оценки необходимо добавить только новый класс состояния, и новый класс состояния вставляется в логику оценки путем изменения XML. Это соответствует принципу открытого-закрытого.
прокси-режим
, Режим прокси
выучить java
Режим прокси предназначен для расширения функции класса без изменения целевого класса и пользователей.
В шаблоне прокси есть «целевые объекты» и «прокси-объекты», и они должны реализовывать один и тот же интерфейс. Пользователь напрямую использует прокси-объект, и прокси-объект передает запрос пользователя целевому объекту для обработки. Прокси-объекты могут добавлять дополнительную обработку запросам пользователей.
Использование динамического прокси Java
- Сначала у вас должен быть целевой объект, который должен реализовывать интерфейс:
public interface Subject
{
public void doSomething();
}
public class RealSubject implements Subject
{
public void doSomething()
{
System.out.println( “call doSomething()” );
}
}
- Во-вторых, добавьте дополнительную логику к целевому объекту:
- Настроить класс и реализовать интерфейс InvocationHandler;
- Реализовать функцию вызова и написать логику, которую необходимо добавить в эту функцию;
public class ProxyHandler implements InvocationHandler
{
private Object proxied;
public ProxyHandler( Object proxied )
{
this.proxied = proxied;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
//在转调具体目标对象之前,可以执行一些功能处理
//转调具体目标对象的方法
return method.invoke( proxied, args);
//在转调具体目标对象之后,可以执行一些功能处理
}
}
- Создайте прокси-объект, и вызывающая сторона сможет использовать объект напрямую:
RealSubject real = new RealSubject();
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class},
new ProxyHandler(real));
proxySubject.doSomething();
Перепечатано с:blog.CSDN.net/U010425776/…
В статье есть что-то неуместное, пожалуйста, поправьте меня, вы также можете обратить внимание на мой паблик WeChat:好好学java
, получить высококачественные видеоуроки и учебные ресурсы, такие как java.