EDIT:
This shows the use of DIP in raw JavaScript and a less complete jQuery example. However, the following description can be easily applied to jQuery. See the jQuery example below.
The best way is to use the āAdapter Patternā - also called the āwrapperā.
An adapter is basically a way to wrap an object or module so that it provides its dependents with the same consistent interface . Thus, a dependent class (usually a higher-level class ) can easily replace modules of the same type.
An example of this is a high-level module (or higher), which depends on Geo / Mapping modules.
Let's analyze this. If our supra module already uses GoogleMaps, but then the manual decides that it is cheaper to go with LeafletMaps, we donāt want to rewrite every method call from gMap.showMap(user, latLong) to leaflet.render(apiSecret,latLong, user) , etc. d. It would be a nightmare to port our application from one framework to another.
What we need: we need a "wrapper" that provides the same consistent interface for the supra module - and does this for each lower-level module (or infra-module).
Here is a simple example:
var infra1 = (function(){ function alertMessage(message){ alert(message); } return { notify: alertMessage }; })(); var infra2 = (function(){ function logMessage(message){ console.log(message); } return { notify: logMessage }; })(); var Supra = function(writer){ var notifier = writer; function writeMessage(msg){ notifier.notify(msg); } this.writeNotification = writeMessage; }; var supra; supra = new Supra(infra1); supra.writeNotification('This is a message'); supra = new Supra(infra2); supra.writeNotification('This is a message');
Please note that no matter what type of lower-level module we write (in this case infra1 and infra2 ), we do not need to rewrite any implementation of our high-level module, Supra . This is because DIP uses two different principles of software development: āIoCā (control inversion) and āDIā (dependency injection).
The best analogy I've come across is the image shown below.
Each electrical source relies on an interface specific to the types of things that need to be connected to it.
jQuery Description:
This template can be easily applied to the use of frameworks such as jQuery. One example would be a simple DOM-Query descriptor. We can use DIP to allow loose communication so that if we ever decide to switch frameworks or rely on our own DOM-Query methods, maintenance is easy:
var jQ = (function($){ return { getElement: $ }; })(jQuery); var nativeModule = (function(){ return { getElement: document.querySelector }; })(); var SupraDOMQuery = function(api){ var helper = api, thus = this; function queryDOM(selector){ el = helper.getElement(selector); return thus; } this.get = queryDOM; }; var DOM; DOM = new SupraDOMQuery(jQ); DOM.get('#id.class'); DOM = new SupraDOMQuery(nativeModule); DOM.get('#id.class');
Obviously this example will require more functionality to be practical, but I hope this makes sense.
In principle, the differences between the adapter and the facade become somewhat trivial. In Facade, you are probably looking at one module that wraps an API or another module; while the adapter creates a consistent Facade API for each of its modules and uses this technique to eliminate hard communication.
Most books on JavaScript design patterns follow the adapter pattern; one that specifically translates the "jQuery Adapter" is learning the JavaScript design patterns from Addy Osmani published by O'Reilly here . However, I also advise looking at the design patterns of JavaScript by Dustin Diaz and Ross Harms, published by Apress - check it out . However, I think it is important to understand the context in which we plan to implement DIP with respect to jQuery.
Hope this helps clarify the situation :)