Document Information

Last modified:
2008/07/02 13:36 by aback

Classes

Declaration

The new class definition is a more concise and compact way to define new classes. It is also more powerful and elegant and it is expected to be easier to read and to understand.

Due to the closed form the JavaScript code that handles the actual class definition (and also the generator of the automatic build process) know all parts of the class at definition time. This opens the door for many useful runtime checks during development as well as clever optimizations during the build process.

The most basic definition of a regular class qx.test.Cat that has a constructor reads:

qx.Class.define("qx.test.Cat", {
  construct : function() { ... }
});

As you can see, the define method to define a class takes two arguments, the full qualified name of the new class, and a map that contains a varying number from a predefined set of keys and their values. Here, only the construct key is used. More keys are discussed further down.

An instance of this class is created and its constructor is called by the usual statement:

var kitty = new qx.test.Cat;

Members

Members of a class come in to flavors:

  • Class members (also called “static” members) are attached to the class itself, not to individual instances
  • Instance members that are attached to each indiviual instance of a class

Class Members

A static member of a class can be one of the following:

  • Class Variable
  • Class Method

In the Cat class we may attach a class variable LEGS (where uppercase notation is a common coding convention), and a class method makeSound() in a statics section of the class declaration:

qx.Class.define("qx.test.Cat", {
  ...
  statics : {
    LEGS: 4,
    makeSound : function() { ... }
  }
});

Accessing those class members involves the fully-qualified class name:

var foo = qx.test.Cat.LEGS;
alert(qx.test.Cat.makeSound());

Instance Members

An instance member of a class can be one of the following:

  • Instance Variable
  • Instance Method

They may be defined in the members section of the class declaration:

qx.Class.define("qx.test.Cat", {
  ...
  members: {
    name : "Kitty",
    getName: function() { return this.name }
  }
});

Accessing those members involves an instance of the class:

var kitty = new qx.test.Cat;
kitty.name = "Sweetie";
alert(kitty.getName());

Primitive Types vs. Reference Types

There is a fundamental JavaScript language feature that could lead to problems, if not properly understood. It centers around the different behavior in the assignment of JavaScript’s two data types (primitive types vs. reference types).

Please make sure you understand the following explanation to avoid possible future coding errors.

Primitive types include Boolean, Number, String, null and the rather unusual undefined. If such a primitive type is assigned to an instance variable in the class declaration, it behaves as if each instance had a copy of that value. They are never shared among instances.

Reference types include all other types, e.g. Array, Function, RegExp and the generic Object. As their name suggests, those reference types merely point to the corresponding data value, which is represented by a more complex data structure than the primitive types. If such a reference type is assigned to an instance variable in the class declaration, it behaves as if each instance just pointed to the complex data structure. All instances share the same value, unless the corresponding instance variable is assigned a different value.

Example: If an instance variable was assigned an array in the class declaration, any instance of the class could (knowingly or unknowingly) manipulate this array in such a way that each instance would be affected by the changes. Such a manipulation could be pushing a new item into the array or changing the value of a certain array item. All instances would share the array.

You have to be careful when using complex data types in the class declaration, because they are shared by default:

members:
{
  foo: [1, 2, 4]   // all instances would start to share this data structure
}

If you do not want that instances share the same data, you should defer the actual initialization into the constructor:

construct: function()
{
  this.foo = [1, 2, 4];   // each instance would get assigned its own data structure
},
members:
{
  foo: null   // to be initialized in the constructor
}

Access

In many object-oriented classes a concept exists that is referred to as “access” or “visibility” of members (well, or even classes, etc.). Based on the well-known access modifiers of Java, the following three types exist for qooxdoo members:

  • public: To be accessed from any class/instance
  • protected: To be accessed only from derived classes or their instances
  • private: To be accessed only from the defining class/instance

Unfortunately, JavaScript is very limited in enforcing those protection mechanism. Therefore, the following coding convention is supposed to be used to declare the access type of members:

  • public: members may not start with an underscore
  • protected: members start with a single underscore _
  • private: members start with a double underscore __

There are possibilities to enforce or at least check the various degrees of accessibility:

  • rename private members in the build version
  • check instance of this in protected methods
  • ...

Special Types of Classes

Besides a “regular” class there is built-in support for the follwing special types:

Static Classes

A static class is not instantiated and only contains static members. Setting its type to static makes sure only such static members, no constructor and so on are given in the class definition. Otherwise error messages are presented to the developer:

qx.Class.define("qx.test.Cat", {
  type : "static"
  ...
});

Abstract Classes

An abstract class may not be instantiated. It merely serves as a superclass that needs to be derived from. Concrete classes (or concrete members of such derived classes) contain the actual implementation of the abstract members. If an abstract class is to be instantiated, an error message is presented to the developer.

qx.Class.define("qx.test.Cat", {
  type : "abstract"
  ...
});

Singletons

The singleton design pattern makes sure, only a single instance of a class may be created. Every time an instance is requested, either the already created instance is returned or, if no instance is available yet, a new one is created and returned. Requesting the instance of such a singleton class is done by using the getInstance() method.

qx.Class.define("qx.test.Cat", {
  type : "singleton"
  ...
});

Inheritance

Single Inheritance

JavaScript supports the concept of single inheritance. It does not support (true) multiple inheritance like C++. Most people agree on the fact that such a concept tends to be very complex and error-prone. There are other ways to shoot you in the foot. qooxdoo only allows for single inheritance as well:

