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.

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).


Up: Interoperable Polymorphism [Contents]

Part of the GNU Project

Copyright © 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.

"GNU Inside!" Page Fold licensed under CC-BY-SA 2.0; incorporates "A Big GNU Head".

The source code of this website is available in the website branch of the Git repository.

Authored by Mike Gerwitz

AltStyle によって変換されたページ (->オリジナル) /