Document Information

Last modified:
2007/08/31 18:10 by ecker

Comparison between old and new OO features

This article proposes a new class system and extended OO features. We are looking forward for any of your feedback and contribution!











Class definition

qooxdoo 0.6.x qooxdoo 0.7
qx.OO.defineClass("my.cool.Class");
qx.Class.define("my.cool.Class");

Inheritance

qooxdoo 0.6.x qooxdoo 0.7
Only implicit: Reference to super class is given as second argument of the class definition:
qx.OO.defineClass("my.cool.Class", 
  my.great.SuperClass);
Explicit: Reference to super class is given in key extend:
qx.Class.define("my.cool.Class", {
  extend : my.great.SuperClass
});

Constructor

qooxdoo 0.6.x qooxdoo 0.7
Only implicit: Constructor is given as third argument of the class definition:
qx.OO.defineClass("my.cool.Class", 
  my.great.SuperClass,
  function() {
    ...
});
Explicit: Constructor is given in key construct:
qx.Class.define("my.cool.Class", 
{
  extend : my.great.SuperClass,
  construct : function() {
    ...
  }
});

Class members

qooxdoo 0.6.x qooxdoo 0.7
Class members are given somewhere after class definition (no closed form):
qx.OO.defineClass("my.cool.Class");
 
qx.Class.foo = VALUE;
 
qx.Class.bar = function() {};
Class members are part of the class definition (closed form), given in the map statics:
qx.Class.define("my.cool.Class", 
{
  statics : 
  { 
    foo : VALUE,
    bar : function() {}
  }
});

Instance Members

qooxdoo 0.6.x qooxdoo 0.7
Instance members are given somewhere after class definition (no closed form):
qx.OO.defineClass("my.cool.Class");
 
qx.Proto.foo = VALUE;
 
qx.Proto.bar = function() {};
Instance members are part of the class definition (closed form), given in the map members:
qx.Class.define("my.cool.Class", 
{
  members: 
  { 
    foo : VALUE,
    bar : function() {}
  }
});

Accessing Static Members

qooxdoo 0.6.x qooxdoo 0.7
Need to use the fully-qualified class name. Requires manual updates if class name changes.
qx.OO.defineClass("my.cool.Class", ...);
qx.Class.PI = 3.141;
qx.Proto.circumference = function(radius) {
  return 2 * my.cool.Class.PI * radius;
};
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;
    }
  }
});

Calling the Superclass Constructor

qooxdoo 0.6.x qooxdoo 0.7
Need to repeat the super class name. Requires manual updates if super class (name) changes.
qx.OO.defineClass("my.cool.Class", 
my.great.SuperClass,
function(x) {
  my.great.SuperClass.call(this, x);
});
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

qooxdoo 0.6.x qooxdoo 0.7
Need to repeat the super class name. Requires manual updates if super class (name) changes.
qx.OO.defineClass("my.cool.Class", 
  my.great.SuperClass,
  ...
});
 
qx.Proto.foo = function(x) {
  my.great.SuperClass.prototype.foo.call(this, x);
};
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)

This construct has this advantage: 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.

qooxdoo 0.6.x qooxdoo 0.7
Need to repeat the super class name. Requires manual updates if super class (name) changes.
qx.OO.defineClass("my.cool.Class", 
  my.great.SuperClass,
  ...
});
 
qx.Proto.foo = function() {
  my.great.SuperClass.prototype.foo.apply(this, arguments);
};
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) {
      arguments.callee.base.apply(this, arguments);
    }
  }
});

Destructor

qooxdoo 0.6.x qooxdoo 0.7
Developers needed to make sure a dispose() method was supplied that dealt with memory management of complex data structures:
qx.OO.defineClass("my.cool.Class", 
  my.great.SuperClass, 
  function() {
    ...
});
qx.Proto.dispose = function()
{
    ...
});
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

qooxdoo 0.6.x qooxdoo 0.7
Not available. Leading uppercase I as a naming convention.
qx.Interface.define("my.cool.IInterface");

Attaching interfaces to a class

qooxdoo 0.6.x qooxdoo 0.7 (planned)
Not available. The implement key contains either a reference to an single interface, or an array of multiple interfaces :
qx.Class.define("my.cool.Class", 
{
  implement : [my.cool.IInterface, my.other.cool.IInterface],
  ...
});

Mixins

qooxdoo 0.6.x qooxdoo 0.7
Not available. 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

qooxdoo 0.6.x qooxdoo 0.7
Not available. 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]
  ...
});

Access

qooxdoo 0.6.x qooxdoo 0.7
Vague naming convention, rather inconsistent use. 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

qooxdoo 0.6.x qooxdoo 0.7
Not available as explicit declaration. 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

qooxdoo 0.6.x qooxdoo 0.7
Not available as explicit declaration. Required explicit coding in the constructor and adding class variables to ensure runtime checking. Declaration allows for useful checks during development and does not require explicit code.
qx.Class.define("my.cool.Class", {
  type : "abstract"
});

Singletons

qooxdoo 0.6.x qooxdoo 0.7
Not available as explicit declaration. Required explicit coding. 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

qooxdoo 0.6.x qooxdoo 0.7
Immediate access to members of the same class during declaration of new members was only inconsistently possible in 0.6.x. While it was possible, for instance, to set-up a new class variable by using a previously defined class variable, the order of declaration was crucial. On the other hand, it was not possible to easily code access to class variables from within mehtods using qx.Class:
qx.OO.defineClass("my.cool.Class", ...);
 
qx.Class.driveLetter = "C";
 
// the following works
qx.Class.drive = qx.Class.driveLetter + ":\\";
 
qx.Proto.whatsTheDrive = function() {
  // the following does _not_ work!
  // return "Drive is " + qx.Class.drive;
  // it has to be coded using the fully-qualified class name
  return "Drive is " + my.cool.Class.drive;
};
The closed form of the class definition for qooxdoo 0.7 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. If the feature of accessing previously defined members is not absolutely neccessary, the 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

qooxdoo 0.6.x qooxdoo 0.7
Methods which are browser-specific often have top-level switches to decide already at load time which implementation to use. This avoids browser checks at runtime inside the method, but is rather ugly looking and hard to document using the API documentation strings.
if (qx.core.Client.isMshtml() || qx.core.Client.isOpera())
{
  qx.Proto.foo = function() {
    // Internet Explorer or Opera
  }
}
else
{
  qx.Proto.foo = function() {
    // All other browsers
  }
}
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.Variant.select("qx.client", 
  {
    "mshtml|opera": function() {
       // Internet Explorer or Opera
    },
    "default": function() {
       // All other browsers
    }
  });
}

Events

qooxdoo 0.6.x qooxdoo 0.7
In qooxdoo 0.6 it is not possible to determine from the code, which events an object fires. Special API documentation strings in the class header are used to indicate which types of events are used in the class but there is no way to check the correctness of these declarations.
/**
 * @event click {qx.event.type.MouseEvent} Fired if the widget is clicked.
 */
qx.OO.defineClass("qx.some.Class", qx.core.Target, function()
{
...
});
 
...
In qooxdoo 0.7 the 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 me 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"
  } 
  ...
})

Information

Last modified:
2007/08/31 18:10 by ecker

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.