Заместитель (шаблон проектирования)
Заместитель (англ. Proxy) — структурный шаблон проектирования, предоставляющий объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).
Заместитель | |
---|---|
Proxy | |
Тип | структурный |
Назначение | Предоставляет суррогатный объект, управляющий доступом к другому объекту. |
Плюсы |
|
Минусы |
|
Описан в Design Patterns | Да |
Цель
Проблема
Необходимо контролировать доступ к объекту, не изменяя при этом поведение клиента.
Необходимо иметь доступ к объекту так, чтобы не создавать реальные объекты непосредственно, а через другой объект, который может иметь дополнительную функциональность.
Решение
Создать суррогат реального объекта. «Заместитель» хранит ссылку, которая позволяет заместителю обратиться к реальному субъекту (объект класса «Заместитель» может обращаться к объекту класса «Субъект», если интерфейсы «Реального Субъекта» и «Субъекта» одинаковы). Поскольку интерфейс «Реального Субъекта» идентичен интерфейсу «Субъекта», так, что «Заместителя» можно подставить вместо «Реального Субъекта», контролирует доступ к «Реальному Субъекту», может отвечать за создание или удаление «Реального Субъекта». «Субъект» определяет общий для «Реального Субъекта» и «Заместителя» интерфейс так, что «Заместитель» может быть использован везде, где ожидается «Реальный Субъект». При необходимости запросы могут быть переадресованы «Заместителем» «Реальному Субъекту».
Виды
- Протоколирующий прокси: сохраняет в лог все вызовы «Субъекта» с их параметрами.
- Удалённый заместитель (англ. 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 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();
}
}
}
Scala
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)
}
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 */
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
Ruby
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
PHP5
<?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));
?>
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 = None
# Быстрые операции - не требуют реального субъекта
def add(self, x, y):
return x + y
def sub(self, x, y):
return x - y
# Медленная операция - требует создания реального субъекта
def mul(self, x, y):
if not self.math:
self.math = Math()
return self.math.mul(x, y)
def div(self, x, y):
if y == 0:
return float('inf') # Вернуть positive infinity
if not self.math:
self.math = Math()
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
Swift
// 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)
- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно-ориентированного проектирования. Паттерны проектирования.=Design Patterns.Elements of reusable object-oriented software. — СПб.: Питер, 2001. — 368 с. — ISBN 5-272-00355-1.
- Эрик Фримен, Элизабет Фримен, Кэтти Сьера, Берт Бейтс. Паттерны проектирования. — СПб.: Питер, 2012. — 656 с. — ISBN 978-5-459-00435-9.