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(); }
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(); }
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.
• Object Interface Compatibility: | Using vanilla ECMAScript objects where type checking is performed on GNU ease.js interfaces | |
• Building Interfaces Around Objects: | Using interfaces to validate APIs of ECMAScript objects |
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).