@c This document is part of the ease.js manual @c Copyright (c) 2011 Mike Gerwitz @c Permission is granted to copy, distribute and/or modify this document @c under the terms of the GNU Free Documentation License, Version 1.3 @c or any later version published by the Free Software Foundation; @c with no Invariant Sections, no Front-Cover Texts, and no Back-Cover @c Texts. A copy of the license is included in the section entitled ``GNU @c Free Documentation License''. @node Member Keywords @chapter Member Keywords Keywords are defined within the context of the @dfn{definition object} (@pxref{dfnobj,,Definition Object}). In the sections that follow, let @var{C} denote a class that contains the definition object @var{dfn}, which in turn contains @var{keywords} within the declaration of method @var{name}, whose definition is denoted by @var{value}. The table below summarizes the available keywords accepted by @var{keywords}. @float Table, t:keywords @multitable @columnfractions .10 .90 @headitem Keyword @tab Description @item @code{public} @tab Places member @var{name} into the public API for @var{C} (@pxref{Access Modifiers}). @item @code{protected} @tab Places member @var{name} into the protected API for @var{C} (@pxref{Access Modifiers}). @item @code{private} @tab Places member @var{name} into the private API for @var{C} (@pxref{Access Modifiers}). @item @code{static} @tab Binds member @var{name} to class @var{C} rather than instance of @var{C}. Member data shared with each instance of type @var{C}. @xref{Static Members}. @item @code{abstract} @tab Declares member @var{name} and defers definition to subtype. @var{value} is interpreted as an argument list and must be of type @code{array}. May only be used with methods. Member @var{name} must be part of @var{dfn} of either an @code{Interface} or @code{AbstractClass}. @xref{Abstract Members}. @item @code{const} @tab Defines an immutable property @var{name}. May not be used with methods or getters/setters. @xref{Constants}. @item @code{virtual} @tab Declares that method @var{name} may be overridden by subtypes. Methods without this keyword may not be overridden. May only be used with methods. @xref{Inheritance}. @item @code{override} @tab Overrides method @var{name} of supertype of @var{C} with @var{value}. May only override virtual methods. May only be used with methods. @xref{Inheritance}. @end multitable @caption{Supported keywords} @end float Not all keywords are supported by each member and some keywords conflict with each other. More information can be found in the appropriate sections as mentioned above in @ref{t:keywords}. @menu * Access Modifiers:: Control the context in which members may be accessed @end menu @node Access Modifiers @section Access Modifiers @dfn{Access modifiers}, when provided in @var{keywords}, alter the interface into which the definition of member @var{name} is placed. There are three interfaces, or levels of @dfn{visibility}, that dictate the context from which a member may be accessed, listed here from the most permissive to the least: @table @dfn @item public Accessible outside of @var{C} or any instance of @var{C} (e.g. @samp{foo.publicProp}). @ref{Inheritance,,Inherited} by subtypes of @var{C}. @item protected Not accessible outside of @var{C} or an instance of @var{C} (e.g. @samp{this.protectedProp} within context of @var{C}). @ref{Inheritance,,Inherited} by subtypes of @var{C}. @item private Not accessible outside of @var{C} or any instance of @var{C}. @emph{Not} @ref{Inheritance,,inherited} by subtypes of @var{C}. @end table @float Table, t:access-modifiers @multitable @columnfractions .10 .90 @headitem Keyword @tab Description @item @code{public} @tab Places member @var{name} in public interface (accessible outside of @var{C} or instance of @var{C}; accessible by subtypes). Implied if no other access modifier is provided. @item @code{protected} @tab Places member @var{name} in protected interface (accessible only within @var{C} or instance of @var{C}; accessible by subtypes). @item @code{private} @tab Places member @var{name} in private interface (accessible only within @var{C} or instance of @var{C}; not accessible by subtypes). @end multitable @caption{Access modifiers} @end float Access modifiers have the following properties: @itemize @item Only one access modifier may appear in @var{keywords} for any given @var{name}. @item If no access modifier is provided in @var{keywords} for any member @var{name}, member @var{name} is implicitly @code{public}. @end itemize @menu * Discussion: Access Modifiers Discussion. Uses and rationale * Example: Access Modifiers Example. Demonstrating access modifiers @end menu @node Access Modifiers Discussion @subsection Discussion One of the major hurdles ease.js aimed to address (indeed, one of the core reasons for its creation) was that of encapsulation. JavaScript's prototypal model provides limited means of encapsulating data. Since functions limit scope, they may be used to mimic private members; these are often referred to as @dfn{privileged members}. However, declaring classes in this manner tends be messy, which has the consequence of increasing maintenance costs and reducing the benefit of the implementation. ease.js aims to provide an elegant implementation that is both a pleasure to work with and able to support protected members. By default, all members are public. This means that the members can be accessed and modified from within an instance as well as from outside of it. Subtypes (classes that inherit from it; @pxref{Inheritance}) will inherit public members. Public methods expose an API by which users may use your class. Public properties, however, should be less common in practice for a very important reason, which is explored throughout the remainder of this section. @subsubsection Encapsulation @dfn{Encapsulation} is the act of hiding information within a class or instance. Classes should be thought of black boxes; we want them to do their job, but we should not concern ourselves with @emph{how} they do their job. Encapsulation takes a great deal of complexity out of an implementation and allows the developer to focus on accomplishing the task by focusing on the implementing in terms of the problem domain. For example - consider a class named @var{Dog} which has a method @code{walk()}. To walk a dog, we simply call @code{Dog().walk()}. The @code{walk()} method could be doing anything. In the case of a real dog, perhaps it will send a message to the dog's brain, perform the necessary processing to determine how that command should be handled and communicate the result to the limbs. The limbs will communicate back the information they receive from their nerves, which will be processed by the brain to determine when they hit the ground, thereby triggering additional actions and the further movement of the other legs. This could be a terribly complicated implementation if we had to worry about how all of this was done. In addition to the actual walking algorithm, we have the state of each of the legs - their current position, their velocity, the state of each of the muscles, etc. This state pertains only to the operations performed by the dog. Exposing this state to everyone wouldn't be terribly useful. Indeed, if this information was exposed, it would complicate the implementation. What if someone decided to alter this state in the middle of a walking operation? Or what if the developer implementing @var{Dog} relied on this state in order to determine when the leg reached a certain position, but later versions of @var{Dog} decided to alter the algorithm, thereby changing those properties? By preventing these details from being exposed, we present the developer with a very simple interface@footnote{One would argue that this isn't necessary a good thing. What if additional flexibility was needed? @var{Dog}, in the sense of this example, can be thought of as a Facade (GoF). One could provide more flexibility by composing @var{Dog} of, say, @var{Leg} instances, a @var{Brain}, etc. However, encapsulation still remains a factor. Each of those components would encapsulate their own data.}. Rather than the developer having to be concerned with moving each of the dog's legs, all they have to do is understand that the dog is being walked. When developing your classes, the following best practices should be kept in mind: @itemize @item When attempting to determine the best access modifier (@pxref{Access Modifiers}) to use for a member, start with the least level of visibility (@code{private}) and work your way up if necessary. @item If your member is not private, be sure that you can justify your choice. @itemize @item If protected - why do subclasses need access to that data? Is there a better way to accomplish the same task without breaking encapsulation? @item If public - is this member necessary to use the class externally? In the case of a method - does it make sense to be part of a public API? If a property - why is that data not encapsulated? Should you consider an accessor method? @end itemize @end itemize @node Access Modifiers Example @subsection Example Let's consider our @var{Dog} class in more detail. We will not go so far as to implement an entire nervous system in our example. Instead, let's think of our @var{Dog} similar to a wind-up toy: @float Figure, f:encapsulation @verbatim Class( 'Dog', { 'private _legs': {}, 'private _body': {}, // ... 'public walk': function() { this.stand(); this._moveFrontLeg( 0 ); this._moveBackLeg( 1 ); this._moveFrontLeg( 1 ); this._moveBackLeg( 0 ); }, 'protected stand': function() { if ( this.isSitting() ) { // ... } }, 'public rollOver': function() { this._body.roll(); }, 'private _moveFrontLeg': function( leg ) { this._legs.front[ leg ].move(); }, 'private _moveBackLeg': function( leg ) { this._legs.back[ leg ].move(); }, // ... } ); @end verbatim @caption{Encapsulating behavior of a class} @end float As you can see above, the act of making the dog move forward is a bit more complicated than the developer may have originally expected. The dog has four separate legs that need to be moved individually. The dog must also first stand before it can be walked, but it can only stand if it's sitting. Detailed tasks such as these occur all the time in classes, but they are hidden from the developer using the public API. The developer should not be concerned with all of the legs. Worrying about such details brings the developer outside of the problem domain and into a @emph{new} problem domain - how to get the dog to walk. @subsection Private Members Let's first explore private members. The majority of the members in the @var{Dog} class (@pxref{f:encapsulation,}) are private. This is the lowest level of visibility (and consequently the @emph{highest} level of encapsulation). By convention, we prefix private members with an underscore. Private members are available @emph{only to the class that defined it} and are not available outside the class. @float Figure, f:encapsulation-call-priv @verbatim var dog = Dog(); dog._moveFrontLeg( 1 ); // TypeError: Object # has no method '_moveFrontLeg' @end verbatim @caption{Cannot access private members outside the class} @end float You will notice that the dog's legs are declared private as well (@pxref{f:encapsulation,}). This is to ensure we look at the dog as a whole; we don't care about what the dog is made up of. Legs, fur, tail, teeth, tongue, etc - they are all irrelevant to our purpose. We just want to walk the dog. Encapsulating those details also ensures that they will not be tampered with, which will keep the dog in a consistent, predictable state. Private members cannot be inherited. Let's say we want to make a class called @var{TwoLeggedDog} to represent a dog that was trained to walk only on two feet. We could approach this in a couple different ways. The first way would be to prevent the front legs from moving. What happens when we explore that approach: @float Figure, f:encapsulation-inherit-priv @verbatim var two_legged_dog = Class( 'TwoLeggedDog' ).extend( Dog, { /** * This won't override the parent method. */ 'private _moveFrontLeg': function( leg ) { // don't do anything return; }, } )(); two_legged_dog.walk(); @end verbatim @caption{Cannot override private members of supertype} @end float If you were to attempt to walk a @var{TwoLeggedDog}, you would find that @emph{the dog's front legs still move}! This is because, as mentioned before, private methods are not inherited. Rather than overriding the parent's @var{_moveFrontLeg} method, you are instead @emph{defining a new method}, with the name @var{_moveFrontLeg}. The old method will still be called. Instead, we would have to override the public @var{walk} method to prevent our dog from moving his front feet. @subsection Protected Members Protected members are often misunderstood. Many developers will declare all of their members as either public or protected under the misconception that they may as well allow subclasses to override whatever functionality they want. This makes the class more flexible. While it is true that the class becomes more flexible to work with for subtypes, this is a dangerous practice. In fact, doing so @emph{violates encapsulation}. Let's reconsider the levels of visibility in this manner: @table @strong @item public Provides an API for @emph{users of the class}. @item protected Provides an API for @emph{subclasses}. @item private Provides an API for @emph{the class itself}. @end table Just as we want to hide data from the public API, we want to do the same for subtypes. If we simply expose all members to any subclass that comes by, that acts as a peephole in our black box. We don't want people spying into our internals. Subtypes shouldn't care about the dog's implementation either. Private members should be used whenever possible, unless you are looking to provide subtypes with the ability to access or override methods. In that case, we can move up to try protected members. Remember not to make a member public unless you wish it to be accessible to the entire world. @var{Dog} (@pxref{f:encapsulation,}) defined a single method as protected - @code{stand()}. Because the method is protected, it can be inherited by subtypes. Since it is inherited, it may also be overridden. Let's define another subtype, @var{LazyDog}, which refuses to stand. @float Figure, f:encapsulation-inherit-prot @verbatim var lazy_dog = Class( 'LazyDog' ).extend( Dog, { /** * Overrides parent method */ 'protected stand': function() { // nope! this.rollOver(); return false; }, } )(); lazy_dog.walk(); @end verbatim @caption{Protected members are inherited by subtypes} @end float There are a couple important things to be noted from the above example. Firstly, we are able to override the @code{walk()} method, because it was inherited. Secondly, since @code{rollOver()} was also inherited from the parent, we are able to call that method, resulting in an upside-down dog that refuses to stand up, just moving his feet. Another important detail to notice is that @code{Dog.rollOver()} accesses a private property of @var{Dog} -- @var{_body}. Our subclass does not have access to that variable. Since it is private, it was not inherited. However, since the @code{rollOver()} method is called within the context of the @var{Dog} class, the @emph{method} has access to the private member, allowing our dog to successfully roll over. If, on the other hand, we were to override @code{rollOver()}, our code would @emph{not} have access to that private object. Calling @samp{this.__super()} from within the overridden method would, however, call the parent method, which would again have access to its parent's private members.