Javascript. Proxy pattern

Паттерн Заместитель (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