предисловие
Основное содержание этой статьи:
- Знакомство с шаблоном декоратора
- Пример
- Типичное применение шаблона декоратора анализа исходного кода
- Шаблон декоратора в Java I/O
- Шаблон декоратора в весенней сессии
- Паттерн декоратора в кеше Mybatis
- Суммировать
шаблон декоратора
Шаблон декоратора: динамическое добавление некоторых дополнительных обязанностей к объекту С точки зрения добавления объектных функций шаблон оформления является более гибким, чем создание реализаций подкласса. Паттерн Decorator — это объектно-структурный паттерн.
В шаблоне декоратора, чтобы сделать систему более гибкой и расширяемой, мы обычно определяем абстрактный класс декоратора и используем конкретный класс декоратора в качестве его подкласса.
Роль
Компонент (абстрактный компонент): Это общий родительский класс для конкретных компонентов и абстрактных классов оформления. Он объявляет бизнес-методы, реализованные в конкретных компонентах. Его введение позволяет клиентам обрабатывать неукрашенные и украшенные объекты согласованным образом, чтобы реализовать прозрачную работу клиента на боковая сторона.
БетонКомпонент: это подкласс класса абстрактного компонента, используемый для определения конкретного объекта компонента, реализующий методы, объявленные в абстрактном компоненте, и декоратор может добавить к нему дополнительные обязанности (методы).
Декоратор (абстрактный класс декоратора): он также является подклассом класса абстрактных компонентов и используется для добавления обязанностей к конкретным компонентам, но конкретные обязанности реализуются в его подклассах. Он поддерживает ссылку на абстрактный объект-компонент, через который может быть вызван метод декорированного объекта-компонента, и этот метод расширяется через его подклассы для достижения цели декорирования.
ConcreteDecorator (класс по декорированию бетона): это подкласс абстрактного класса декоратора, который отвечает за добавление новых обязанностей к компоненту. Каждый конкретный класс декоратора определяет некоторые новые варианты поведения, он может вызывать методы, определенные в абстрактном классе декоратора, и может добавлять новые методы для расширения поведения объекта.
Поскольку и конкретный класс компонента, и класс оформления реализуют один и тот же абстрактный интерфейс компонента, шаблон оформления динамически налагает на объект дополнительные обязанности прозрачным для клиента образом.Другими словами, клиент не чувствует, что объект до декорирования и какая разница после декорирования. Паттерн Decorator расширяет функциональность объекта, не создавая дополнительных подклассов.
орнаментЯдро заключается в разработке абстрактных декоративных классов..
Пример
Абстрактный класс блинов
public abstract class ABattercake {
protected abstract String getDesc();
protected abstract int cost();
}
Класс блинов, наследующий абстрактный класс блинов, блин стоит 8 юаней.
public class Battercake extends ABattercake {
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int cost() {
return 8;
}
}
Класс абстрактного оформления, следует отметить, что,Класс абстрактного оформления объединяет абстрактный класс блина через свойства-члены, а также наследует абстрактный класс блина., и здесь определяется новый бизнес-методdoSomething()
public abstract class AbstractDecorator extends ABattercake {
private ABattercake aBattercake;
public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
}
protected abstract void doSomething();
@Override
protected String getDesc() {
return this.aBattercake.getDesc();
}
@Override
protected int cost() {
return this.aBattercake.cost();
}
}
Декоратор яйца, наследующий абстрактный класс украшения, декоратор яйца добавляет яйцо в родительский класс, а цена добавляет 1 юань.
public class EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 1;
}
public void egg() {
System.out.println("增加了一个鸡蛋");
}
}
Декоратор колбасы, аналогичный декоратору яиц, наследует абстрактный класс декорирования, добавляет колбасу к родительскому классу и увеличивает цену на 2 доллара.
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一根香肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
тестить, покупать блины
1. Купите блин
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
вывод
煎饼, 销售价格: 8
2. Купите блинчик с яйцами
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
вывод
煎饼 加一个鸡蛋, 销售价格: 9
3. Купите блинчик с двумя яйцами
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
вывод
煎饼 加一个鸡蛋 加一个鸡蛋, 销售价格: 10
4. Купите блин с двумя яйцами и колбасой
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
вывод
煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠, 销售价格: 12
Нарисуйте диаграмму классов UML, как показано ниже.
Резюме
Поскольку и конкретный класс компонента, и класс оформления реализуют один и тот же абстрактный интерфейс компонента, шаблон оформления динамически налагает на объект дополнительные обязанности прозрачным для клиента образом.Другими словами, клиент не чувствует, что объект до декорирования и какая разница после декорирования.
Например, если мы добавим в блин яйцо, то это можно записать такaBattercake = new EggDecorator(aBattercake);
, клиент все еще может поставитьaBattercake
как оригиналaBattercake
то же самое, но теперьaBattercake
был украшен яйцами
Паттерн Decorator расширяет функциональность объекта, не создавая дополнительных подклассов.
Прозрачные декоративные узоры и полупрозрачные декоративные узоры
В приведенном выше примере декорированный объект представляет собой тип класса, созданный абстракцией.ABattercake
переменная для ссылки, в класс декоратора яиц мы добавили новыйegg()
метод, если в этот момент мы хотимвызовите только этот методнельзя назвать
если тип ссылочной переменной не изменен наEggDecorator
, так что вы можете позвонить
EggDecorator eggBattercake = new EggDecorator(aBattercake);
eggBattercake.egg();
В процессе фактического использования, поскольку новое поведение может потребоваться вызывать отдельно, эта форма режима оформления также часто появляется.Полупрозрачный режим украшения, в то время как стандартный шаблон декоратораРежим прозрачного оформления.
(1) Режим прозрачного оформления
В режиме прозрачного оформления клиент должен полностью запрограммировать абстракцию, а прозрачность режима оформления требует, чтобы клиентская программа не объявляла объекты как конкретные типы компонентов или конкретные типы оформления, но все они должны были быть объявлены как абстрактные типы компонентов. .
(2) Полупрозрачный режим украшения
Дизайн режима прозрачного оформления более сложен, и иногда нам нужно вызывать вновь добавленный бизнес-метод отдельно. Чтобы иметь возможность вызывать новый метод, мы должны использовать определенный тип оформления для определения декорируемого объекта, а конкретный тип компонента все еще может быть определен с использованием абстрактного типа компонента.Этот режим оформления является полупрозрачным режимом оформления.
Полупрозрачный режим оформления может сделать систему более гибкой, конструкция относительно проста и очень удобна в использовании, но его самым большим недостатком является то, чтоНевозможно реализовать несколько украшений для одного и того же объекта, и клиенту необходимо по-разному обращаться с объектом до оформления и с объектом после оформления.
Особенности декоративного режима
(1) Старайтесь, чтобы интерфейс декорированного класса оставался таким же, как и интерфейс декорированного класса, чтобы клиент мог последовательно обрабатывать как объект до декорирования, так и объект после декорирования. Это означает, что, где это возможно, мы должны попытаться использовать режим прозрачного оформления.
(2) Постарайтесь сохранить класс конкретного компонента как «легкий» класс, то есть не добавляйте слишком много вариантов поведения в класс конкретного компонента, мы можем расширить его, украсив класс.
(3) Если существует только один класс конкретных компонентов, абстрактный класс оформления может использоваться как прямой подкласс класса конкретных компонентов.
Типичное применение шаблона декоратора анализа исходного кода
Шаблон декоратора в Java I/O
При использовании ввода-вывода Java всегда существуют различные потоки ввода, потоки вывода, потоки символов, потоки байтов, потоки фильтров, потоки буферов и т. д., и если вы не знакомы с шаблонами проектирования внутри, вы всегда увидите, что это намного проще понять ввод-вывод Java с точки зрения шаблонов проектирования.
Давайте сначала воспользуемся рисунком, чтобы увидеть, что такое ввод-вывод Java.На следующем рисунке наглядно показана роль ввода-вывода Java.
Из приведенного выше рисунка видно, что в Java приложение считывает байты с адреса источника через метод Read входного потока (InputStream), а затем записывает поток в адрес назначения через метод Write выходного потока ( Выходной поток).
Существует три основных источника потоков: локальные файлы (File), консоли и сетевое взаимодействие через сокеты.
На следующем рисунке показаны класс декоратора и декорированный класс в Java, а также отношения между ними.Здесь перечислены только отношения в InputStream:
Как видно из рисунка выше, поскольку класс, наследующий FilterInputStream, является классом декоратора, его можно использовать для переноса других потоков.Класс декоратора также может переупаковывать декораторы и классы.
Вот краткое изложение нескольких сценариев применения часто используемых потоков.:
имя потока | Сценарии применения |
---|---|
ByteArrayInputStream | При обращении к массиву, используя в качестве InputStream буфер в памяти, ЦП считывает данные из буфера более чем в 10 раз быстрее, чем с носителя |
StringBufferInputStream | Принимает объект String как . Входной поток. Устарело, глючит при преобразовании символов |
FileInputStream | Доступ к файлам, использование файла в качестве InputStream для реализации операции чтения файла |
PipedInputStream | Каналы доступа, в основном используемые в потоках, один поток отправляет данные через выходной поток канала, а другой поток считывает данные через входной поток канала, что может реализовать связь между двумя потоками. |
SequenceInputStream | Объединяет несколько InputStreams в один InputStream Класс Sequence InputStream позволяет приложениям последовательно объединять несколько входных потоков. |
DataInputStream | Специальный поток, функция чтения различных базовых типов данных, таких как byte, int, String |
ObjectInputStream | Поток объектов, функция чтения объектов |
PushBackInputStream | Оттолкните входной поток, вы можете перемотать некоторые данные, прочитанные в буфер входного потока. |
BufferedInputStream | Буферизованные потоки, добавление возможностей буферизации |
Давайте рассмотрим пример обертывания потока в Java.:
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class StreamDemo {
public static void main(String[] args) throws IOException{
DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\hello.txt")));
while(in.available()!=0) {
System.out.print((char)in.readByte());
}
in.close();
}
}
выходной результат
hello world!
hello Java I/O!
В приведенной выше программе поток упаковывается дважды: сначала BufferedInputStream используется для упаковки FileInputStream в буферизованный поток, который должен добавить функцию буферизации к FileInputStream, а затем DataInputStream дополнительно упаковывается для облегчения обработки данных.
Если хотитеРеализовать собственный поток обертки, в соответствии с приведенной выше диаграммой классов вам необходимо наследовать абстрактный класс оформления FilterInputStream
Например, класс-декоратор, реализующий такую операцию: переводит все строчные буквы во входном потоке в прописные.
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class UpperCaseInputStream extends FilterInputStream {
protected UpperCaseInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toUpperCase(c));
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int result = super.read(b, off, len);
for (int i = off; i < off + result; i++) {
b[i] = (byte) Character.toUpperCase((char) b[i]);
}
return result;
}
public static void main(String[] args) throws IOException {
int c;
InputStream in = new UpperCaseInputStream(new FileInputStream("D:\\hello.txt"));
try {
while ((c = in.read()) >= 0) {
System.out.print((char) c);
}
} finally {
in.close();
}
}
}
вывод
HELLO WORLD!
HELLO JAVA I/O!
Вся система ввода-вывода Java основана на потоке символов (InputStream/OutputStream) и потоке байтов (Reader/Writer) в качестве базовых классов.Ниже приведена частичная диаграмма классов OutputStream, Reader и Writer.Для получения дополнительной информации см. другую информацию
Шаблон декоратора в весеннем кеше
Смотретьorg.springframework.cache.transaction
в упаковкеTransactionAwareCacheDecorator
этот класс
public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache;
public TransactionAwareCacheDecorator(Cache targetCache) {
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
public <T> T get(Object key, Class<T> type) {
return this.targetCache.get(key, type);
}
public void put(final Object key, final Object value) {
// 判断是否开启了事务
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 将操作注册到 afterCommit 阶段
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
public void afterCommit() {
TransactionAwareCacheDecorator.this.targetCache.put(key, value);
}
});
} else {
this.targetCache.put(key, value);
}
}
// ...省略...
}
Этот класс реализуетCache
интерфейс, в то время какCache
Объединение в класс становится свойством членаtargetCache
, так что вы можете сделать смелое предположениеTransactionAwareCacheDecorator
является классом оформления, но здесь нет абстрактного класса оформления, иTransactionAwareCacheDecorator
Без подклассов отношение класса декоратора здесь не так сложно, как отношение декоратора в вводе-выводе Java.
Основная функция этого класса: через SpringTransactionSynchronizationManager
положи этоput/evict/clear
Операции синхронизируются с транзакциями, управляемыми Spring, только наafter-commit
stage выполняет фактическое кэшированиеput/evict/clear
работать. Если ни одна транзакция неactive
, будет выполнено немедленноput/evict/clear
действовать
Шаблон декоратора в весенней сессии
Примечание: конец шаблона адаптера также может быть Wrapper.
своего родаServletRequestWrapper
Код выглядит следующим образом:
public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;
public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
this.request = request;
}
@Override
public Object getAttribute(String name) {
return this.request.getAttribute(name);
}
//...省略...
}
Вы можете видеть, что параServletRequest
Упаковано, вот выкройка декоратора, смотрите на рисунке ниже, весенняя сессияSessionRepositoryFilter
внутренний классSessionRepositoryRequestWrapper
иServletRequestWrapper
Отношение
видимыйServletRequestWrapper
это первый слой упаковки,HttpServletRequestWrapper
Упаковка через наследование добавляет функции, связанные с HTTP,SessionRepositoryRequestWrapper
Он упаковывается посредством наследования, и добавляются функции, связанные с сеансом.
Паттерн декоратора в кеше Mybatis
org.apache.ibatis.cache
Файловая структура пакета выглядит следующим образом
Мы можем судить о роли класса по имени пакета, в котором находится класс.Cache
это абстрактный класс компонентов,PerpetualCache
это определенный класс компонентов,decorators
Класс под пакетом является декоративным классом, абстрактного декоративного класса нет.
О функции, которую декорирует декорируемый класс, также можно судить по имени
Резюме шаблона декоратора
орнаментглавное преимуществоследующее:
- Для расширения функциональности объекта шаблон оформления является более гибким, чем наследование, и не приводит к резкому увеличению количества классов.
- Функцию объекта можно расширять динамически, а различные конкретные классы оформления можно выбирать во время выполнения через файл конфигурации для достижения различного поведения.
- Объект можно декорировать несколько раз.Используя различные специальные классы декорирования, а также расположение и комбинацию этих классов декорирования, можно создать множество комбинаций различных поведений для получения объектов с более мощными функциями.
- Конкретный класс компонента и конкретный класс украшения могут быть изменены независимо друг от друга. Пользователи могут добавлять новые определенные классы компонентов и определенные классы оформления в соответствии со своими потребностями. Исходный код библиотеки классов не нужно изменять, что соответствует «открытому и закрытому». принцип".
орнаментглавный недостатокследующее:
- При использовании паттерна декорирования для проектирования системы будет генерироваться множество мелких объектов.Разница между этими объектами заключается в том, как они связаны друг с другом, а не в их классах или значениях атрибутов.Генерация большого количества мелких объектов обязательно займет больше места Многие системные ресурсы влияют на производительность определенной программы.
- Режим декорирования обеспечивает более гибкое решение, чем наследование, но это также означает, что он более подвержен ошибкам, чем наследование, а также сложен в устранении неполадок.Для объектов, декорированных несколько раз, может потребоваться проверка на наличие ошибок во время отладка громоздка.
Применимая сцена:
- Добавляйте обязанности к отдельным объектам динамично и прозрачно, не затрагивая другие объекты.
- Режим украшения можно использовать, когда систему нельзя расширить путем наследования или когда наследование не способствует расширению и обслуживанию системы. Существует два основных типа ситуаций, в которых нельзя использовать наследование: первый заключается в том, что в системе имеется большое количество независимых расширений, и для поддержки каждого расширения или комбинации расширений будет генерироваться большое количество подклассов, что делает число количество подклассов стремительно растет, третий Второй класс связан с тем, что класс был определен и не может быть унаследован (например, последний класс в языке Java).
Ссылаться на:
Лю Вэй: Шаблоны проектирования Java Edition
MOOC Java Design Patterns Интенсивный метод отладки + анализ памяти
Фреймворк логирования Java: роль slf4j и принцип ее реализации
HankingHu: глубокое понимание общей инфраструктуры ввода-вывода Java с помощью шаблона декоратора.
HryReal: сценарий использования класса ввода-вывода Java
Рекомендуемое чтение
Шаблоны проектирования | Простые фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны фабричных методов и типичные приложения
Шаблоны проектирования | Абстрактные фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны построителей и типичные приложения
Шаблоны проектирования | Шаблоны прототипов и типовые приложения
Шаблоны проектирования | Шаблоны внешнего вида и типичные приложения
Больше материалов можно найти в моем личном блоге:laijianfeng.org