Видео курс Шаблоны проектирования

Основные темы, рассматриваемые на уроке:
01:52 1 Метафора
06:36 2 Программный код метафоры
09:27 3 Структура паттерна на языке UML
09:55 4 Структура паттерна на языке C#
10:27 5 Диаграмма объектов
11:25 6 Применимость Паттерна (Разновидности Proxy)
20:02 7 Назначение паттерна
20:44 8 Использование паттерна

Видео урок посвящен шаблону проектирования Proxy, который предоставляет объект-заместитель для контроля доступа к другому объекту.

Для просмотра полной версии видеокурса и получения доступа к дополнительным учебным материалам Вам необходимо оформить подписку
Оформить подписку

Паттерн Proxy

Название

Заместитель

Также известен как

Surrogate (Суррогат)

Классификация

По цели: структурный

По применимости: к объектам

Частота использования

Выше средней    -   1 2 3 4 5

Назначение

Паттерн Proxy - предоставляет объект-заместитель для контроля доступа к другому объекту.

Введение

Как видно из альтернативного названия паттерна Proxy, его еще называют Surrogate (Суррогат).  А что такое суррогат в житейском смысле?

Суррогат - (от лат. surrogatus - поставленный взамен «вместо» другого) это предмет или продукт, заменяющий собой какой-либо другой предмет или продукт, с которым он (суррогат) имеет лишь некоторые общие свойства, но суррогат не обладает всеми качествами оригинального предмета или продукта, например, суррогатом зерен кофе - являются ячмень или желуди, суррогатом сахара – является сахарин. Также суррогатом часто называют различного рода подделки (фальсифицированные продукты), например, подделанный виски или водку, кроссовки abibas или батарейки Panosanic и др.

Но для формирования метафоры, которая позволила бы быстро и афористически ярко описать принципы работы паттерна Proxy, традиционно-житейского понимания суррогата на достаточно. К реализации паттерна Proxy выдвигается ряд важных требований, а именно то, что «оригинальный (настоящий) объект» и его «суррогат» должны взаимодействовать друг с другом, а также должна быть возможность, чтобы оригинальный объект всегда мог собой заменить суррогат в месте его использования, соответственно интерфейсы взаимодействия оригинального объекта и его суррогата должны совпадать.  В объективной реальности трудно найти такие случаи совместной работы оригинального объекта и его суррогата, чтобы удовлетворить всем требованиям паттерна Proxy. Поэтому, чтобы критики не говорили, что данная работа не указывает на то, что ее авторы интересовались когда-нибудь современным кинематографом, обратимся к сюжету фильма «Суррогаты» с Брюсом Уиллисом в главной роли для формирования удовлетворительной метафоры.

Действие разворачивается в 2057 году. Некий ученый Лайонел Кэнтop разработал нейрокомпьютерный интерфейс (систему для обмена информацией между мозгом человека и электронным устройством, например, компьютером), который позволяет человеку, силой нервных импульсов управлять роботом андроидом. При этом робот андроид внешне похож на человека оператора и ведёт себя полностью как человек. Настоящий же человек-оператор находящийся удаленно от своего робота суррогата — чувствует и переживает все ощущения, которые «воспринимает» его электронный двойник-суррогат через систему встроенных датчиков. В результате такого изобретения мир преобразился. Живые люди не выходят из своих домов, а вместо них по улицам ходят роботы-суррогаты. Главный герой фильма — агент ФБР Том Грир роль которого играет Брюс Уиллис, также, пользуется суррогатом, напоминающим по внешнему виду владельца в его молодые годы.

Мы не будем раскрывать весь сюжет кинофильма «Суррогаты», чтобы не испортить удовольствие от просмотра тем, кто намеревается посмотреть этот фильм. Рассмотрим еще только один интересующий нас момент из этого фильма.

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

Обратите внимание на связку «человек-оператор» и его «робот-суррогат» в кинофильме «Суррогаты». Суррогат в двустороннем порядке обменивается сигналами со своим оператором (принимая и передавая сигналы), при этом сам оператор оказывается защищен от агрессивной среды обитания, но в случае острой необходимости, оператор может занять место своего суррогата, что говорит о совпадении интерфейсов взаимодействия человека-оператора и робота-суррогата.

Из сказанного выше легко сформировать модель и реализовать ее программно.

См. пример к главе: \012_Proxy\004_Surrogates

Структура паттерна на языке UML

См. пример к главе: \012_Proxy\001_Proxy

Структура паттерна на языке C#

Так может выглядеть диаграмма объектов при использовании паттерна Proxy:

