Паттерн Заместитель (Proxy, Surrogate, Суррогат)

Название и классификация паттерна

Заместитель — паттерн, структурирующий объекты.

Назначение

Вам нужно управлять ресурсоемкими объектами. Вы не хотите создавать экземпляры таких объектов до момента их реального использования. Proxy является суррогатом другого объекта и управляет доступом к нему.

Обсуждение паттерна Proxy

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

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

Существует четыре ситуации, когда можно использовать паттерн Proxy:

  • • виртуальный Proxy является Заместителем объектов, создание которых обходится дорого. Реальный объект создается только при первом запросе/доступе клиента к объекту;
  • • удаленный Proxy предоставляет локального представителя для объекта, который находится в другом адресном пространстве («заглушки» в RPC и CORBA);
  • • защитный Proxy контролирует доступ к основному объекту. «Суррогатный» объект предоставляет доступ к реальному объекту, только вызывающий объект имеет соответствующие права;
  • • интеллектуальный Proxy выполняет дополнительные действия при доступе к объекту.

Вот типичные области применения интеллектуальных Рюху:

  • • подсчет числа ссылок на реальный объект. При отсутствии ссылок память под объект автоматически освобождается (известен также как интеллектуальный указатель или smart pointer);
  • • загрузка объекта в память при первом обращении к нему;
  • • установка запрета на изменение реального объекта при обращении к нему других объектов.

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

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

  • удаленный Заместитель предоставляет локального представителя вместо объекта, находящегося в другом адресном пространстве;
  • виртуальный Заместитель создает «тяжелые» объекты по требованию;
  • защищающий Заместитель контролирует доступ к исходному объекту. Такие Заместители полезны, когда для разных объектов определены различные права доступа;
  • «умная» ссылка — это замена обычного указателя. Она позволяет выполнить дополнительные действия при доступе к объекту. К типичным применениям такой ссылки можно отнести:
  • — подсчет числа ссылок на реальный объект, с тем чтобы занимаемую им память можно было освободить автоматически, когда не останется ни одной ссылки (такие ссылки называют еще «умными» указателями);
  • — загрузку объекта в память при первом обращении к нему;
  • — проверку и установку блокировки на реальный объект при обращении к нему, чтобы никакой другой объект не смог в это время изменить его.

Структура паттерна Proxy

Заместитель Proxy и реальный объект Real Subject имеют одинаковые интерфейсы класса Subject, поэтому Заместитель может использоваться «прозрачно» для клиента вместо реального объекта.

UML-диаграмма классов паттерна Proxy Структура паттерна Заместитель приведена на рис. 59.

Участники

Proxy (imageProxy) — Заместитель:

• хранит ссылку, которая позволяет Заместителю обратиться к реальному субъекту. Объект класса Proxy может обращаться к

UML-диаграмма паттерна Заместитель

Рис. 59. UML-диаграмма паттерна Заместитель

объекту класса Subj ect, если интерфейсы классов RealSubj ect и Subj ect одинаковы;

  • • предоставляет интерфейс, идентичный интерфейсу Subj ect, так что Заместитель всегда может быть подставлен вместо реального субъекта;
  • • контролирует доступ к реальному субъекту и может отвечать за его создание и удаление.

Прочие обязанности зависят от вида Заместителя:

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

Subject — субъект: определяет общий для RealSubject и Proxy интерфейс, так что класс Proxy можно использовать везде, где ожидается RealSubject.

RealSubject (Image) — реальный субъект: определяет реальный объект, представленный Заместителем.

Отношения

Proxy при необходимости переадресует запросы объекту RealSubject. Детали зависят от вида Заместителя.

Результаты

С помощью паттерна Заместитель при доступе к объекту вводится дополнительный уровень косвенности. У этого подхода есть много вариантов в зависимости от вида Заместителя:

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

Есть еще одна оптимизация, которую паттерн Заместитель иногда скрывает от клиента. Она называется копированием при записи (copy-on-write) и имеет много общего с созданием объекта по требованию. Копирование большого и сложного объекта — очень дорогая операция. Если копия не модифицировалась, то нет смысла эту цену платить. Если отложить процесс копирования, применив Заместитель, то можно быть уверенным, что эта операция произойдет только тогда, когда он действительно был изменен. Чтобы во время записи можно было копировать, необходимо подсчитывать ссылки на субъект. Копирование Заместителя просто увеличивает счетчик ссылок. И только тогда, когда клиент запрашивает операцию, изменяющую субъект, Заместитель действительно выполняет копирование. Одновременно Заместитель должен уменьшить счетчик ссылок. Когда счетчик ссылок становится равным нулю, субъект уничтожается.

