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 (anew 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