См. пример к главе: \012_Proxy\001_Proxy

Участники

  • Proxy - Заместитель:

Представляет собой класс объекта-заместителя. Объект заместитель хранит в себе ссылку на реальный субъект, что позволяет заместителю обращаться к реальному субъекту напрямую. Заместитель имеет такой же интерфейс, как и реальный субъект, что позволяет в нужный момент подставлять заместителя вместо реального субъекта и наоборот.  Заместитель контролирует доступ к реальному субъекту и может отвечать за создание экземпляра реального субъекта если это может оказаться необходимым. Прочие обязанности заместителя зависят от вида заместителя. Заместители бывают четырех видов: удаленный заместитель (посол), виртуальный заместитель, защищающий заместитель и заместитель – умная ссылка.

  • Subject - Субъект:

Предоставляет общий интерфейс для Proxy и RealSubject. Proxy возможно использовать везде где ожидается использование RealSubject.

  • RealSubject - Реальный субъект:

Представляет собой класс объекта, для которого требуется создание заместителя.

Отношения между участниками

Отношения между классами

  • Класс RealSubject связан связью отношения наследования с абстрактным классом Subject.
  • Класс Proxy связан связью отношения наследования с абстрактным классом Subject и связью отношения ассоциации с классом RealSubject.

Отношения между объектами

  • Объект-заместитель класса Proxy, переадресует клиентские запросы целевому объекту класса RealSubject.

Мотивация

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

Вместо реального изображения, можно временно использовать его упрощенный суррогат (заместитель). В роли заместителя можно использовать изображение с более низким разрешением (preview или thumbnail), которое быстрее отобразится на странице в момент, когда изображение станет видимым пользователю.

Например, при использовании EBookDroid (программа просмотра документов и электронных книг) можно увидеть эффект задержки полного отображения новой страницы. Такая задержка может быть связана с аппаратной составляющей (слабый процессор, мало памяти).

На рисунке видно, что, в момент «перелистывания» страниц, на появившейся странице (снизу) контент отображается с более низким разрешением чем на странице (сверху).

У разработчиков приложения может быть несколько вариантов организации отображения новой страницы: Первый вариант - отобразить пустую страницу и ожидать пока загрузится контент. Второй вариант – отображать информацию на странице по мере загрузки, что тоже заставляет ждать пользователя. Третий вариант – показать пользователю страницу в более низком качестве, при этом пользователь по очертаниям и силуэтам может принять решение – остаться на данной странице и ожидать ее полной загрузки или продолжить «листать» книгу.

Применимость паттерна

Паттерн Proxy рекомендуется использовать, когда требуется воспользоваться целевым объектом не напрямую, а через объект-заместитель.

Объект-заместитель имеет четыре технологические разновидности:

  • Удаленный заместитель («Посол» или «Ambassador»).

Удаленный объект-Proxy – это объект, который находится в другом адресном пространстве относительно целевого объекта и обеспечивает доступ к целевому объекту. Например, при использовании технологии WCF создается объект прокси представляющий собой обертку для сервиса-потребителя (consumer), который связывается с сервисом-поставщиком (provider).

См. пример к главе: \012_Proxy\005_Ambassador

  • Виртуальный заместитель.

Виртуальный заместитель, это объект, который создает «тяжелые» объекты по требованию. Как бывает в жизни. Например, человек хочет приобрести автомобиль. Прежде чем его купить, клиент обычно пользуется объектом-заместителем этого автомобиля, а именно буклетом который описывает автомобиль. И только после того как клиент убедится, что это именно то что ему требуется, едет в автосалон на «test-drive». Пример и техническое описание виртуального заместителя представлены в разделе «Мотивация». 

См. пример к главе: \012_Proxy\002_ImageProxy

  • Защищающий заместитель.

Защищающий заместитель, контролирует доступ к своему целевому объекту. Такие заместители используются, когда требуется установить различные права доступа к целевому объекту. Например, при реализации подхода CRUD популяризированного Джеймсом Мартином. CRUD – (Create, Read, Update, Delete – Создание, Чтение, Обновление, Удаление).

См. пример к главе: \012_Proxy\ 003_ProxyCRUD

  • Заместитель «Умная ссылка».

Объект «умная ссылка» - представляет собой объектно-ориентированное представление обычного указателя (адреса переменной или метода в памяти). В качестве примера умной ссылки в языке C# можно привести такие синтаксические конструкции как делегат (delegate), критическая секция (lock), оператор автоматической генерации программного кода итератора (yield), переменная запроса LINQ, операторы автоматической генерации программного кода асинхронного выполнения метода (async и await). Предлагается рассмотреть каждую из упомянутых конструкций, представляющую собой неявное выражение паттерна Proxy вида «умная ссылка».

