Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Декоратор (Перевод с английского главы «Decorator» из книги «Pro Objective-C Design Patterns for iOS» Carlo Chung)

Anna | 2.07.2014 | нет комментариев

Обычно, делая фотографии, вы не задумываетесь, как оформите их потом. Вы фотографируете легко потому, что хотите поймать момент. Скажем, одну из фотографий вы после этого распечатали, потом решили разместить в рамку с странным стеклом. Но позднее вы могли бы разместить ту же фотографию в иную рамку, если бы захотели. Даже невзирая на то, что вы изменили рамку, картинка осталась той же, потому что вы легко что-то добавляли к ней, но не изменяли ее при этом.

В объектно-ориентированном программировании заимствовали схожую идею добавления поведения к иным объектам без потери их начальных особенностей, т. е. расширенный объект будет усовершенствованной версией того же самого класса (фото в рамке). Всякое «совершенствование» (рамка) может быть наложено и снято динамически. Мы называем данный паттерн проектирования Декоратором, так как декоратор может добавляться к иному декоратору либо начальному объекту для растяжения его свойств, оставив начальное поведение нетронутым.

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

Что такое паттерн Декоратор?

Типичный паттерн Декоратор содержит родительский отвлеченный класс Component, тот, что объявляет некоторые всеобщие операции для других определенных компонентов. Отвлеченный класс Component может быть обернут в иной класс DecoratorDecorator содержит ссылку на иной ComponentConcreteDecoratorопределяет некоторое расширенное поведение для других схожих Component-ов и Decorator-ов и исполняет операцию включенного в него объекта Component. Их отношения проиллюстрированы на рисунке 16-1.

image

Рисунок 16–1. Диаграмма классов паттерна Декоратор

Класс Component определяет некоторый отвлеченный способ operation, тот, что его определенные классы переопределяют, Дабы иметь свои личные специфические операции. Decorator – это базовый класс, тот, что определяет «декорирующее» поведение для растяжения других экземпляров Component (либо Decorator) за счет включения в объект Decorator. Его способ operation по умолчанию легко пересылает сообщение включенному componentConcreteDecoratorA и ConcreteDecoratorB переопределяют родительскийoperation, Дабы добавить их собственное поведение к вызову способа operation component-а через применение super. Если вам необходимо расширить component только один раз, тогда вы можете пренебречь базовым классом Decorator и дозволить ConcreteDecorator-ам перенаправлять всякие запросы напрямую кcomponent. Это схоже на образование цепочки операций с добавлением одного поведения поверх иного, как проиллюстрировано на диаграмме объектов на рисунке 16–2.

image

Рисунок 16–2. Реализация паттерна Декоратор и его функциональность

Примечание. Паттерн Декоратор: Добавляет добавочные вероятности к объекту динамически. Декораторы предоставляют эластичную альтернативу наследованию для растяжения функциональности.*

*Исходное определение, данное в книге «Паттерны проектирования» GoF (Addison Wesley, 1994).

Когда бы вы могли применять паттерн Декоратор?

