JavaScript/Notes/PrivateProxy
Proxy
A proxy is as an interface to something else.
Private Proxy
The private proxy exposes a public interface object. This public interface object delegates some of its responsibility to a private instance.
Structural Pattern: The private and public instances are tightly coupled. They can share data with each other within the function where they are defined, but not outside of there.
Usage: This pattern is useful when you have details and complexities that can be hidden. By exposing a very limited set of methods, the code allows caller less opportunity to modify and break the objects it produces.
Example: User wants to be able to interact with "program" objects on a calendar. The program can be then modified by the user.
The application handles modification of program objects through a ProgramProxy, by calling setPhases, setCampaigns. The caller can also call `buildHTML` to generate new HTML for the program or toJSON to get a JSON representation of it.
<source lang="javascript"> var Program = function(A) {
"use strict";
var phaseListClassName = "program-phase-list", phaseClassName = "program-phase";
function Program(id, config) { var start = config.startDate, end = config.endDate; this.id = id; this.dateRange = new A.DateRange(start, end); this.phases = config.phases.slice(); }
var programs = {}, programProxies = {};
var phaseListElement = document.createElement("ul"), phaseElement = document.createElement("li"); phaseListElement.className = phaseListClassName; phaseElement.className = phaseClassName;
function ProgramProxy(id, config) { this.id = id; this.start = config.startDate; this.displayName = config.name; this.end = config.endDate; programs[id] = new Program(id, config); }
ProgramProxy.prototype = { setPhases: function(phases) { if(phases && phases.slice) { programs[this.id].phases = phases.slice(); } },
buildHTML: function() { var program = programs[this.id], html = ""; html += buildPhaseHTML(program); html += buildCampaignHTML(program); return html; } };
var div = document.createElement("div"); function buildPhaseHTML(program) { div.innerHTML = ""; var i, li, phases = program.phases, len = phases.length, phaseRationalValue, ul = phaseListElement.cloneNode(false); ul.id = "program-" + program.id; for(i = 0; i < len; i++) { li = phaseElement.cloneNode(false); li.id = "program-" + program.id + "-phase-" + phases[i].name + ""; li.style.width = getPhaseWidth(phases[i], program.dateRange.duration); ul.appendChild(li); } div.appendChild(ul); return div.innerHTML; } function buildCampaignHTML() { return"";
"
- "
+ "
- fixme:\u00A0buildCampaignHTML" + "
";
}
function getPhaseWidth(phase, programDuration) { var phaseDateRange = new A.DateRange( phase.startDate, phase.endDate); var rationalValue = phaseDateRange.duration/programDuration; return (0|100 * rationalValue) + "%"; }
function getOrCreateProgram(config) { return programProxies[config.id] || (programProxies[config.id] = new ProgramProxy(config.id, config)); }
function removeProgram(id) { if(!programs[id]) { throw new ReferenceError("Program: " + id + " does not exist." ); } delete programs[id]; delete programProxies[id]; }
return { getOrCreate: getOrCreateProgram, remove: removeProgram };
}({
DateRange: DateRange // defined elsewhere.
}); </source>
`Program.getOrCreate` returns a `ProgramProxy`.
`ProgramProxy` has two methods, both of which delegate to `Program` instances.
The body of buildHTML <source lang="javascript"> setPhases: function(phases) {
if(phases && phases.slice) { programs[id].phases = phases.slice(); }
} </source>
This is possible because `ProgramProxy` and each `Program` have the same id. Each are stored in one of two objects, keyed by their id. See also: Proxy Design Pattern.