Делегат (delegate) как «умная ссылка», представляет собой функционально-ориентированную сущность в объектно-ориентированном представлении, предназначенную для хранения и передачи указателя (адреса первого байта тела метода в памяти) на метод. Умность делегата заключается в том, что программисту не требуется напрямую работать с числовым представлением адреса первого байта тела метода, а также в наличии дополнительной функциональности которая обслуживает хранящийся в делегате указатель. Но не принято «языком ООП» говорить, что мы вызываем метод по указателю содержащемся в делегате, правильно сказать, что мы вызываем метод сообщенный с делегатом. Вызывая метод Invoke, говорят: - «Мы вызываем метод сообщенный с делегатом.» (подразумевая синхронный вызов метода), а вызывая метод BeginInvoke говорят: - «Мы асинхронно вызываем метод сообщенный с делегатом.».

Критическая секция (lock) как «умная ссылка», представляет собой безопасную конструкцию по работе с объектом синхронизации доступа к разделяемому ресурсу, между несколькими потоками (нитями). Умность конструкции lock заключается в том, что она избавляет программиста использовать напрямую вызовы методов Monitor.Enter и Monitor.Exit, использование которых напрямую зачастую приводит к трудно обнаруживаемым ошибкам.

Оператор автоматической генерации программного кода итератора (yield) как «умная ссылка», представляет собой быстрый и безопасный способ получения объекта-итератора. Умность оператора yield заключается в том, что программисту не требуется утруждать себя пониманием устройства сложного итератора, например, создание внутреннего (пассивного) итератора вручную, не всегда оказывается легкой задачей.

Переменная запроса LINQ, как «умная ссылка», представляет собой переменную содержащую ссылку на коллекцию, которую возвращает связанное с ней (ней - переменной запроса) выражение запроса LINQ. Умность переменной запроса LINQ заключается в том, что она принимает в себя ссылку на коллекцию (другими словами инициирует выполнение выражения запроса) только тогда, когда эту переменную начинают использовать фактически, если в процессе выполнения программного модуля к переменной запроса не будет произведено обращение, то и выражение запроса не будет выполняться исполняющей средой. Понятно, что любое выражение запроса LINQ трансформируется в последовательность вызовов расширяющих методов, которые могут формировать определенную коллекцию, как результат своего выполнения, что не всегда может положительно сказаться на производительности в случае хаотичного или одновременного выполнения всех имеющихся выражений запроса в программе.

Связка операторов автоматической генерации программного кода, обслуживающего асинхронное выполнение метода (async и await) как «умная ссылка», представляют собой генератор машины состояния для асинхронного выполнения. Умность связки async и await заключается в том, что большинство рутинных операций по организации асинхронной обработки эта связка берет на себя. Например, организация «асинхронной машины состояний» (конечного автомата, обслуживающего асинхронные вызовы – реализация методов MoveNext и SetStateMachine интерфейса IAsyncStateMachine), организация цепочек «продолжений» асинхронного выполнения нескольких операций и др.

Результаты

Паттерн Proxy позволяет дополнительно защитить (скрыть) субъект при попытке доступа к нему, добавляя уровень косвенности во взаимоотношений объектов. В зависимости от варианта применения паттерна можно выделить следующие полезные особенности:

  • удаленный заместить может скрывать реальное расположение субъекта от клиента (в адресном пространстве диска или в памяти).
  • виртуальный заместитель может оптимизировать ресурсы, создавая субъекты по требованию, реализуя технику отложенной (или ленивой) инициализации.
  • защищающий заместитель и «умная» ссылка позволяют проводить дополнительные действия (например, верификацию, валидацию, кеширование и т.п.) при попытке доступа к субъекту.

Дополнительно паттерн Proxy может скрывать от клиента возможность копирования при записи (copy-on-write). Эта оптимизация очень похожа на технику создания объектов по требованию, она позволяет экономить ресурсы при копировании больших и сложных субъектов. Если проводить действия с суррогатом субъекта вместо оригинала (может требовать значительно меньше вычислительных ресурсов чем действия с оригиналом) и организовать отложенное копирование таким образом, чтобы оно производилось только тогда, когда окончена модификация суррогата и он отличается от первоначального оригинала, то можно существенно уменьшить плату за копирование «тяжелых» субъектов.

