1
0
Fork 0

Added 'Type Checks and Polymorphism' subsection to manual

closure/master
Mike Gerwitz 2011-03-21 00:04:44 -04:00
parent 7bbe44adc3
commit 824185c3ff
3 changed files with 114 additions and 0 deletions

View File

@ -392,6 +392,8 @@ conciseness.
@menu @menu
* Understanding Member Inheritance:: How to work with inherited members * Understanding Member Inheritance:: How to work with inherited members
* Type Checks and Polymorphism:: Substituting similar classes for
one-another
* Overriding Methods:: Overriding inherited methods * Overriding Methods:: Overriding inherited methods
@end menu @end menu
@ -457,6 +459,113 @@ inherited from @var{Dog}. If we actually run the example, you will notice that
the dog does indeed bark, showing that we are able to call our parent's method the dog does indeed bark, showing that we are able to call our parent's method
even though we did not define it ourselves. even though we did not define it ourselves.
@node Type Checks and Polymorphism
@subsection Type Checks and Polymorphism
The fact that the API of the parent is inherited is a very important detail. If
the API of subtypes is guaranteed to be @emph{at least} that of the parent, then
this means that a function expecting a certain type can also work with any
subtypes. This concept is referred to as @dfn{polymorphism}, and is a very
powerful aspect of Object-Oriented programming.
Let's consider a dog trainer. A dog trainer can generally train any type of dog
(technicalities aside), so it would stand to reason that we would want our dog
trainer to be able to train @var{LazyDog}, @var{AngryDog}, @var{TwoLeggedDog},
or any other type of @var{Dog} that we may throw at him/her.
@float Figure, f:polymorphism-uml
@image{img/composition-uml}
@caption{Class structure to demonstrate polymorphism}
@end float
Type checks are traditionally performed in JavaScript using the
@code{instanceOf} operator. While this can be used in most inheritance cases
with ease.js, it is not recommended. Rather, you are encouraged to use ease.js's
own methods for determining instance type@footnote{The reason for this will
become clear in future chapters. ease.js's own methods permit checking for
additional types, such as Interfaces.}. Support for the @code{instanceOf}
operator is not guaranteed.
Instead, you have two choices with ease.js:
@table @code
@item Class.isInstanceOf( type, instance );
Returns @code{true} if @var{instance} is of type @var{type}. Otherwise, returns
@code{false}.
@item Class.isA( type, instance );
Alias for @code{Class.isInstanceOf()}. Permits code that may read better
depending on circumstance and helps to convey the ``is a'' relationship that
inheritance creates.
@end table
For example:
@float Figure, f:instanceof-ex
@verbatim
var dog = Dog()
lazy = LazyDog(),
angry = AngryDog();
Class.isInstanceOf( Dog, dog ); // true
Class.isA( Dog, dog ); // true
Class.isA( LazyDog, dog ); // false
Class.isA( Dog, lazy ); // true
Class.isA( Dog, angry ); // true
// we must check an instance
Class.isA( Dog, LazyDog ); // false; instance expected, class given
@end verbatim
@caption{Using ease.js to determine instance type}
@end float
It is important to note that, as demonstrated in @ref{f:instanceof-ex} above, an
@emph{instance} must be passed as a second argument, not a class.
Using this method, we can ensure that the @var{DogTrainer} may only be used with
an instance of @var{Dog}. It doesn't matter what instance of @var{Dog} - be it a
@var{LazyDog} or otherwise. All that matters is that we are given a @var{Dog}.
@float Figure, f:polymorphism-easejs
@verbatim
var DogTrainer = Class( 'DogTrainer',
{
'public __construct': function( dog )
{
// ensure that we are given an instance of Dog
if ( Class.isA( Dog, dog ) === false )
{
throw Error( "Expected instance of Dog" );
}
}
} );
// these are all fine
DogTrainer( Dog() );
DogTrainer( LazyDog() );
DogTrainer( AngryDog() );
DogTrainer( TwoLeggedDog() );
// this is not fine; we're passing the class itself
DogTrainer( LazyDog );
// nor is this fine, as it is not a dog
DogTrainer( {} );
@end verbatim
@caption{Polymorphism in ease.js}
@end float
It is very important that you use @emph{only} the API of the type that you are
expecting. For example, only @var{LazyDog} and @var{AngryDog} implement a
@code{poke()} method. It is @emph{not} a part of @var{Dog}'s API. Therefore, it
should not be used in the @var{DogTrainer} class. Instead, if you wished to use
the @code{poke()} method, you should require that an instance of @var{LazyDog}
be passed in, which would also permit @var{AngryDog} (since it is a subtype of
@var{LazyDog}).
Currently, it is necessary to perform this type check yourself. In future
versions, ease.js will allow for argument type hinting/strict typing, which will
automate this check for you.
@node Overriding Methods @node Overriding Methods
@subsection Overriding Methods @subsection Overriding Methods
When a method is inherited, you have the option of either keeping the parent's When a method is inherited, you have the option of either keeping the parent's

Binary file not shown.

View File

@ -0,0 +1,5 @@
,-----------------------,
| DogTrainer | ,-------,
|-----------------------|<>-------| Dog |
| +__construct(dog:Dog) | `-------`
`-----------------------`