Паттерн Заместитель (Proxy pattern) предоставляет суррогатный объект, управляющий доступом к другому объекту. Выделяют несколько вариантов управления доступом в заместителях:
- удаленный заместитель управляет доступом к удаленному объекту;
- виртуальный заместитель управляет доступом к ресурсу, создание которого требует больших затрат ресурсов;
- защитный заместитель контролирует доступ к ресурсу в соответствии с системой привилегий.
Данный паттерн относится к структурным паттернам. Основная реализация основана на отложенной инициализации объекта. Данный паттерн полезно использовать там, где приходится работать с дорогими ресурсами, например, пусть создание реального объекта или его использование занимает много ресурсов, тогда прокси-объект может взять на себя часть обязанностей данного объекта с целью избегания лишних обращений к нему, например, прокси может иметь кэш, который можно отдавать клиентам на их запросы, вместо повторного обращения к ресурсам. Вот примерчик сказанного.
/* real object */
var http = {
makeRequest: function (handler) {
$.ajax({
url: 'javascript.js.php',
async: false, // синхронный режим
success: handler
});
}
};
/* proxy object */
var proxy = {
cache: null,
makeRequest: function (handler) {
console.log(this.cache);
if (this.cache != null) {
handler(this.cache);
}
else {
http.makeRequest(function (data) {
proxy.cache = data;
handler(data);
});
}
}
};
var handler = function (data) {
// some actions
};
// первоначальное обращение к реальному объекту http
proxy.makeRequest(handler);
// выдача кэша
proxy.makeRequest(handler);
Ниже приведена диаграмма классов данного паттерна
Стоит отметить, что на Javascript реализовать удаленного заместителя в силу ее природы невозможно, хотя есть идеи, высказываемые некоторыми разработчиками, например см. книгу - Pro JavaScript Design Pattens. Ross Harmes. Dustan Diaz - о работе через ajax с удаленными веб сервисами или объектами. Но это только некоторого рода приближение. Также затруднительно реализовать защитного заместителя, в силу слабой типизации языка. Хотя на мой взгляд, все зависит от смысла вкладываемого к слово защитный :) Ниже приведу еще один пример реализации данного паттерна.
/* interface Subject - Image */
var Image = function () {
this.display = function () {};
};
/* RealSubject - RealImage */
var RealImage = function (fileName) {
var fileName = fileName;
this.display = function () {
console.log("Displaying " + fileName);
};
this.loadFromDisk = function () {
console.log("Loading " + fileName);
};
this.loadFromDisk(fileName);
};
RealImage.prototype = new Image;
RealImage.prototype.constructor = RealImage;
/* Proxy - ProxyImage */
var ProxyImage = function (fileName) {
var fileName = fileName,
realImage = null;
this.display = function () {
if (realImage === null) {
realImage = new RealImage(fileName);
}
realImage.display();
};
};
ProxyImage.prototype = new Image;
ProxyImage.prototype.constructor = ProxyImage;
/* testing... */
var Application = function () {
this.run = function () {
var image = new ProxyImage("test_10mb.jpg");
//image will be loaded from disk
image.display();
//image will not be loaded from disk
image.display();
};
};
var app = new Application();
app.run();
На выходе получаем следующий лог
Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg