Table of Contents
- Class definition
- Inheritance
- Constructor
- Static members
- Instance Members
- Accessing Static Members
- Calling the Superclass Constructor
- Calling the Overridden Superclass Method
- Calling the Overridden Superclass Method (passing all arguments)
- Destructor
- Interfaces
- Mixins
- Attaching mixins to a class
- Attaching mixins to an already defined class
- Access
- Static classes
- Abstract classes
- Singletons
- Immediate access to previously defined members
- Browser specific methods
- Events
Features for Object Orientation
Class definition
A class is defined by providing its name as a string:
qx.Class.define("my.cool.Class");
This example only creates a trivial class my.cool.Class. A typical class declaration contains OO features like constructor, instance members, static members, etc. This additional information is provided as a second parameter in form of a map. Since the entire class definition is given in qx.Class.define(), it is called a “closed form” of class declaration:
qx.Class.define("my.cool.Class", { // declare constructor, members, ... });
A regular (non-static) class can simply be instantiated using the new keyword:
var myClass = new my.cool.Class;
Inheritance
In order to derive the current class from another class, the reference to the super class is provided by the key extend:
qx.Class.define("my.great.SuperClass", { // I'm the super class }); qx.Class.define("my.cool.Class", { extend : my.great.SuperClass });
Constructor
The constructor of a regular class is provided as a function declaration in key construct:
qx.Class.define("my.cool.Class", { extend : my.great.SuperClass, construct : function() { ... } });
Static members
Static members (often called “class” members) are also part of the class definition and declared in a map to the statics key. Static methods are given by providing a function declaration, while all other values declare static attributes. Typically they are given in uppercase to distinguish them from instance members:
qx.Class.define("my.cool.Class", { statics : { FOO : VALUE, BAR : function() { ... } } });
Static members, both methods and attributes, can be accessed by using the fully-qualified class name:
my.cool.Class.FOO = 3.141; my.cool.Class.BAR();
Instance Members
Similar to static members, instance members are also part of the class definition. For them the members key is used:
qx.Class.define("my.cool.Class", { members: { foo : VALUE, bar : function() { ... } } });
The instance members can be accessed by using an actual instance of a class:
var myClass1 = new my.cool.Class; myClass1.foo = 3.141; myClass1.bar();
Accessing Static Members
Generic form. Requires no updates if class name changes. This code can optionally be optimized for performance in build versions.
qx.Class.define("my.cool.Class", { statics : { PI : 3.141 } members : { circumference : function(radius) { return 2 * this.self(arguments).PI * radius; } } });
this.self to be available, the class must have as a direct or indirect base class qx.core.Object.
this.superclass, like in this example:
qx.Class.define('A', {
statics: {
f: function() {}
}
});
qx.Class.define('B'), {
extend: A,
members: {
e: function() {
this.superclass.self(arguments).f();
}
}
});
Static functions can access other static functions directly through the this keyword.
Calling the Superclass Constructor
Generic form. Requires no updates if super class (name) changes. This code can optionally be optimized for performance in build versions.
qx.Class.define("my.cool.Class", { extend : my.great.SuperClass, construct : function(x) { this.base(arguments, x); } });
Calling the Overridden Superclass Method
Generic form without using prototype. Requires no updates if super class (name) changes. This code can optionally be optimized for performance in build versions.
qx.Class.define("my.cool.Class", { extend : my.great.SuperClass, ... members : { foo : function(x) { this.base(arguments, x); } } });
Calling the Overridden Superclass Method (passing all arguments)
Generic form without using prototype. Requires no updates if super class method changes. If the signature of a method is changed later by e.g. adding more parameters, all overriden methods don’t have to be updated, provided the base method is called with unmodified parameters. This code can optionally be optimized for performance in build versions.
qx.Class.define("my.cool.Class", { extend : my.great.SuperClass, ... members : { foo : function(x) { arguments.callee.base.apply(this, arguments); } } });
Destructor
As a logical match to any existing constructor given by the key construct, a destructor is explicitely given by the destruct key:
qx.Class.define("my.cool.Class", { extend : my.great.SuperClass, construct : function() { ... } destruct : function() { ... } });
Interfaces
Leading uppercase I as a naming convention.
qx.Interface.define("my.cool.IInterface");
Mixins
Leading uppercase M as a naming convention. A mixin can have all the things a class can have: properties, destructor, constructor and members.
qx.Mixin.define("my.cool.MMixin");
Attaching mixins to a class
The include key contains either a reference to an single mixin, or an array of multiple mixins:
qx.Class.define("my.cool.Class", { include : [my.cool.MMixin, my.other.cool.MMixin] ... });
Attaching mixins to an already defined class
qx.Class.include(qx.ui.core.Widget, qx.MWidgetExtensions);
Access
By the following naming convention. Goal is to be as consistent as possible. During the build process private members can optionally be renamed to random names in order to ensure that they cannot be called from outside the class.
publicMember _protectedMember __privateMember
Static classes
Explicit declaration allows for useful checks during development. For example. construct or members are not allowed for such a purely static class.
qx.Class.define("my.cool.Class", { type : "static" });
Abstract classes
Declaration allows for useful checks during development and does not require explicit code.
qx.Class.define("my.cool.Class", { type : "abstract" });
Singletons
Declaration allows for useful checks during development and does not require explicit code. A method getInstance() is added to such a singleton class.
qx.Class.define("my.cool.Class", { type : "singleton", extend : my.great.SuperClass });
Immediate access to previously defined members
The closed form of the class definition does not allow immediate access to other members, as they are part of the configuration data structure themselves. While it is typically not a feature used very often, it nonetheless needs to be supported by the new class declaration. Instead of some trailing code outside the closed form of the class declaration, an optional defer method is called after the other parts of the class definition have been finished. It allows access to all previously declared statics, members and dynamic properties.
defer should *not* be used in the class definition. It is missing some important capabilities compared to the regular members definition and it cannot take advantage of many crucial features of the build process (documentation, optimization, etc.).
qx.Class.define("my.cool.Class", { statics: { driveLetter : "C" }, defer: function(statics, members, properties) { statics.drive = statics.driveLetter + ":\\"; members.whatsTheDrive = function() { return "Drive is " + statics.drive; }; } });
Browser specific methods
To maintain the closed form, browser switches on method level is done using variants. Since the generator knows about variants it is (optionally) possible to only keep the code for each specific browser and remove the implementation for all other browsers from the code and thus generate highly-optimized browser-specific builds. It is possible to use an logical “or” directly inside a variant key. If none of the keys matches the variant, the “default” key is used:
members: { foo: qx.core.Variant.select("qx.bom.client.Engine.NAME", { "mshtml|opera": function() { // Internet Explorer or Opera }, "default": function() { // All other browsers } }) }
Events
qooxdoo’s class definition has a special events key. The value of the key is a map, which maps each distinct event name to the name of the event class whose instances are passed to the event listeners. The event system can now (optionally) check whether an event type is supported by the class and issue a warning if an event type is unknown. This ensures that each supported event must be listed in the event map.
qx.Class.define("qx.come.Class", { extend: qx.core.Target, events : { /** Fired when the widget is clicked. */ "click": "qx.event.type.MouseEvent" } ... })