Копирование при записи может существенно уменьшить плату за копирование «тяжелых» субъектов.

Особенности паттерна Proxy

Adapter предоставляет своему объекту другой интерфейс . Proxy предоставляет тот же интерфейс. Decorator предоставляет расширенный интерфейс.

Decorator и Proxy имеют разные цели, но схожие структуры. Оба вводят дополнительный уровень косвенности: их реализации хранят ссылку на объект, на который они отправляют запросы.

Реализация паттерна Proxy

Инициализация при первом использовании.

Спроектируйте класс-обертку с «дополнительным уровнем косвенности».

Этот класс содержит указатель на реальный класс.

Этот указатель инициализируется нулевым значением.

Реальный объект создается при поступлении запроса «на первом использовании» (отложенная инициализация или lazy intialization). Запрос всегда делегируется реальному объекту.

class Real Image

{

int m_id; public:

Reallmage(int i)

{

m_id = i;

cout << " $$ ctor: " << m_id << ' ';

}

~RealImage()

{

cout << " dtor: " << m_id << ' ';

}

void draw()

{

cout << " drawing image " << m_id << V;

}

// 1. Класс-обертка с "дополнительным уровнем косвенности" class Image {

//2. Класс-обертка содержит указатель на реальный класс Reallmage *m_the_real_thing; int m_id; static int s_next; public:

Image()

{

m_id = s_next++;

// 3. Инициализируется нулевым значением m_the_real_thing = 0;

}

~Image()

{

delete m_the_real_thing;

}

void draw()

{

// 4. Реальный объект создается при поступлении // запроса "на первом использовании" if (!m_the_real_thing) m_the_real_thing = new Reallmage(m_id);

// 5. Запрос всегда делегируется реальному объекту m_the_real_thing->draw();

}

};

int Image::s_next = 1 ;

int main()

{

Image images[5|;

for (int i; true;)

{

cout << "Exit[0], Image[l-5]: ein >> i; if (i == 0) break;

images[i - l].draw();

}

Вывод программы:

Exit[0], Image[l-5]: 2 $$ ctor: 2 drawing image 2 Exit[0], Image[l-5]: 4 $$ ctor: 4 drawing image 4 Exit[0], Image[l-5]: 2 drawing image 2 Exit[0], Image[l-5]: 0 dtor: 4 dtor: 2

Защитный Proxy контролирует доступ к основному объекту.

class Person

{

string nameString;

static string list[]; static int next; public:

Person()

{

nameString = list[next++];

}

string name()

{

return nameString;

}

};

string Person: :list[] =

{

"Tom", "Dick", "Harry", "Bubba"

};

int Person::next = 0;

class PettyCash Protected

{

int balance; public:

PettyCashProtected()

{

balance = 500;

}

bool withdraw(int amount)

{

if (amount > balance) return false; balance -= amount; return true;

}

int getBalance()

{

return balance;

}

class PettyCash

{

PettyCash Protected realThing; public:

bool withdraw(Person &p, int amount)

{

if (p.name() == "Tom” || p.name() "Harry" || p.name() == "Bubba") return realThing.withdraw(amount); else

return false;

}

int getBalance()

{

return realThing.getBalance();

}

};

int main()

{

PettyCash pc;

Person workers[4];

for (int i = 0, amount = 100; i < 4; i++, amount += 100) if (!pc.withdraw(workers[i], amount)) cout << "No money for" << workers[i].name() << ' '; else

cout << amount << " dollars for" << workers[i].name() << ' ';

cout << "Remaining balance is" << pc.getBalance() << ' ';

}

Вывод программы:

100 dollars for Tom No money for Dick 300 dollars for Harry No money for Bubba Remaining balance is 100

Родственные паттерны

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

Реализация Декоратора похожа на реализацию Заместителя, но назначение совершенно иное. Декоратор добавляет объекту новые обязанности, а Заместитель контролирует доступ к объекту.

 
< Пред   СОДЕРЖАНИЕ     След >