Декоратор (Decorator) представляет собой немного спорный паттерн. Его почти всегда можно заменить чем-нибудь другим. Например, Компоновщиком или Строителем.
Но все же Декоратор вполне может найти свое место в вашем проекте. Не зря его активно используют при разработке библиотек ввода-вывода. Особенно это заметно в Java.
В основе паттерна Декоратор лежит базовый интерфейс (или абстрактный класс). Для него создаются реализации, выполняющие полезные действия. Декоратор при этом выступает в качестве прозрачной обертки над реализациями, имея тот же самый интерфейс. Он делегирует все вызовы оборачиваемому объекту, но добавляет свои специфические преобразования.
рука мертвеца в покере что она значит.
Преимущество от использования Декоратора заключается в том, что он позволяет комбинировать цепочки обернутых объектов произвольной сложности. В результате удается избежать ненужного дублирования кода.
Рассмотрим пример. Создадим небольшое приложение, которое применяет графические фильтры к загруженному изображению. Фильтры могут накладываться, поэтому Декоратор позволит нам динамически создавать комбинированные фильтры, доступные для повторного использования.
Спроектируем простой интерфейсный класс ImageFilter
. Заголовочный файл imagefilter.h
:
#ifndef IMAGEFILTER_H
#define IMAGEFILTER_H
#include <QPixmap>
class ImageFilter {
public:
ImageFilter();
virtual ~ImageFilter();
virtual QPixmap apply( const QPixmap& pix ) = 0;
};
#endif // IMAGEFILTER_H
Он имеет всего одну чисто виртуальную функцию apply()
, которая принимает на вход QPixmap
, и возвращает результат применения фильтра.
Поскольку нам интересны как фильтры по отдельности, так и комбинации фильтров, то каждая реализация станет потенциальным Декоратором. Начнем с фильтра отзеркаливания.
В Qt довольно легко создать подобный эффект. Объявим класс MirroredImageFilter
, наследующий ImageFilter
, в mirroredimagefilter.h
:
#ifndef MIRROREDIMAGEFILTER_H
#define MIRROREDIMAGEFILTER_H
#include <imagefilter.h>
#include <memory>
class MirroredImageFilter : public ImageFilter {
public:
explicit MirroredImageFilter( const std::shared_ptr< ImageFilter >& filter = nullptr );
QPixmap apply( const QPixmap& pix );
private:
std::shared_ptr< ImageFilter > m_filter;
};
#endif // MIRROREDIMAGEFILTER_H
В качестве параметра конструктор принимает указатель на оборачиваемый фильтр. Если параметр равен nullptr
, то класс ничего не декорирует, и выступает в качестве самостоятельной реализации. А вот и она, в файле mirroredimagefilter.cpp
:
#include "mirroredimagefilter.h"
MirroredImageFilter::MirroredImageFilter( const std::shared_ptr< ImageFilter >& filter ) :
m_filter( filter ) {
}
QPixmap MirroredImageFilter::apply( const QPixmap& pix ) {
QPixmap pixResult = pix;
if( m_filter ) {
pixResult = m_filter->apply( pix );
}
return QPixmap::fromImage( pixResult.toImage().mirrored( true, false ) );
}
Один фильтр уже есть. Проверим его работу. Файл main.cpp
:
#include <QApplication>
#include <QFileDialog>
#include <QLabel>
#include "mirroredimagefilter.h"
int main( int argc, char** argv ) {
QApplication app( argc, argv );
QString fileName = QFileDialog::getOpenFileName(
nullptr,
"Load Image",
QString(),
"Images (*.jpg *.png *.bmp)"
);
QPixmap pix;
if( pix.load( fileName ) ) {
QLabel lblMirrored;
lblMirrored.setPixmap( MirroredImageFilter().apply( pix ) );
lblMirrored.show();
return app.exec();
}
return 0;
}
Эта программа уже что-то делает, но пока что Декоратора здесь еще нет. Он появится, когда мы добавим хотя бы еще один фильтр. Займемся этим прямо сейчас.
Пусть второй фильтр-Декоратор умеет осуществлять поворот изображения на указанный угол. Посмотрим на соответствующий заголовочный файл rotatedimagefilter.h
:
#ifndef ROTATEDIMAGEFILTER_H
#define ROTATEDIMAGEFILTER_H
#include "imagefilter.h"
#include <memory>
class RotatedImageFilter : public ImageFilter {
public:
RotatedImageFilter( double angle = 90.0, const std::shared_ptr< ImageFilter >& filter = nullptr );
QPixmap apply( const QPixmap& pix );
private:
double m_angle;
std::shared_ptr< ImageFilter > m_filter;
};
#endif // ROTATEDIMAGEFILTER_H
А вот и реализация в rotatedimagefilter.cpp
:
#include "rotatedimagefilter.h"
RotatedImageFilter::RotatedImageFilter( double angle, const std::shared_ptr< ImageFilter >& filter ) :
m_angle( angle ), m_filter( filter ) {
}
QPixmap RotatedImageFilter::apply( const QPixmap& pix ) {
QPixmap pixResult = pix;
if( m_filter ) {
pixResult = m_filter->apply( pix );
}
return pixResult.transformed( QTransform().rotate( m_angle ) );
}
Теперь у нас есть два фильтра. Каждый из них можно использовать отдельно, а можно скомбинировать в цепочку. Попробуем все это на практике (файл main.cpp
):
#include <QApplication>
#include <QFileDialog>
#include <QLabel>
#include "mirroredimagefilter.h"
#include "rotatedimagefilter.h"
int main( int argc, char** argv ) {
QApplication app( argc, argv );
QString fileName = QFileDialog::getOpenFileName(
nullptr,
"Load Image",
QString(),
"Images (*.jpg *.png *.bmp)"
);
QPixmap pix;
if( pix.load( fileName ) ) {
QLabel lblMirrored;
lblMirrored.setPixmap( MirroredImageFilter().apply( pix ) );
lblMirrored.show();
QLabel lblRotated;
lblRotated.setPixmap( RotatedImageFilter().apply( pix ) );
lblRotated.show();
QLabel lblCombined;
lblCombined.setPixmap(
RotatedImageFilter( 45.0, std::make_shared< MirroredImageFilter >() ).apply( pix )
);
lblCombined.show();
return app.exec();
}
return 0;
}
В результате запуска программы на экране появятся три QLabel
: с отзеркаленным изображением, с повернутым на 90 градусов изображением и с изображением, которое было сначала отзеркалено, а потом повернуто на 45 градусов:
Поскольку мы создали всего два фильтра, то существенную выгоду от использования паттерна Декоратор увидеть сложно. Но с ростом числа реализаций количество их возможных комбинаций будет возрастать очень быстро. В этом случае Декоратор в сочетании со Стратегией или каким-нибудь другим полиморфным подходом окажется весьма привлекательным решением.
Скачать исходники с примером использования паттерна Декоратор в Qt