Next: , Up: Access Modifiers


3.1.1 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 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; see 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.

Following common conventions in modern object-oriented languages, members with an underscore prefix (e.g. _foo) are implicitly private; this behavior can be overridden by explicitly specifying an access modifier. This convention allows for more concise member definitions and is more natural to those who use JavaScript's native prototype model.

3.1.1.1 Encapsulation

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 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 Dog which has a method walk(). To walk a dog, we simply call Dog().walk(). The 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 Dog relied on this state in order to determine when the leg reached a certain position, but later versions of 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 interface1. 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:


Footnotes

[1] One would argue that this isn't necessary a good thing. What if additional flexibility was needed? Dog, in the sense of this example, can be thought of as a Facade (GoF). One could provide more flexibility by composing Dog of, say, Leg instances, a Brain, etc. However, encapsulation still remains a factor. Each of those components would encapsulate their own data.