Заместитель (шаблон проектирования)
Заместитель | |
---|---|
Proxy | |
Тип | структурный |
Назначение | Предоставляет суррогатный объект, управляющий доступом к другому объекту. |
Плюсы |
|
Минусы |
|
Описан в Design Patterns | Да |
Заместитель (англ. Proxy) — структурный шаблон проектирования, предоставляющий объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).
Цель
[править | править код]Проблема
[править | править код]Необходимо контролировать доступ к объекту, не изменяя при этом поведение клиента.
Необходимо иметь доступ к объекту так, чтобы не создавать реальные объекты непосредственно, а через другой объект, который может иметь дополнительную функциональность.
Решение
[править | править код]Создать суррогат реального объекта. «Заместитель» хранит ссылку, которая позволяет заместителю обратиться к реальному субъекту (объект класса «Заместитель» может обращаться к объекту класса «Субъект», если интерфейсы «Реального Субъекта» и «Субъекта» одинаковы). Поскольку интерфейс «Реального Субъекта» идентичен интерфейсу «Субъекта», так, что «Заместителя» можно подставить вместо «Реального Субъекта», контролирует доступ к «Реальному Субъекту», может отвечать за создание или удаление «Реального Субъекта». «Субъект» определяет общий для «Реального Субъекта» и «Заместителя» интерфейс так, что «Заместитель» может быть использован везде, где ожидается «Реальный Субъект». При необходимости запросы могут быть переадресованы «Заместителем» «Реальному Субъекту».
Виды
[править | править код]- Протоколирующий прокси: сохраняет в лог все вызовы «Субъекта» с их параметрами.
- Удалённый заместитель (англ. remote proxies): обеспечивает связь с «Субъектом», который находится в другом адресном пространстве или на удалённой машине. Также может отвечать за кодирование запроса и его аргументов и отправку закодированного запроса реальному «Субъекту»,
- Виртуальный заместитель (англ. virtual proxies): обеспечивает создание реального «Субъекта» только тогда, когда он действительно понадобится. Также может кэшировать часть информации о реальном «Субъекте», чтобы отложить его создание,
- Копировать-при-записи: обеспечивает копирование «субъекта» при выполнении клиентом определённых действий (частный случай «виртуального прокси»).
- Защищающий заместитель (англ. protection proxies): может проверять, имеет ли вызывающий объект необходимые для выполнения запроса права.
- Кэширующий прокси: обеспечивает временное хранение результатов расчёта до отдачи их множественным клиентам, которые могут разделить эти результаты.
- Экранирующий прокси: защищает «Субъект» от опасных клиентов (или наоборот).
- Синхронизирующий прокси: производит синхронизированный контроль доступа к «Субъекту» в асинхронной многопоточной среде.
- «Умная» ссылка (англ. smart reference proxy): производит дополнительные действия, когда на «Субъект» создается ссылка, например, рассчитывает количество активных ссылок на «Субъект».
Преимущества и недостатки от применения
[править | править код]Преимущества
[править | править код]- удалённый заместитель;
- виртуальный заместитель может выполнять оптимизацию;
- защищающий заместитель;
- «умная» ссылка(указатель);
Недостатки
[править | править код]- резкое увеличение времени отклика.
Сфера применения
[править | править код]Шаблон заместитель может применяться в случаях работы с сетевым соединением, с огромным объектом в памяти (или на диске) или с любым другим ресурсом, который сложно или тяжело копировать. Хорошо известный пример применения — объект, подсчитывающий число ссылок.
Прокси и близкие к нему шаблоны[1]
[править | править код]- Адаптер обеспечивает отличающийся интерфейс к объекту.
- Прокси обеспечивает тот же самый интерфейс.
- Декоратор обеспечивает расширенный интерфейс.
Примеры реализации
[править | править код]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 double add(double x, double y) { lazyInitMath(); return math.add(x, y); } public double sub(double x, double y) { lazyInitMath(); return math.sub(x, y); } public double mul(double x, double y) { lazyInitMath(); return math.mul(x, y); } public double div(double x, double y) { lazyInitMath(); return math.div(x, y); } private void lazyInitMath() { if (math == null) { math = new Math(); } } }
object Main extends App { val p: IMath = new MathProxy 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" */ trait IMath { def add(x: Double, y: Double): Double def sub(x: Double, y: Double): Double def mul(x: Double, y: Double): Double def div(x: Double, y: Double): Double } /** * "Real Subject" */ class Math extends IMath { def add(x: Double, y: Double) = x + y def sub(x: Double, y: Double) = x - y def mul(x: Double, y: Double) = x * y def div(x: Double, y: Double) = x / y } /** * "Proxy Object" */ class MathProxy extends IMath { private lazy val math = new Math def add(x: Double, y: Double) = math.add(x, y) def sub(x: Double, y: Double) = math.sub(x, y) def mul(x: Double, y: Double) = math.mul(x, y) def div(x: Double, y: Double) = math.div(x, y) }
/** * "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; virtual ~IMath () = default; }; /** * "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); } }
/* Subject */ class IMath { add(x, y) {} sub(x, y) {} } /* Real Subject */ class RMath extends IMath { add(x, y) { return x + y; } sub(x, y) { return x - y; } } /* Proxy */ class MathProxy extends IMath { math = new RMath() add(x, y) { return this.math.add(x, y) } sub(x, y) { return this.math.sub(x, y) } } const test = new MathProxy() alert(test.add(3, 2)) // 5 alert(test.sub(3, 2)) // 1
module ProxyPattern # Proxy has same interface like a Real Subject # Real Subject class Account attr_reader :balance def initialize(balance = 0) @balance = balance end def deposit(amount) @balance += amount end def withdraw(amount) @balance -= amount end end module Protection # Additional functionality to control the access to the realSubject # Proxy class AccountProxy def initialize(subject, current_user) @subject = subject @current_user = current_user @balance = 0 end def deposit(amount) @subject.deposit(amount) if authorize end def withdraw(amount) @subject.withdraw(amount) if authorize end def balance @subject.balance end private def authorize puts 'Access denied' unless @current_user == 'admin' @current_user == 'admin' end end def self.run puts '=> Proxy :: Protection' puts 'as user' protected_account = AccountProxy.new(Account.new, 'user') protected_account.deposit(20) protected_account.withdraw(10) puts protected_account.balance puts 'as admin' protected_account = AccountProxy.new(Account.new, 'admin') protected_account.deposit(20) protected_account.withdraw(10) puts protected_account.balance puts '' end end module Virtual # Delay realSubject loading (lazy loading) # Proxy class AccountProxy def initialize(local_balance = 0) @local_balance = local_balance end def deposit(amount) @local_balance += amount end def withdraw(amount) @local_balance -= amount end def balance subject.balance end def subject @subject ||= Account.new(@local_balance) end end def self.run puts '=> Proxy :: Virtual' local_account = AccountProxy.new local_account.deposit(20) local_account.withdraw(10) local_account.deposit(15) local_account.withdraw(5) puts 'No real account yet:' puts local_account.inspect local_account.balance puts 'Real account was created:' puts local_account.inspect puts '' end end def self.run Protection.run Virtual.run end end
<?php /// Subject - субъект /// определяет общий для 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" может обращаться к объекту класса /// "Math", если интерфейсы классов "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)); ?>
//файл 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)); } } }
# -*- 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 x + y def sub(self, x, y): return x - y def mul(self, x, y): return self.math.mul(x, y) def div(self, x, y): return float('inf') if y == 0 else 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))
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
// Subject protocol Match { func add(x: Int, y: Int) -> Int func sub(x: Int, y: Int) -> Int func mul(x: Int, y: Int) -> Int func div(x: Int, y: Int) -> Int } // Real Subject class MatchImp: Match { func add(x: Int, y: Int) -> Int { return x + y } func sub(x: Int, y: Int) -> Int { return x - y } func mul(x: Int, y: Int) -> Int { return x * y } func div(x: Int, y: Int) -> Int { return x / y } } // Proxy class MatchProxy: Match { private let math = MatchImp() func add(x: Int, y: Int) -> Int { return math.add(x: x, y: y) } func sub(x: Int, y: Int) -> Int { return math.sub(x: x, y: y) } func mul(x: Int, y: Int) -> Int { return math.mul(x: x, y: y) } func div(x: Int, y: Int) -> Int { return math.div(x: x, y: y) } } // Use Proxy let proxy = MatchProxy() print("7 * 8 = \(proxy.mul(x: 8, y: 7))")
См. также
[править | править код]Примечания
[править | править код]- ↑ Дневники разработчика // Заместитель (Proxy) . Дата обращения: 26 мая 2010. Архивировано из оригинала 14 мая 2010 года.
Литература
[править | править код]- CodeLIB.YOURS // Заместитель (Proxy) Архивная копия от 14 мая 2010 на Wayback Machine
- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно-ориентированного проектирования. Паттерны проектирования.=Design Patterns.Elements of reusable object-oriented software. — СПб.: Питер, 2001. — 368 с. — ISBN 5-272-00355-1.
- Эрик Фримен, Элизабет Фримен, Кэтти Сьера, Берт Бейтс. Паттерны проектирования. — СПб.: Питер, 2012. — 656 с. — ISBN 978-5-459-00435-9.
Ссылки
[править | править код]- Паттерн Proxy (заместитель) Архивная копия от 8 апреля 2012 на Wayback Machine — назначение, описание, особенности и реализация на C++.