qx.Class.define("qx.test.Cat", {
  extend: qx.test.Animal
});

Multiple Inheritance

Not supported. There are more practical and less error-prone solutions that allow for typical features of multiple inheritance: Interfaces and Mixins (see below).

Polymorphism (Overriding)

qooxdoo does, of course, allow for polymorphism, that is most easily seen in the ability to override methods in derived classes.

Calling the Superclass Constructor

It is hard to come up with an appealing syntax and efficient implementation for calling the superclass constructor from the constructor of a derived class. You simply cannot top Java’s super() here. At least there is some generic way that does not involve to use the superclass name explicitly:

qx.Class.define("qx.test.Cat", {
  extend: qx.test.Animal,
  construct: function(x) {
    this.base(arguments, x);
  }
});

this.base(arguments, x) is internally mapped to arguments.callee.call(this, x). The latter form can be handled by JavaScript natively, which means it is very efficient. Therefore it is planned to convert the source version code into the native code while generating the build version that is to be deployed.

Calling the corresponding Superclass Method

Similar to calling the superclass constructor:

qx.Class.define("qx.test.Cat", {
  extend: qx.test.Animal,
  members: {
    makeSound : function() {
      this.base(arguments);
    }
  }
});

Calling the Superclass Method or Constructor with all parameters

This variant allows to pass all the parameters (unmodified):

qx.Class.define("qx.test.Animal", {
  members: {
    makeSound : function(howManyTimes) {
       ....
    }
  }
});
 
qx.Class.define("qx.test.Cat", {
  extend: qx.test.Animal,
  members: {
    makeSound : function() {
      this.debug("I'm a cat");
      /* howManyTimes or any other parameter are passed.  We don't need to know how many parameters are used. */
      arguments.callee.base.apply(this, arguments);
    }
  }
});

Calling an arbitrary (static) Method

Static members of the superclass (both variables and methods) can be accessed. Here is the syntax for methods:

qx.Class.define("qx.test.Cat", {
  extend: qx.test.Animal,
  statics : {
    someStaticMethod : function(x) {
      ...
    }
  },
  members: {
    makeSound : function(x) {
      this.self(arguments).someStaticMethod(x);
    }
  }
});

(The syntax for variables is simply this.self(arguments).someStaticVar).

For this.self to be available, the class must have as a derived class of qx.core.Object.

For calling a static method from a static method, you can directly use the this keyword.

Interfaces

The new class system supports Interfaces. The implementation is based on the feature set of Java interfaces. Most relevant features of Java-like interfaces are supported. A class can define which interface or multiple interfaces it implements by using the implement key:

qx.Class.define("qx.test.Cat", {
  implement : [qx.test.IPet, qx.test.IFoo]
});

Mixins

Unlike interfaces, Mixins do contain concrete implementations of methods. They borrow some ideas from Ruby.

Features:

  • Add mixins to the definition of a class: All members of the mixin are added to the class definition.
  • Add a mixin to a class after the class is defined. Enhances the functionality but is not allowed to overwrite existing members.
  • Patch existing classes. Change the implementation of existing methods. Should normally be avoided but, as some projects may need to patch qooxdoo, we better define a clean way to do so.

The concrete implementations of mixins are used in a class through the key include:

qx.Class.define("qx.test.Cat", {
  include : [qx.test.MPet, qx.test.MSleep]
});

Development Goals

The new OO capabilities in 0.7 were developed with the following goals in mind:

  • Closed form of class declaration
  • Interfaces (Java-like)
  • Mixins (Ruby-like)
  • Easy calling of super classes (constructor or methods)
  • Better concepts for private, protected and public members
  • Improved dynamic properties
  • Migration support for existing applications
  • Browser specific builds (Gecko, Mshtml, Opera, Webkit)
  • Simplified settings
  • More runtime checks for the application development phase

The previous class declaration

For a summary of the class declaration in qooxdoo 0.6.x and some of its shortcomings that are being addressed by the new declaration scheme, please see this article.

Comparison between old (0.6.x) and new (0.7) OO features

For a quick overview there is a Comparison between old and new OO features available.

Browser optimized builds

Methods can be tagged to be specific for just one browser. The class system and the generator are responsible for selecting the correct version of the method for the current browser. The generator may create optimized builds and strip out all methods, which are not needed for a given browser.

Features:

  • Runtime selection of the correct method depending on the current browser
  • Optimized builds
    • Loader script which automatically loads the correct version
    • Maybe toggle other optimizations for specific browsers as well (for example, do string optimizations only in IE)

See Variants for more details.

Simplified Settings

Settings should no longer be fully qualified by the namespace. There should be a simple way to define settings in JS and using the generator.

See Defining and using settings for more details.

More runtime checks

  • More runtime checks in the source version
  • Strip the checks from the build version
  • Remove debugging code from the build version

Class Declaration Quick Ref

Information

Last modified:
2008/07/02 13:36 by aback

Account

Not logged in

 
 

Job Offers

To further improve qooxdoo we are seeking javascript developers. Read more...

Rich Ajax Platform (RAP)

RAP uses qooxdoo, Java and the Eclipse development model to build rich web applications. Read more...

qooxdoo Web Toolkit (QWT)

Similar to GWT this framework allows to create impressive qooxdoo applications just using Java. Read more...

Pustefix

Pustefix is a MVC-based web application framework using Java and XML/XSLT. Read more...

 
SourceForge.net Logo

Bad Behavior has blocked 0 potential spam attempts in the last 7 days.