4.3.1 Interface Interop
Interfaces, when used within the bounds of GNU ease.js, allow for
strong typing of objects. Further, two interfaces that share the same API
are not equivalent; this permits conveying intent: Consider two interfaces
Enemy and Toad, each defining a method croak. The
method for Enemy results in its death, whereas the method for
Toad produces a bellowing call. Clearly classes implementing these
interfaces will have different actions associated with them; we would
probably not want an invincible enemy that croaks like a toad any time you
try to kill it (although that’d make for amusing gameplay).
var Enemy = Interface( { croak: [] } ),
Toad = Interface( { croak: [] } ),
AnEnemy = Class.implement( Enemy ).extend( /*...*/ ),
AToad = Class.implement( Toad ).extend( /*...*/ );
// GNU ease.js does not consider these interfaces to be equivalent
Class.isA( Enemy, AnEnemy() ); // true
Class.isA( Toad, AnEnemy() ); // false
Class.isA( Enemy, AToad() ); // false
Class.isA( Toad, AToad() ); // true
defeatEnemy( AnEnemy() ); // okay; is an enemy
defeatEnemy( AToad() ); // error; is a toad
function defeatEnemy( enemy )
{
if ( !( Class.isA( Enemy, enemy ) ) ) {
throw TypeError( "Expecting enemy" );
}
enemy.croak();
}
figure 4.1: Croak like an enemy or a toad?
In JavaScript, it is common convention to instead use duck typing,
which does not care what the intent of the interface is—it merely cares
whether the method being invoked actually exists.14 So, in the case of the
above example, it is not a problem that an toad may be used in place of an
enemy—they both implement croak and so something will
happen. This is most often exemplified by the use of object literals to
create ad-hoc instances of sorts:
var enemy = { croak: function() { /* ... */ ) },
toad = { croak: function() { /* ... */ ) };
defeatEnemy( enemy ); // okay; duck typing
defeatEnemy( toad ); // okay; duck typing
// TypeError: object has no method 'croak'
defeatEnemy( { moo: function() { /*...*/ } } );
function defeatEnemy( enemy )
{
enemy.croak();
}
figure 4.2: Duck typing with object literals
Duck typing has the benefit of being ad-hoc and concise, but places the onus on the developer to realize the interface and ensure that it is properly implemented. Therefore, there are two situations to address for GNU ease.js users that prefer strongly typed interfaces:
- Ensure that non-ease.js users can create objects acceptable to the strongly-typed API; and
- Allow ease.js classes to require a strong API for existing objects.
These two are closely related and rely on the same underlying concepts.
Footnotes
(14)
“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.” (James Whitcomb Riley).