Overwritting Bindings
A binding can be overwritten by using either Permanent Overwriting or Temporary Overwriting.
Permanent Overwriting
A binding can be permanently overwritten by any others having the same contract. The last comer wins. For example:
$.bind('my car').to(Car).use('an engine', 'a fuel tank', 'Porsche'); // manufacturer of 'my car' is Porsche.
$.bind('my car').to(Car).use('an engine', 'a fuel tank', 'BMW'); // manufacturer of 'my car' is now BMW.
// display car information in console:
console.log($('my car')); // will be: "Manufacturer: BMW"
The previous binding using Porsche will be gone. Meaning$('my car')
will always return a BMW in result.
Temporary Overwriting
This allows you to temporarily replace a binding in a particular context while still keeping the old one. The Temporary Overwriting can be done with an Identity Map.
Getting back to Getting Started, you wanted to have a car like this:
$.bind('an engine').to(Engine);
$.bind('a fuel tank').to(FuelTank);
$.bind('my car').to(Car).use('an engine', 'a fuel tank');
The statement$('my car')
will persistently give you a car with an Engine and a not-empty-FuelTank everytime it is called. Now what if you want to temporarily 'hijack' the FuelTank and replace it with an EmptyFuelTank for awhile?
function EmptyFuelTank() {
this.isEmpty = function() {
return true; // of course, it's always empty.
}
}
The replacement can be done easily. First, create an object as an Identity Map and put your EmptyFuelTank into it, then pass the map to the component resolution method:
var identityMap = { 'a fuel tank': new EmptyFuelTank() };
$('my car', identityMap).start();
The output in cosole now looks like:
Car starting...
Out of fuel. Please refuel.
Notes
Key must be the same as the contract of which you want to replace. In the sample above, it's 'a fuel tank' that was used in the first binding statement.
The subtitute must be a component (a
new EmptyFuelTank()
). Class and Method are NOT applicable.
The Identity Map also helps you retrieve resolvable dependencies from resolving a component. If you want to retrieve a dependency, reserve its contract in the map with default value to undefined. For example:
var identityMap = {
'a fuel tank': new EmptyFuelTank(), // for replacement
'an engine': undefined // reserved, to be retrieved.
};
$('my car', identityMap).start();
var engine = identityMap['an engine']; // the resolved Engine.
var fuelTank = identityMap['a fuel tank']; // the EmptyFuelTank.
Not only for replacement, the Identity Map is also useful in case of you want to share your resolved dependencies across some component resolutions. Below is an example:
// an inspector that inspects car fuel tank's condition
function FuelInspector(fuelTank) {
this.inspect = function() {
if (fuelTank.isEmpty) console.log('Warning: Bingo fuel!!!');
else console.log('Ready.');
}
}
$.bind(FuelInspector).to(FuelInspector).use('a fuel tank');
var map = { 'a fuel tank': undefined }; // just reserve the fuel tank, we don't intend to replace anything.
$('my car', map).start(); // map now contains 'a fuel tank'
// following inspector will use 'a fuel tank' encapsulated in the map
// rather than asking container to resolve the FuelTank again.
$(FuelInspector, map).inspect();
Value Binding will not be returned as part of Identity Map, based on the fact that you usually use$.value()
with pre-defined constants which are already shared among your components.
Last updated