- Proxy (шаблон проектирования)
-
Шаблон проектирования Заместитель Proxy Тип: структурный
Описан в Design Patterns Да
Шаблон Proxy (определяет объект-заместитель англ. surrogate иначе -заменитель англ. placeholder) — шаблон проектирования, который предоставляет объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).
Содержание
Цель
Проблема
Необходимо управлять доступом к объекту так, чтобы создавать громоздкие объекты «по требованию».
Решение
Создать суррогат громоздкого объекта. «Заместитель» хранит ссылку, которая позволяет заместителю обратиться к реальному субъекту (объект класса «Заместитель» может обращаться к объекту класса «Субъект», если интерфейсы «Реального Субъекта» и «Субъекта» одинаковы). Поскольку интерфейс «Реального Субъекта» идентичен интерфейсу «Субъекта», так, что «Заместителя» можно подставить вместо «Реального Субъекта», контролирует доступ к «Реальному Субъекту», может отвечать за создание или удаление «Реального Субъекта». «Субъект» определяет общий для «Реального Субъекта» и «Заместителя» интерфейс, так, что «Заместитель» может быть использован везде, где ожидается «Реальный Субъект». При необходимости запросы могут быть переадресованы «Заместителем» «Реальному Субъекту».
Шаблон proxy бывает нескольких видов, а именно:
- Удаленный заместитель (англ. remote proxies) : обеспечивает связь с «Субъектом», который находится в другом адресном пространстве или на удалённой машине. Так же может отвечать за кодирование запроса и его аргументов и отправку закодированного запроса реальному «Субъекту»,
- Виртуальный заместитель (англ. virtual proxies): обеспечивает создание реального «Субъекта» только тогда, когда он действительно понадобится. Так же может кэшировать часть информации о реальном «Субъекте», чтобы отложить его создание,
- Копировать-при-записи: обеспечивает копирование «субъекта» при выполнении клиентом определённых действий (частный случай «виртуального прокси»).
- Защищающий заместитель (англ. protection proxies): может проверять, имеет ли вызывающий объект необходимые для выполнения запроса права.
- Кэширующий прокси: обеспечивает временное хранение результатов расчёта до отдачи их множественным клиентам, которые могут разделить эти результаты.
- Экранирующий прокси: защищает «Субъект» от опасных клиентов (или наоборот).
- Синхронизирующий прокси: производит синхронизированный контроль доступа к «Субъекту» в асинхронной многопоточной среде.
- Smart reference proxy: производит дополнительные действия, когда на «Субъект» создается ссылка, например, рассчитывает количество активных ссылок на «Субъект».
Преимущества и недостатки от применения
Преимущества:
- удаленный заместитель;
- виртуальный заместитель может выполнять оптимизацию;
- защищающий заместитель;
- "умная" ссылка;
- Недостатки
- резкое увеличение времени отклика.
Сфера применения
Шаблон Proxy может применяться в случаях работы с сетевым соединением, с огромным объектом в памяти (или на диске) или с любым другим ресурсом, который сложно или тяжело копировать. Хорошо известный пример применения — объект, подсчитывающий число ссылок.
Прокси и близкие к нему шаблоны[1]
- Адаптер обеспечивает отличающийся интерфейс к объекту.
- Прокси обеспечивает тот же самый интерфейс.
- Декоратор обеспечивает расширенный интерфейс.
Примеры реализации
Java
Пример реализацииpublic class Main { public static void main(String[] args) { // Create math proxy IMath p = new MathProxy(); // Do the math System.out.println("4 + 2 = " + p.add(4, 2)); System.out.println("4 - 2 = " + p.sub(4, 2)); System.out.println("4 * 2 = " + p.mul(4, 2)); System.out.println("4 / 2 = " + p.div(4, 2)); } } /** * "Subject" */ public interface IMath { public double add(double x, double y); public double sub(double x, double y); public double mul(double x, double y); public double div(double x, double y); } /** * "Real Subject" */ public class Math implements IMath { public double add(double x, double y) { return x + y; } public double sub(double x, double y) { return x - y; } public double mul(double x, double y) { return x * y; } public double div(double x, double y) { return x / y; } } /** * "Proxy Object" */ public class MathProxy implements IMath { private Math math; public MathProxy() { math = new Math(); } public double add(double x, double y) { return math.add(x, y); } public double sub(double x, double y) { return math.sub(x, y); } public double mul(double x, double y) { return math.mul(x, y); } public double div(double x, double y) { return math.div(x, y); } }
C++
Пример реализации/** * "Subject" */ class IMath { public: virtual double add(double, double) = 0; virtual double sub(double, double) = 0; virtual double mul(double, double) = 0; virtual double div(double, double) = 0; }; /** * "Real Subject" */ class Math : public IMath { public: virtual double add(double x, double y) { return x + y; } virtual double sub(double x, double y) { return x - y; } virtual double mul(double x, double y) { return x * y; } virtual double div(double x, double y) { return x / y; } }; /** * "Proxy Object" */ class MathProxy : public IMath { public: MathProxy() { math = new Math(); } virtual ~MathProxy() { delete math; } virtual double add(double x, double y) { return math->add(x, y); } virtual double sub(double x, double y) { return math->sub(x, y); } virtual double mul(double x, double y) { return math->mul(x, y); } virtual double div(double x, double y) { return math->div(x, y); } private: IMath *math; }; #include <iostream> using std::cout; using std::endl; int main() { // Create math proxy IMath *proxy = new MathProxy(); // Do the math cout << "4 + 2 = " << proxy->add(4, 2) << endl; cout << "4 - 2 = " << proxy->sub(4, 2) << endl; cout << "4 * 2 = " << proxy->mul(4, 2) << endl; cout << "4 / 2 = " << proxy->div(4, 2) << endl; delete proxy; return 0; }
C#
Пример реализацииusing System; using System.Threading; class MainApp { static void Main() { // Create math proxy IMath p = new MathProxy(); // Do the math Console.WriteLine("4 + 2 = " + p.Add(4, 2)); Console.WriteLine("4 - 2 = " + p.Sub(4, 2)); Console.WriteLine("4 * 2 = " + p.Mul(4, 2)); Console.WriteLine("4 / 2 = " + p.Div(4, 2)); // Wait for user Console.Read(); } } /// <summary> /// Subject - субъект /// </summary> /// <remarks> /// <li> /// <lu>определяет общий для <see cref="Math"/> и <see cref="Proxy"/> интерфейс, так что класс /// <see cref="Proxy"/> можно использовать везде, где ожидается <see cref="Math"/></lu> /// </li> /// </remarks> public interface IMath { double Add(double x, double y); double Sub(double x, double y); double Mul(double x, double y); double Div(double x, double y); } /// <summary> /// RealSubject - реальный объект /// </summary> /// <remarks> /// <li> /// <lu>определяет реальный объект, представленный заместителем</lu> /// </li> /// </remarks> class Math : IMath { public Math() { Console.WriteLine("Create object Math. Wait..."); Thread.Sleep(1000); } public double Add(double x, double y){return x + y;} public double Sub(double x, double y){return x - y;} public double Mul(double x, double y){return x * y;} public double Div(double x, double y){return x / y;} } /// <summary> /// Proxy - заместитель /// </summary> /// <remarks> /// <li> /// <lu>хранит ссылку, которая позволяет заместителю обратиться к реальному /// субъекту. Объект класса <see cref="MathProxy"/> может обращаться к объекту класса /// <see cref="IMath"/>, если интерфейсы классов <see cref="Math"/> и <see cref="IMath"/> одинаковы;</lu> /// <lu>предоставляет интерфейс, идентичный интерфейсу <see cref="IMath"/>, так что заместитель /// всегда может быть предоставлен вместо реального субъекта;</lu> /// <lu>контролирует доступ к реальному субъекту и может отвечать за его создание /// и удаление;</lu> /// <lu>прочие обязанности зависят от вида заместителя: /// <li> /// <lu><b>удаленный заместитель</b> отвечает за кодирование запроса и его аргументов /// и отправление закодированного запроса реальному субъекту в /// другом адресном пространстве;</lu> /// <lu><b>виртуальный заместитель</b> может кэшировать дополнительную информацию /// о реальном субъекте, чтобы отложить его создание.</lu> /// <lu><b>защищающий заместитель</b> проверяет, имеет ли вызывающий объект /// необходимые для выполнения запроса права;</lu> /// </li> /// </lu> /// </li> /// </remarks> class MathProxy : IMath { Math math; public MathProxy() { math = null; } /// <summary> /// Быстрая операция - не требует реального субъекта /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public double Add(double x, double y) { return x + y; } public double Sub(double x, double y) { return x - y; } /// <summary> /// Медленная операция - требует создания реального субъекта /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public double Mul(double x, double y) { if (math == null) math = new Math(); return math.Mul(x, y); } public double Div(double x, double y) { if (math == null) math = new Math(); return math.Div(x, y); } }
JavaScript
Пример реализации/* Subject */ function IMath() { this.add = function(x, y) {}; this.sub = function(x, y) {}; } /* Real Subject */ function RMath() { /* IMath.call(this); // агрегируем IMath, т.к. нативного наследования нет // этот вариант следует использовать вместо прототипирования, // если в IMath имеются приватные переменные, // которые могут быть доступны через геттеры в IMath */ this.add = function(x, y) { return x + y; }; this.sub = function(x, y) { return x - y; }; } RMath.prototype = new IMath(); RMath.prototype.constructor = RMath; /* Proxy */ function MathProxy() { var math = new RMath(); this.add = function(x, y) { return math.add(x, y); }; this.sub = function(x, y) { return math.sub(x, y); }; } var test = new MathProxy(); alert(test.add(3, 2)); // 5 alert(test.sub(3, 2)); // 1
Ruby
Пример реализации# # "Subject" - не нужен # # # "Real Subject" # class Math def add(x, y); x + y; end def sub(x, y); x - y; end def mul(x, y); x * y; end def div(x, y); x / y; end end # # "Proxy Object" # class MathProxy def initialize @math = Math.new end def add(x, y); @math.add(x, y); end def sub(x, y); @math.sub(x, y); end def mul(x, y); @math.mul(x, y); end def div(x, y); @math.div(x, y); end end # Create math proxy p = MathProxy.new; # Do the math puts "4 + 2 = #{p.add(4, 2)}" puts "4 - 2 = #{p.sub(4, 2)}" puts "4 * 2 = #{p.mul(4, 2)}" puts "4 / 2 = #{p.div(4, 2)}"
PHP5
Пример реализации<?php /// Subject - субъект /// <lu>определяет общий для Math и "Proxy" интерфейс, так что класс /// "Proxy" можно использовать везде, где ожидается interface IMath { function Add($x, $y); function Sub($x, $y); function Mul($x, $y); function Div($x, $y); } /// RealSubject - реальный объект /// определяет реальный объект, представленный заместителем class Math implements IMath { public function __construct() { print ("Create object Math. Wait..."); sleep(5); } public function Add($x, $y){return $x + $y;} public function Sub($x, $y){return $x - $y;} public function Mul($x, $y){return $x * $y;} public function Div($x, $y){return $x / $y;} } /// Proxy - заместитель /// хранит ссылку, которая позволяет заместителю обратиться к реальному /// субъекту. Объект класса "MathProxy" может обращаться к объекту класса /// "IMath", если интерфейсы классов "Math" и "IMath" одинаковы; /// предоставляет интерфейс, идентичный интерфейсу "IMath", так что заместитель /// всегда может быть предоставлен вместо реального субъекта; /// контролирует доступ к реальному субъекту и может отвечать за его создание /// и удаление; /// прочие обязанности зависят от вида заместителя: /// удаленный заместитель отвечает за кодирование запроса и его аргументов /// и отправление закодированного запроса реальному субъекту в /// другом адресном пространстве; /// виртуальный заместитель может кэшировать дополнительную информацию /// о реальном субъекте, чтобы отложить его создание. /// защищающий заместитель проверяет, имеет ли вызывающий объект /// необходимые для выполнения запроса права; class MathProxy implements IMath { protected $math; public function __construct() { $this->math = null; } /// Быстрая операция - не требует реального субъекта public function Add($x, $y) { return $x + $y; } public function Sub($x, $y) { return $x - $y; } /// Медленная операция - требует создания реального субъекта public function Mul($x, $y) { if ($this->math == null) $this->math = new Math(); return $this->math->Mul($x, $y); } public function Div($x, $y) { if ($this->math == null) $this->math = new Math(); return $this->math->Div($x, $y); } } $p = new MathProxy; // Do the math print("4 + 2 = ".$p->Add(4, 2)); print("4 - 2 = ".$p->Sub(4, 2)); print("4 * 2 = ".$p->Mul(4, 2)); print("4 / 2 = ".$p->Div(4, 2)); ?>
ActionScript
Пример реализации//файл IMath.as package { public interface IMath { function add(a : Number, b : Number) : Number; function sub(a : Number, b : Number) : Number; function mul(a : Number, b : Number) : Number; function div(a : Number, b : Number) : Number; } } //файл MathSubject.as package { public class MathSubject implements IMath { public function add(a : Number, b : Number) : Number { return a + b; } public function sub(a : Number, b : Number) : Number { return a - b; } public function mul(a : Number, b : Number) : Number { return a * b; } public function div(a : Number, b : Number) : Number { return a / b; } } } //файл MathProxy.as package { public class MathProxy implements IMath { private var math : MathSubject; public function MathProxy() { math = new MathSubject(); } public function add(a : Number, b : Number) : Number { return math.add(a, b); } public function sub(a : Number, b : Number) : Number { return math.sub(a, b); } public function mul(a : Number, b : Number) : Number { return math.mul(a, b); } public function div(a : Number, b : Number) : Number { if (b != 0) return math.div(a, b); else { trace("Division by zero."); return Number.POSITIVE_INFINITY; } } } } //файл Main.as package { import flash.display.Sprite; public class Main extends Sprite { public function Main() { playWithMath(new MathSubject()); playWithMath(new MathProxy()); } public function playWithMath(math : IMath) : void { trace(math.add(5, 0)); trace(math.sub(5, 0)); trace(math.mul(5, 0)); trace(math.div(5, 0)); } } }
Python
Пример реализации# -*- coding: utf-8 -*- class IMath: """Интерфейс для прокси и реального субъекта""" def add(self, x, y): raise NotImplementedError() def sub(self, x, y): raise NotImplementedError() def mul(self, x, y): raise NotImplementedError() def div(self, x, y): raise NotImplementedError() class Math(IMath): """Реальный субъект""" def add(self, x, y): return x + y def sub(self, x, y): return x - y def mul(self, x, y): return x * y def div(self, x, y): return x / y class Proxy(IMath): """Прокси""" def __init__(self): self.math = Math() def add(self, x, y): return self.math.add(x, y) def sub(self, x, y): return self.math.sub(x, y) def mul(self, x, y): return self.math.mul(x, y) def div(self, x, y): if y == 0: return float('inf') # Вернуть positive infinity return self.math.div(x, y) p = Proxy() x, y = 4, 2 print '4 + 2 = ' + str(p.add(x, y)) print '4 - 2 = ' + str(p.sub(x, y)) print '4 * 2 = ' + str(p.mul(x, y)) print '4 / 2 = ' + str(p.div(x, y))
VB.NET
Пример реализацииImports System.Threading Class MainApp Shared Sub Main() ' Create math proxy Dim p As IMath = New MathProxy() ' Do the math Console.WriteLine("4 + 2 = " & p.Add(4, 2)) Console.WriteLine("4 - 2 = " & p.Subtr(4, 2)) Console.WriteLine("4 * 2 = " & p.Mul(4, 2)) Console.WriteLine("4 / 2 = " & p.Div(4, 2)) ' Wait for user Console.Read() End Sub End Class ''' <summary> ''' Subject - субъект ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяет общий для <see cref="Math"/> и <see cref="Proxy"/> интерфейс, так что класс ''' <see cref="Proxy"/> можно использовать везде, где ожидается <see cref="Math"/></lu> ''' </li> ''' </remarks> Public Interface IMath Function Add(ByVal x As Double, ByVal y As Double) As Double Function Subtr(ByVal x As Double, ByVal y As Double) As Double Function Mul(ByVal x As Double, ByVal y As Double) As Double Function Div(ByVal x As Double, ByVal y As Double) As Double End Interface ''' <summary> ''' RealSubject - реальный объект ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяет реальный объект, представленный заместителем</lu> ''' </li> ''' </remarks> Class Math Implements IMath Public Sub New() Console.WriteLine("Create object Math. Wait...") Thread.Sleep(1000) End Sub Public Function Add(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Add Return x + y End Function Public Function Subtr(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Subtr Return x - y End Function Public Function Mul(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Mul Return x * y End Function Public Function Div(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Div Return x / y End Function End Class ''' <summary> ''' Proxy - заместитель ''' </summary> ''' <remarks> ''' <li> ''' <lu>хранит ссылку, которая позволяет заместителю обратиться к реальному ''' субъекту. Объект класса <see cref="MathProxy"/> может обращаться к объекту класса ''' <see cref="IMath"/>, если интерфейсы классов <see cref="Math"/> и <see cref="IMath"/> одинаковы;</lu> ''' <lu>предоставляет интерфейс, идентичный интерфейсу <see cref="IMath"/>, так что заместитель ''' всегда может быть предоставлен вместо реального субъекта;</lu> ''' <lu>контролирует доступ к реальному субъекту и может отвечать за его создание ''' и удаление;</lu> ''' <lu>прочие обязанности зависят от вида заместителя: ''' <li> ''' <lu><b>удаленный заместитель</b> отвечает за кодирование запроса и его аргументов ''' и отправление закодированного запроса реальному субъекту в ''' другом адресном пространстве;</lu> ''' <lu><b>виртуальный заместитель</b> может кэшировать дополнительную информацию ''' о реальном субъекте, чтобы отложить его создание.</lu> ''' <lu><b>защищающий заместитель</b> проверяет, имеет ли вызывающий объект ''' необходимые для выполнения запроса права;</lu> ''' </li> ''' </lu> ''' </li> ''' </remarks> Class MathProxy Implements IMath Private math As Math = Nothing ''' <summary> ''' Быстрая операция - не требует реального субъекта ''' </summary> Public Function Add(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Add Return x + y End Function Public Function Subtr(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Subtr Return x - y End Function ''' <summary> ''' Медленная операция - требует создания реального субъекта ''' </summary> Public Function Mul(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Mul If math Is Nothing Then math = New Math() End If Return math.Mul(x, y) End Function Public Function Div(ByVal x As Double, ByVal y As Double) As Double Implements IMath.Div If math Is Nothing Then math = New Math() End If Return math.Div(x, y) End Function End Class
См. также
структурные шаблоны проектирования адаптер | мост | компоновщик | декоратор | фасад | заместитель | приспособленец | Выделение частного класса данных
Шаблоны проектирования Основные Порождающие Структурные Адаптер • Выделение частного класса данных • Декоратор • Заместитель • Компоновщик • Мост • Приспособленец • Фасад
Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик - Пост-объектное программирование
Литература
Ссылки
- Паттерн Proxy (заместитель) — назначение, описание, особенности и реализация на С++.
Категории:- Шаблоны проектирования
- Объектно-ориентированное программирование
- Структурные шаблоны проектирования
Wikimedia Foundation. 2010.