Есть три распространенные обстановки, в которых вы могли бы разглядеть вероятность применения этого паттерна:

  • Вы хотите добавлять функциональность к отдельным объектам динамически и прозрачно без могущества на другие объекты.
  • Вы хотите расширить поведение класса, с которым это делать было бы непрактично. Определение класса может быть спрятано и недостижимо для наследования от него, либо расlqvmk!code>UIImage в UIKit из фреймворка Cocoa Touch представляет объекты изображения. Сам класс UIImage имеет достаточно ограниченный интерфейс для манипулирования изображением. Нет ничего, помимо нескольких свойств изображения, таких, как размер, цвет и т. д. Мы расширим обыкновенный объект изображения некоторыми вероятностями, доступными во фреймворке Quartz 2D. Есть два подхода для реализации паттерна — подклассы и категории.
    Реализация Декораторов через подклассы

    В случае подхода, основанного на подклассах, конструкция будет схожа на начальный жанр паттерна, показанный на рисунке 16–1. Исключительное отличие в том, что финальным типом элемента будет категория UIImage, а не его подкласс. Есть одна структурная задача, которая не дает нам разделять тот же интерфейс, тот, что реализует UIImageUIImage является прямым потомком NSObject и не больше. Это своего рода финальный класс. Дабы применять какое-то подобие интерфейса “Component” (как родительский интерфейс в диаграмме классов на рисунке 16–1), Дабы объединить и UIImage, и классы фильтров совместно, нам необходимо обходное решение. Теперь у нас есть две задачи:

    • Нам необходимо сделать наши классы фильтров такими же, как UImage, но у UIImage нет высокоуровневого интерфейса для совместного распределения (наследование от UIImage тут не предполагается).
    • У UIImage есть несколько способов, связанных с рисованием контента в нынешнем контексте, таких, как drawAsPatternInRect:drawAtPoint:drawAtPoint:blendMode:alpha:drawInRect: иdrawInRect:blendMode:alpha:. Если мы дозволим классам фильтров реализовывать те же способы, то усложним все и можем не получить того итога, тот, что хотим в соответствии с тем, как работает Quartz 2D. Мы возвратимся к этому позднее.

    Что мы собираемся сделать? Раньше каждого, безусловно, нам необходим интерфейс для распределенияUIImage с группой классов фильтров, Дабы данный паттерн работал и оба типа классов могли разделять один и тот же базовый тип. Идея применения UIImage в качестве высокоуровневого типа для этой цели (т. е. наследование от него) дрянна, потому что тогда фильтры будет трудно применять. Мы создаем интерфейс ImageComponent в виде протокола как наилучший базовый тип для всех. Но подождите минуту; разве мы не упомянули, что UIImage не наследует ни от какого интерфейса, а легко прямо наследует NSObject? Да, это так – вот где нам необходимо творческое решение. Мы сделаем категориюUIImage, которая реализует ImageComponent. Тогда компилятор будет знать, что UIImage иImageComponent — родственники, и не будет браниться. UIImage даже не осведомлен, что у него сейчас есть новейший базовый тип. Только тем, кто будет применять фильтры, необходимо знать об этом.

    Также мы не собираемся возиться с начальными draw* способами, определенными в UIImage, но как же мы тогда расширим вероятности рисования в ином ImageComponent? Мы скоро до этого дойдем.

    Диаграмма классов, которая показывает их связи, показана на рисунке 16–4.

    image

    Рисунок 16–4. Диаграмма классов разных фильтров изображений, которые реализуют паттерн Декоратор

    Протокол ImageComponent задает отвлеченный интерфейс со всеми способами draw* из класса UIImage. Всякий определенный ImageComponent и схожие декораторы обязаны уметь обрабатывать эти вызовы. Сообщение draw* для экземпляра UIImage дозволит ему рисовать его содержимое в нынешнем графическом контексте. Всякий из способов может создавать также трансформации и другие результаты в этом контексте. Таким образом, мы можем внедрить нашу собственную фильтрацию перед всякий операцией draw*.

    Наш определенный компонент тут – это тип UIImage, но мы не хотим создавать его подкласс легко, Дабы сделаут отсутствовать.

    Листинг 16–2 содержит объявление категории для UIImage, которое мы сумеем применять с другими декораторами позднее.

    Листинг 16–2. UIImage ImageComponent.h

    #import "ImageComponent.h"
    @interface UIImage (ImageComponent) <ImageComponent>
    @end
    

    Она (категория) реализует протокол ImageComponent без какой-либо реальной имплементации. Сейчас мы обратимся к нашему основному классу декоратора — ImageFilter. Его объявление показано в листинге 16–3.

    Листинг 16–3. ImageFilter.h

    #import "ImageComponent.h"
    #import "UIImage ImageComponent.h"
    @interface ImageFilter : NSObject <ImageComponent>
    {
        @private
        id <ImageComponent> component_;
    }
    @property (nonatomic, retain) id <ImageComponent> component;
    - (void) apply;
    - (id) initWithImageComponent:(id <ImageComponent>) component;
    - (id) forwardingTargetForSelector:(SEL)aSelector;
    @end
    

    Он содержит ссылку на ImageComponent в виде component_, которая может быть отдекорирована с поддержкой всяких других определенных декораторов. ImageFilter переопределяетforwardingTargetForSelector: и объявляет apply. Реализация класса показана в листинге 16–4.

    Листинг 16–4. ImageFilter.m

    #import "ImageFilter.h"
    @implementation ImageFilter
    @synthesize component=component_;
    - (id) initWithImageComponent:(id <ImageComponent>) component
    {
        if (self = [super init])
        {
            // сберегаем ImageComponent
            [self setComponent:component];
        }
        return self;
    }
    - (void) apply
    {
        // должен быть переопределен подклассами
        // для использования настоящих фильтров
    }
    - (id) forwardingTargetForSelector:(SEL)aSelector
    {
        NSString *selectorName = NSStringFromSelector(aSelector);
        if ([selectorName hasPrefix:@"draw"])
        {
            [self apply];
        }
        return component_;
    }
    @end
    

    Не так уж много пригодного делает способ initWithImageComponent:. Он легко присваивает ссылку наImageComponent из параметра способа себе. Также его способ apply не делает в данном случае ничего, пока мы не увидим его вновь в определенных классах фильтров.

    Что тут увлекательно, так это то, что мы используем forwardingTargetForSelector: для перехвата вызовов сообщений, которые экземпляр ImageFilter не знает, как обработать. Данный способ разрешает подклассам передать рантайму иного получателя сообщения, так что начальное сообщение будет переадресовано. Но мы заинтересованы только во каждому, что имеет префикс @“draw”, и после этого мы переадресовываем все остальное прямо к component_, возвращая его среде исполнения. Скажем, когда сообщение drawAtRect: посылается экземпляру ImageFilter, оно будет перехвачено в способе forwardingTargetForSelector:, ждя альтернативного получателя, потому что у ImageFilterнет никакой реализации для этого. Так как сообщение содержит префикс “draw”, способ посылает сообщение apply, Дабы сделать что-то перед тем, как component_ обработает сообщение позднее.

    Сейчас мы теснее можем получить несколько реальных фильтров. 1-й, тот, что мы собираемся сделать, — ImageTransformFilter, как показано в листинге 16–5.

    Листинг 16–5. ImageTransformFilter.h

    #import "ImageFilter.h"
    @interface ImageTransformFilter : ImageFilter
    {
        @private
        CGAffineTransform transform_;
    }
    @property (nonatomic, assign) CGAffineTransform transform;
    - (id) initWithImageComponent:(id <ImageComponent>)component
    transform:(CGAffineTransform)transform;
    - (void) apply;
    @end
    

    ImageTransformFilter является подклассом ImageFilter и переопределяет способ apply. Он также объявляет закрытую переменную transform_ типа CGAffineTransform с ассоциированным с ней свойством для доступа к ней. Так как CGAffineTransform – это конструкция C, качество должно быть присваемого типа, так как его значению невозможно вызвать retain, как иным объектам Objective-C. У фильтра есть свой способ инициализации. Способ initWithImageComponent:(id <ImageComponent>)component transform: (CGAffineTransform)tranform принимает экземплярImageComponent и значение CGAffineTransform во время инициализации. component будет передан способу initWithComponent: с поддержкой super, а transform будет присвоено закрытой переменной, как показано в листинге 16–6.

    Листинг 16–6. ImageTransformFilter.m

    @implementation ImageTransformFilter
    @synthesize transform=transform_;
    - (id) initWithImageComponent:(id <ImageComponent>)component
    transform:(CGAffineTransform)transform
    {
        if (self = [super initWithImageComponent:component])
        {
            [self setTransform:transform];
        }
        return self;
    }
    - (void) apply
    {
        CGContextRef context = UIGraphicsGetCurrentContext();
        // устанавливаем трансформацию
        CGContextConcatCTM(context, transform_);
    }
    @end
    

    В способе apply мы получаем ссылку CGContextRef от функции Quartz 2DUIGraphicsGetCurrentContext(). Мы не будем вдаваться в детали Quartz 2D тут. Так как ссылка на валидный нынешний контекст получена, мы передаем значение transform_ в CGContextConcatCTM() для добавления его в контекст. Что бы ни было потом нарисовано в контексте, оно будет оттрансформировано с поддержкой переданного значения CGAffineTransform. Фильтр аффинных реформирований сейчас завершен.

    Как и ImageTransformFilterImageShadowFilter также является прямым подклассом классаImageFilter и переопределяет только способ apply. В способе, показанном в листинге 16–7, мы получаем графический контекст, после этого вызываем функцию Quartz 2D CGContextSetShadow(), Дабы добавить тень. Остальное дюже схоже на то, что делает способ ImageTransformFilter. Все, что рисуется в контексте, сейчас будет иметь результат тени, как показано на правом изображении на рисунке 16–6.

    Листинг 16–7. ImageShadowFilter.m

    #import "ImageShadowFilter.h"
    @implementation ImageShadowFilter
    - (void) apply
    {
        CGContextRef context = UIGraphicsGetCurrentContext();
        // создаем тень
        CGSize offset = CGSizeMake (-25, 15);
        CGContextSetShadow(context, offset, 20.0);
    }
    @end
    

    Сейчас мы наполнили оглавлением все фильтры и готовы заняться клиентским кодом. В плане-примере к этой главе есть класс DecoratorViewController, тот, что будет накладывать все эти фильтры в способеviewDidLoad, как показано в листинге 16–8.

    Листинг 16–8. Способ viewDidLoad в DecoratorViewController.m

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // загружаем начальное изображение
        UIImage *image = [UIImage imageNamed:@"Image.png"];
        // создаем трансформацию
        CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
        CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(
        -image.size.width / 2.0,
        image.size.height / 8.0);
        CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform,
        translateTransform);
        // подход, учрежденный на подклассах
        id <ImageComponent> transformedImage = [[[ImageTransformFilter alloc]
        initWithImageComponent:image
        transform:finalTransform]
        autorelease];
        id <ImageComponent> finalImage = [[[ImageShadowFilter alloc]
        initWithImageComponent:transformedImage]
        autorelease];
        // создаем новейший DecoratorView
        // с отфильтрованным изображением
        DecoratorView *decoratorView = [[[DecoratorView alloc]
        initWithFrame:[self.view bounds]]
        autorelease];
        [decoratorView setImage:finalImage];
        [self.view addSubview:decoratorView];
    }
    

    Вначале мы создаем ссылку на начальное изображение бабочки, показанное с левой стороны рисунка 16–6. После этого конструируем CGAffineTransform для поворота и переноса изображения соответственно. И изображение, и трансформация применяются для инициализации экземпляраImageTransformFilter как первого фильтра. После этого мы используем каждый компонент, Дабы сконструировать экземпляр ImageShadowFilter, Дабы наложить тень на объект, полученный отImageTransformFilter. На этом шаге finalImage – это итоговый объект, тот, что содержитImageTransformFilterImageShadowFilter и начальное изображение. После этого мы присваиваем результирующий объект экземпляру DecoratorView перед тем, как добавить его к контроллеру в качестве subviewDecoratorView занимается тем, что рисует изображение в своем способеdrawRect:rect, как показано в листинге 16–9.

    Листинг 16–9. Способ drawRect:rect в DecoratorView.m

    - (void)drawRect:(CGRect)rect
    {
        // Код рисования.
        [image_ drawInRect:rect];
    }
    

    DecoratorView хранит ссылку на итоговое изображение UIImage в качестве image_. СпособdrawRect:rect перенаправляет сообщение drawInRect:rect объекту image_ с параметром rect. После этого отсель пойдет каждая цепь операций по декорированию. ImageShadowFilter перехватит сообщение первым. Позже того, как он добавит тень в нынешний графический контекст и вернетcomponent_ из способа forwardingTargetForSelector:, то же самое сообщение будет направлено возвращенному component_ на дальнейшем шаге. На этом шаге component_ — это реально экземплярImageTransformFilter, когда мы конструировали цепочку на предыдущих шагах. Он также перехватывает способ forwardingTargetForSelector: и настраивает нынешний контекст с поддержкой предопределенного значения CGAffineTransform. После этого он вновь возвращает component_ так же, как и ImageShadowFilter. Но на данный раз это начальное изображение бабочки, следственно когда оно возвращается из ImageTransformFilter и получает сообщение на последнем шаге, его нарисуют в нынешнем контексте, рассматривая теснее все его тени и аффинные реформирования. Это каждая последовательность операций, позже которой мы получим преобразованное и затененное изображение, как показано на рисунке 16–6.

    Примечание: фильтры могут быть объединены в различной последовательности.

    Мы можем сделать то же самое, применяя категории, но с учетом их некоторых характерных особенностей. Дальнейший раздел покажет, как это сделать.

    Реализация Декораторов с поддержкой категорий

    В подходе, использующем категории, нам необходимо только добавить фильтры к классу UIImage в виде категорий, и они будут трудиться прямо как отдельные классы UIImage, но при этом ими не являясь. В этом и заключается красота категорий в Objective-C. Мы собираемся добавить два фильтра, один — для использования 2D-трансформации к изображению, иной – для отбрасывания тени. Диаграмма классов, которая иллюстрирует эти отношения, показана на рисунке 16–7.

    image

    Рисунок 16–7. Диаграмма классов разных фильтров изображений, представленных категориями класса UIImage

    Как и в предыдущем разделе, мы реализуем фильтры реформирования и тени. У нас есть три категории для этого подхода — UIImage (BaseFilter)UIImage (Transform) и UIImage (Shadow). С этого момента будем их называть BaseFilterTransform и Shadow соответственно. BaseFilter определяет некоторые рутинные 2D операции рисования, которые необходимы для отрисовки в нынешнем графическом контексте, схожие на способы класса ImageFilter в предыдущем разделе. Другие категории фильтров могут применять тот же способ для рисования содержащегося в них изображения. И Transform, иShadow не наследуют от BaseFilter, но они того же происхождения, так как все эти классы – категорииUIImage. Способы, определенные в BaseFilter, могут быть использованы и в категориях Transform иShadow, как и в предыдущем подходе, но без наследования. Категория Transform определяет способimageWithTransform:transform, тот, что принимает ссылку на трансформацию (детали увидим немножко позднее), применяет ее к изображению, разрешает ему нарисовать себя, а после этого возвращает оттрансформированное изображение. Категория Shadow определяет способimageWithDropShadow, тот, что отбрасывает тень на внутренний объект изображения и возвращает результирующее изображение с теснее примененным результатом. Допустимо, вы теснее подметили, что категории могут быть сцеплены совместно так же, как и подклассы, описанные в предыдущем разделе. Диаграмма классов, которая иллюстрирует это, показана на рисунке 16–8.

    image

    Рисунок 16–8. Диаграмма объектов показывает, как разные категории-фильтры ссылаются на другие экземпляры UIImage во время выполнения

    Правый конец цепочки – это начальное изображение, как то, что слева на рисунке 16–6. Позже добавления начального изображения к фильтру Shadow и после этого к фильтру Transform, заказчик, тот, что ссылается на объект изображения, получит что-то как бы правой картинки 16–6. Конструкция цепочки дюже схожа на версию с подклассами, за исключением того, что всякая категория используетself в качестве ссылки на нижележащее изображение взамен отдельной ссылки подобно component_.

    Давайте взглянем на код. Вначале мы определим категорию BaseFilter, которая содержит способы, которые могут применяться по умолчанию в других определенных фильтрах, как проиллюстрировано в листинге 16–10.

    Листинг 16–10. UIImage BaseFilter.h

    @interface UIImage (BaseFilter)
    - (CGContextRef) beginContext;
    - (UIImage *) getImageFromCurrentImageContext;
    - (void) endContext;
    @end
    

    BaseFilter содержит три способа, которые помогают рисовать себя в нынешнем контексте, листинг 16–11.

    Листинг 16–11. UIImage BaseFilter.m

    #import "UIImage BaseFilter.h"
    @implementation UIImage (BaseFilter)
    - (CGContextRef) beginContext
    {
        // Создаем графический контекст с целевым размером
        // На iOS 4 и выше применяется UIGraphicsBeginImageContextWithOptions
        // для учета масштаба
        // На больше раннем iOS используйте UIGraphicsBeginImageContext
        CGSize size = [self size];
        if (NULL != UIGraphicsBeginImageContextWithOptions)
            UIGraphicsBeginImageContextWithOptions(size, NO, 0);
        else
        UIGraphicsBeginImageContext(size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        return context;
    }
    - (UIImage *) getImageFromCurrentImageContext
    {
        [self drawAtPoint:CGPointZero];
        // Получаем UIImage из нынешнего контекста
        UIImage *imageOut = UIGraphicsGetImageFromCurrentImageContext();
        return imageOut;
    }
    - (void) endContext
    {
        UIGraphicsEndImageContext();
    }
    @end
    

    beginContext – это примерно то же самое, что в версии с подклассами. Все нужное для рисования происходит тут. Когда контекст готов для рисования, способ вернет его по запросу.

    getImageFromCurrentImageContext рисует в нынешнем контексте и возвращает изображение из него с поддержкой вызова UIGraphicsGetImageFromCurrentImageContext().

    После этого сообщение endContext завершает процесс вызовом функции Quartz 2DUIGraphicsEndImageContext() для чистки контекстно-зависимых источников.

    Сейчас мы можем приступить к категориям наших фильтров. Первая, на которую мы посмотрим, — это категория Transform. В Transform есть только один способ, тот, что принимает конструкциюCGAffineTransform и применяет ее к изображению. Ее объявление представлено в листинге 16–12.

    Листинг 16–12. UIImage Transform.h

    @interface UIImage (Transform)
    - (UIImage *) imageWithTransform:(CGAffineTransform)transform;
    @end
    

    Ее реализация дюже откровенна, как показано в листинге 16–13.

    Листинг 16–13. UIImage Transform.m

    #import "UIImage Transform.h"
    #import "UIImage BaseFilter.h"
    @implementation UIImage (Transform)
    - (UIImage *) imageWithTransform:(CGAffineTransform)transform
    {
        CGContextRef context = [self beginContext];
        // установка трансформации
        CGContextConcatCTM(context, transform);
        // Рисуем начальное изображение в контексте
        UIImage *imageOut = [self getImageFromCurrentImageContext];
        [self endContext];
        return imageOut;
    }
    @end
    

    Способ принимает конструкцию CGAffineTransform, которая содержит информацию об аффинной матрице реформирования. Способ передает transform и контекст в функцию Quartz 2D,CGContextConcatCTM(context, transform). После этого transform добавляется к нынешнему контексту рисования. Сейчас способ посылает self сообщение getImageFromCurrentImageContext, которое определено в категории BaseFilter для рисования на экране. Позже того, как экземпляр UIImageвозвращается из вызова,посылается сообщение endContext для закрытия нынешнего контекста рисования и, в конце концов, возвращается изображение.

    Мы разобрались с нашим фильтром Transform. Это легко, не так ли? Сейчас мы можем определить фильтр Shadow так же легко, как и предшествующий, как показано в листинге 16–14.

    Листинг 16–14. UIImage Shadow.h

    @interface UIImage (Shadow)
    - (UIImage *) imageWithDropShadow;
    @end
    

    Так же, как и фильтр TransformShadow – это категория класса UIImage, имеющая один способ. Способ не принимает параметров, но он содержит на пару шагов огромнее, чем реализация класса Transform. Мы можем увидеть, как отбрасывать тень на изображение в листинге 16–15.

    Листинг 16–15. UIImage Shadow.m

    #import "UIImage Shadow.h"
    #import "UIImage BaseFilter.h"
    @implementation UIImage (Shadow)
    - (UIImage *) imageWithDropShadow
    {
        CGContextRef context = [self beginContext];
        // установка тени
        CGSize offset = CGSizeMake (-25, 15);
        CGContextSetShadow(context, offset, 20.0);
        // Итог начального изображения в контекст
        UIImage * imageOut = [self getImageFromCurrentImageContext];
        [self endContext];
        return imageOut;
    }
    @end
    

    Вначале мы создаем некоторые признаки тени, которые нам необходимы, с поддержкой вызова функции Quartz 2D, CGSizeMake (-25, 15), где два параметра представляют смещения по направлениям X и Y. После этого мы передаем графический контекст в CGContextSetShadow(context, offset, 20.0), иную функцию Quartz 2D, с параметром 20.0, тот, что указывает фактор размытия. В результате, как и в способе addTranform: категории Transform, изображение отображается на экране, объект UIImage извлекается и возвращается.

    К настоящему моменту у нас есть все нужное для фильтрации изображений UIImage. Как мы будем это применять? В том же самом способе viewDidLoad класса DecoratorViewController, как показано в листинге 16–16.

    Листинг 16–16. Способ viewDidLoad в файле DecoratorViewController.m

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // загружаем начальное изображение
        UIImage *image = [UIImage imageNamed:@"Image.png"];
        // создаем трансформацию
        CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(-M_PI / 4.0);
        CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(
        -image.size.width / 2.0,
        image.size.height / 8.0);
        CGAffineTransform finalTransform = CGAffineTransformConcat(rotateTransform,
        translateTransform);
        // подход, использующий категории
        // добавляем трансформацию
        UIImage *transformedImage = [image imageWithTransform:finalTransform];
        // добавляем тень
        id <ImageComponent> finalImage = [transformedImage imageWithDropShadow];
        // создаем новейший image view
        // с отфильтрованным изображением
        DecoratorView *decoratorView = [[[DecoratorView alloc]
        initWithFrame:[self.view bounds]]
        autorelease];
        [decoratorView setImage:finalImage];
        [self.view addSubview:decoratorView];
    }
    

    Начальная ссылка на изображение, создание трансформации и другое такое же, как и в листинге 16–8 версии с подклассами. Отличия в том, что imageWithTransform:, тот, что применяет трансформацию к изображению, исполняется самим объектом изображения и возвращает преобразованное изображение (начальное остается нетронутым). После этого преобразованное изображение исполняет вызов способаimageWithDropShadow, Дабы отбросить тень на себя, а после этого возвращает свою затененную версию как новое изображение под именем finalImagefinalImage будет добавлено на imageView и отображено на экране, как в подходе с подклассами. Однострочная версия вызовов может быть такой:

    finalImage = [[image imageWithTransform:finalTransform] imageWithDropShadow];
    

    Теперь вы можете сказать, в чем разница между подходом, использующим категории, и использующим подклассы? Верно – фильтры UIImage в случае категорий – это способы экземпляра, а не настоящие подклассы, как во втором случае. Нет никакого наследования в подходе, основанном на категориях, так как все фильтры – это часть UIImage! Мы используем ImageComponent в качестве абстрактного типа в подклассовом подходе в то время, как можем применять UIImage на каждому пути во втором подходе. Впрочем в обоих случаях фильтры дозволено накладывать в различной последовательности.

    Дизайн категорий кажется проще для реализации фильтрации. Он проще, так как мы не применяли наследование и инкапсуляцию иного объекта UIImage для растяжения цепочки декораторов. В дальнейшем разделе мы побеседуем о некоторых плюсах и минусах применения категорий для адаптации паттерна.

    Категории Objective-C и паттерн Декоратор

    Категория – это фича языка Objective-C, которая разрешает добавлять поведение (интерфейс способа и реализацию) к классу без наследования. Способы категории не оказывают никакого неблагополучного могущества на начальные способы класса. Способы категории становятся частью класса и могут наследоваться подклассами.

    Как мы видели в предыдущем примере, дозволено применять категории для реализации паттерна Декоратор. Впрочем это не суровая его адаптация; это соответствует плану паттерна, но только как вариант. Поведение, добавляемое категориями Декоратора, — это артефакт времени компиляции, чай Objective-C поддерживает динамическое связывание (какая реализация способа должна быть использована) в силу нрава самого языка. Также категории Декоратора на самом деле не инкапсулируют экземпляр расширяемого класса.

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

    Завершение

    Мы представили паттерн Декоратор с его доктринами и разными подходами в реализации на Objective-C. Реализация с применением подклассов использует больше структурированный подход для соединения разных декораторов. Подход, учрежденный на категориях, проще и больше легок, чем его соперник. Он подходит для приложений, в которых требуется малое число декораторов для существующих классов. Правда категории отличаются от подклассов и не могут верно адаптировать начальный паттерн, они решают те же задачи. Паттерн Декоратор – это обычный выбор для проектирования приложений как бы примера фильтрации изображений. Любая комбинация фильтров изображений может быть применена либо удалена динамически без могущества на целостность начального поведения UIImage.

    В дальнейшей главе мы увидим паттерн, схожий на Декоратор, но служащий иным целям. Он именуется Цепочка Обязанностей.

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB