Up: Interoperable Polymorphism   [Contents]


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:

  1. Ensure that non-ease.js users can create objects acceptable to the strongly-typed API; and
  2. 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).


Up: Interoperable Polymorphism   [Contents]