Proxy Pattern

The Proxy pattern uses one object as the interface for another object

  • It sits between users or clients of the object and the object itself
  • client makes request to proxy, proxy makes request to object
  • clients may or may not know if they are using a proxy
  • the proxy controls access to the original object (acts as a guardian)
  • often used for performance reasons
    • if the object needs to make expensive HTTP requests (or others), we make batch requests in the proxy in order to send fewer requests to the object
    • proxy can include cashe to store requests for further use
  • Proxies can also be used to delay the instantiation of a slow or resourse intensive object, when it is better for performance to delay
    • we might need the object at some time
    • this is called a Just-In-Time Proxy, which is what we will be making
    • first time that proxy invokes methods of objects will be slow, but subsequent times will be fast as object has already been initialized
    • great thing about this is even if client initializes proxy, we may not need the slow object unless a method on the slowObject is needed

Step 1 – Page: slowObject.js


// JavaScript Document
define(function () {
		'use strict';
		
		var SlowObject = function () { //set up slowObject
			this.someMethod = function () {
				console.log('some method on the slow object was invoked');	//use method to log a statement
			};
		};
		
		return {
			init: function () { //add artificial initialization delay with for loop
				for (var x = 0, max = 1000; x < max; x++) { //for loop iterates 1000 times
					console.log('slowness...');
				}
				return new SlowObject();//once loop finishes, returns new instance of slowObject
			}
		};
});

Step 2 - Page: slowObjectProxy.js


// JavaScript Document
define(function (require) {//use require function
		'use strict';
		
		var SlowObjectProxy, slowObjectInstance, //initialize variables
			slowObject = require('proxy/slowObject');
			
		SlowObjectProxy = function () { //set up constructor for proxy object
			this.someMethod = function () { //add method that matches the slowObject method
				var interval; //add the new variable
				
				if (!slowObjectInstance) { //check if the slowObject has been initialized previously
					slowObjectInstance = slowObject.init();	//if return is falsy, we haven't initialized yet, so we use init method to initialize
				} else { //if it has been initialized
					slowObjectInstance.someMethod();	//we invoke the same method on the slowObject
				}
				
				interval = window.setInterval(invokeMethodWhenExists, 100); //set this up to poll the object and wait for initizialization.
				
				function invokeMethodWhenExists() { //the above interval will invoke this method every 100 seconds in order to check if slowObject has been created yet
					if (slowObjectInstance) { //we check the slowObjectIstance variable, and if truthy
						console.log('proxying some method'); //use a log to know the proxy is working as expected
						window.clearInterval(interval); //clear the interval to stop the loop
						
						slowObjectInstance.someMethod(); //then invoke the method on the slow object
					}
				}
			};
		};
		
		return {
			init: function () {
				return new SlowObjectProxy();	
			}
		};
});

Step 3 - Page: init.js


define(function(require) {
	'use strict';

	return {
		init: function() {
			
			var myProxy, //set up variables
				slowObjectProxy = require('proxy/slowObjectProxy');
				
				myProxy = slowObjectProxy.init(); //initialize the slowObject, which delayed by the interval
				myProxy.someMethod();
		}
	};
});

Step 4 - Page: main.js


// JavaScript Document
require (
	['proxy/init'],
	function ( proxy) {
			'use strict';
	
			var examples = {
					
					proxy:proxy
				};
			
			window.runExample = function (example) {
				examples[example].init();
			};
	}
);

Conclusion

The proxy pattern can be used to control access another object, either because it takes a long time to initialize or it is an expensive operation to carry out

  • proxy exposes same interface as the original object and sits between client and object, intercepting requests and handling on behalf of the object.