From 824185c3ffb73630e16e6f96de5fd378fc746039 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 21 Mar 2011 00:04:44 -0400 Subject: [PATCH] Added 'Type Checks and Polymorphism' subsection to manual --- doc/classes.texi | 109 ++++++++++++++++++++++++++++++++++++ doc/img/composition-uml.dia | Bin 0 -> 1487 bytes doc/img/composition-uml.txt | 5 ++ 3 files changed, 114 insertions(+) create mode 100644 doc/img/composition-uml.dia create mode 100644 doc/img/composition-uml.txt diff --git a/doc/classes.texi b/doc/classes.texi index b16bbc8..1843d20 100644 --- a/doc/classes.texi +++ b/doc/classes.texi @@ -392,6 +392,8 @@ conciseness. @menu * Understanding Member Inheritance:: How to work with inherited members +* Type Checks and Polymorphism:: Substituting similar classes for + one-another * Overriding Methods:: Overriding inherited methods @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 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 @subsection Overriding Methods When a method is inherited, you have the option of either keeping the parent's diff --git a/doc/img/composition-uml.dia b/doc/img/composition-uml.dia new file mode 100644 index 0000000000000000000000000000000000000000..1afd2fa81977d5b7c81e75eefd4fcadacf46f8ce GIT binary patch literal 1487 zcmV;=1u*&_iwFP!000021MOQ$lcP2izW1-7=&KupZ490s_f%$5sT?LXmzgsqK*mZ7 ziAbV*e93Q*SiGbJ-zo~fBf__#L63F98t1X4NX@SNIdEz60Fr9 z-+z0u)lV-EAAJNq$e)0LP?0kTPhPFntq{?N>Gb{mUBf#L1Z5gVvBu%_Z-6nF%0W~0 zMHDyjCAK>7`C{G1G#a zGqNKPK$k)A+ALa^*J{;AbOc#hY#33FD|DwgyQ&YUsR%=s$0mWFN4^_ex_`=vXads{esTR3)GIK-WZGQj{6 zRY;do3<1ed1&d*S9PR-um)JS8jQ-L$NC?`Z{{~=wi2>Q|!;b5^djp32^P9IYqCv_# z^2OHqIg-0F>AR7nzafrX4C}Ncl71{S$g3 zH}=OLUzI223kIC4$09_QAjn^<-}IuV%C<>KG6_~(716_b(lukEYr0uBznVOn6W#$;FGa@wl!;RPGqj{2MRF^9gi|nEflV1Sc;J*ld5t1L}F{E zy;z-G#sI7>M8Q@xgUz)0s(AGSE0s=#!s%BUe3!>1X(*n$z;IcCL7J@KCa}Q}a)XAD zh|%N&SI(7NvKL zXa>DA5RGq5iUKxre>&b4R5Iuxe=hWX@^ddavL=QOh9sz_&|@)U4Z{#12#TfmRRfZm z&~gaKV@5At#sIKlQPt3S>pdg0K^V6q6lsHQ2m<~T>{zVq1D8n+*39nM0mGW zLrN%*fG4XgE_O-DB`c;(#1|Sf1X(}){h`oD1KTdC@bsBt|C@`^Uwt=oMDZ@9EZQQk zmqulU1}WnuHEQy2yDA*W{C3dd5Srg1euDyeAoa|^2=!LSu2*N6Z}N=QQI)DDgS0r= zJ*@?&uGyu;wz$q3y&ZC4v)&H2xbRwWjjOKnvEWV@@CB}1mYS7iPa}_276d?Z~DO93@ELm5k_KH2M0+U=BwwF zvgr;}?F+{oS38JD;f`0A+jTYP-nS48|K(8m1L z3RcTEw-NmHbGkKaVT6HzV|CAa`MmfLAS1W?vE9Z{w%b@}Gjk$)_7Ums1S4ghpN*wv z+Lk#nG;>s{d!SX9aNRR)CM_Axa$Ll98@J)Q?Tc}VO=L^(cxsDd0lG#?ORz*zEotN^D` zaBmU?_ZCrb$BcqIRutM<#`N>8GizdKvoSwcYiTPxfzh>v`J8ZoiH$x8bosb6P$( zud_}d7L?Jrr4Z;(vm9FCc(h*lz!+Q)!lQ&_M=`HR+`pr20rB&WTF~^9_R8^H8L?_O pYbG1{WTQCQu725|q*1w^%3i16D|mS*eDUDr;U9#|AGV-4001UK;h_Kk literal 0 HcmV?d00001 diff --git a/doc/img/composition-uml.txt b/doc/img/composition-uml.txt new file mode 100644 index 0000000..f629b6e --- /dev/null +++ b/doc/img/composition-uml.txt @@ -0,0 +1,5 @@ +,-----------------------, +| DogTrainer | ,-------, +|-----------------------|<>-------| Dog | +| +__construct(dog:Dog) | `-------` +`-----------------------`