Реализация

  • Заместитель может не владеть информацией о типе реального объекта, который необходимо заместить. Для реализации такой возможности достаточно организовать  класс Proxy так чтобы он мог работать субъектом только через его абстрактный интерфейс (абстрактный класс Subject), в таком случае класс Proxy сможет обращаться к любому конкретному классу, реализующему абстрактный интерфейс Subject, единообразно. Важно заметить, что если заместитель должен инстанцировать реальных субъектов (в случае с виртуальным заместителем, например), то знание конкретного класса является обязательным.

При реализации паттерна Proxy может возникнуть проблема при обращении к еще не инстанцированному экземпляру класса Subject. Она возможна в случае, когда заместитель должен работать со своими субъектами вне зависимости от реального места расположения экземпляров субъектов (на диске – персистентные, устойчивые экземпляры, в памяти – временные). В таком случае, нужно использовать при реализации такую форму идентификации, которая бы не зависела от адресного пространства. В примере из раздела «Пример кода» в качестве такого идентификатора используется имя файла.

Пример кода

Рассмотрим пример реализации виртуального заместителя. В классе Graphic определен абстрактный интерфейс для графических объектов:

abstract class Graphic
    {
        public string fileName;
        abstract public void Draw();
        abstract public void Load();

        public Image PictureToShow { get; set; }
    }

Класс Picture реализует интерфейс Graphic, позволяя работать с файлами картинок:

class Picture : Graphic
    {
        public Picture(string fileName)
        {
            this.fileName = fileName;
        }

        public override void Draw()
        {
            PictureToShow = Image.FromFile(fileName);
        }

        public override void Load()
        {
            throw new InvalidOperationException();
        }
    }

Класс PictureProxy представляет собой конкретную  реализацию виртуального заместителя, которая имеет тот же интерфейс, что и класс Picture:

class PictureProxy : Graphic
    {
        Picture picture;

        public PictureProxy(string fileName)
        {
            this.fileName = fileName;
            PictureToShow = new Bitmap(Resources.startImg, 52, 62);
        }

        public override void Draw()
        {
            if (picture == null)
            {
                picture = new Picture(fileName);
            }
            picture.Draw();
        }
        
        public override void Load()
        {
            PictureList.listPictures[PictureList.listPictures.IndexOf(this)] =             this.picture;
        }
    }

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

       В результате использования паттерна Proxy в клиентской части программы  появляется возможность использовать кешированные миниатюрные изображения для предпросмотра графических файлов.

Известные применения паттерна в .Net

Microsoft.Build.Tasks.Deployment.ManifestUtilities.ProxyStub

http://msdn.microsoft.com/ru-ru/library/microsoft.build.tasks.deployment.manifestutilities.proxystub(v=vs.110).aspx

System.Runtime.Remoting.Proxies.ProxyAttribute

http://msdn.microsoft.com/en-us/library/system.runtime.remoting.proxies.proxyattribute(v=vs.110).aspx

System.Web.Script.Services.ProxyGenerator

http://msdn.microsoft.com/ru-ru/library/system.web.script.services.proxygenerator(v=vs.110).aspx

System.Web.UI.WebControls.WebParts.ProxyWebPart

http://msdn.microsoft.com/ru-ru/library/system.web.ui.webcontrols.webparts.proxywebpart(v=vs.90).aspx

System.Net.Configuration.DefaultProxySection

http://msdn.microsoft.com/en-us/library/system.net.configuration.defaultproxysection(v=vs.110).aspx

System.Net.GlobalProxySelection

http://msdn.microsoft.com/ru-ru/library/system.net.globalproxyselection(v=vs.110).aspx

System.Net.IWebProxy

http://msdn.microsoft.com/ru-ru/library/system.net.iwebproxy(v=vs.118).aspx

System.Reflection.AssemblyNameProxy

http://msdn.microsoft.com/ru-ru/library/system.reflection.assemblynameproxy(v=vs.110).aspx

System.Runtime.Remoting.Proxies.RealProxy

http://msdn.microsoft.com/en-us/library/system.runtime.remoting.proxies.realproxy(v=vs.110).aspx

System.Web.UI.Design.ScriptManagerProxyDesigner

http://msdn.microsoft.com/ru-ru/library/system.web.ui.design.scriptmanagerproxydesigner(v=vs.90).aspx

System.Runtime.Serialization.SurrogateSelector

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.surrogateselector(v=vs.110).aspx

System.Runtime.Remoting.Messaging.RemotingSurrogateSelector

http://msdn.microsoft.com/ru-ru/library/system.runtime.remoting.messaging.remotingsurrogateselector(v=vs.110).aspx

© 2017 ITVDN, все права защищены