July 2006 - Posts

Writing better OO with ATLAS – part III
Thu, Jul 27 2006 4:20

In the previous posts, I’ve spoken about classes and namespaces. Today I’m writing about interfaces. Interfaces are contracts which define one or more abstract members. It’s important to note that interface usage is slightly different from what we’re used to in classical OO languages (which is something we expected due to the way Javascript works). For instance, you won’t be able to perform casts from a class to an interface, but you can still see if an object implements an interface. Let’s see how we can define an interface:

LA.IStudent = function(){ this.printInfo = Function.abstractMethod;}LA.IStudent.registerInterface( "LA.IStudent" );

abstractMethod is introduced by ATLAS and it throws an exception. This ensures that we must “override” the method on a class that implements the interface. Btw, let’s change the Student class so that it implements the previous interface:

LA.Student = function( age, name ){
  LA.Student.initializeBase( this );
  var _name = name;
  var _age = age; 
  
  this.get_name = function(){ return _name; }
  this.set_name = function( value ){ _name = value; }

  this.get_age = function(){ return _age; }
  this.set_age = function( value ){ _age = value; }
}
LA.Student.registerClass( "LA.Student", null, LA.IStudent );

The only thing new here is the last parameter passed to the registerClass method. In the previous example, the class implements the interface but didn’t “override” the printInfo method. If we instantiate the class and call the method, we’ll have an exception. To avoid that, we must override the printInfo method by adding a new definition in our class:

this.printInfo = function(){ return _name + “ “ + _age;}

Now, we can have similar code to this one:

var std = new LA.Student( 30, "Luis" );alert( std.printInfo() );

As I’ve said, when we build an interface, we’re introducing a contract. Most of the times, it’s normal to use an interface to see if an object has adhered to that contract: if it does, then we know that it has some known members which we can use. We have two options: we can check if an object implements an interface or we can also check if a type implements an interface.

Let’s start by seeing if an object implements a specific interface. To do that, we’re changing the previous example so that the printInfo method call is made only if the object implements the interface:

var std = new LA.Student( 30, "Luis" );
if( LA.IStudent.isimplementedBy( std ) ){
   alert( std.printInfo() );
}

On the other hand, we could also use the object that represents the class definition:

var std = new LA.Student( 30, "Luis" );
if( LA.Student.implementsInterface( "LA.IStudent" ) ){
   alert( std.printInfo() );
}

Btw, you should also note that interfaces are kept in an internal list (maintained by the object that represents the class) and it goes through an initialization process which is responsible for adding the interface members defined by the interface to the class.

I guess this is all you need to know about interfaces.

by luisabreu | with no comments
Filed under:
Writing better OO with ATLAS – part II
Wed, Jul 26 2006 4:38

In the previous post, I started talking about how we can enhance our OO Javascript code by using ATLAS. I ended the previous post by referencing that the registerClass is really important and if you miss calling that the “ATLAS OO” code won’t work properly. So what does it do? Before going into that, I must talk about two methods: registerAbstractClass and registerSealedClass. As you expect, registerAbstractClass registers an abstract class (which shouldn’t be directly instantiated); on the other hand, the registerSealedClass is used to create a sealed class.

Internally, each of the previous methods initializes a field maintained by the object that represents the class. Currently, the platform only enforces sealed classes (ie, though you shouldn’t create an abstract class, you can do that; however, if you try to use a sealed class as the base of another class, it’s like you didn’t set a base class – ie, the base constructor won’t be called during initialization).

Internally, the registerXXX methods are responsible for initializing several fields which are maintained by the object that describes the class. Besides setting the type name (internal field _typeName), it initializes the name of the base classes (maintained in __astrPendingInheritances array) and a list of interfaces implemented by the current class (kept in the _interfaces properties). As we’ll see, these fields are important during instantiation time. Before delving into that, it’s time to see how we can create a new derived class. In this case, we’ll create a Student derived class (which we’ll call UniStudent):

LA.UniStudent = function( age, name, uni ){
    LA.UniStudent.initializeBase( this, [age, name] );
    var _uni = uni;
    this.get_uni = function(){ return _uni; }
    this.set_uni = function( value ){ _uni = value; }
}
LA.UniStudent.registerSealedClass( "LA.UniStudent", LA.Student );

The first thing to note is that calling the initializeBase method must be the first thing a derived class constructor should do. Then we go along and add more fields and members to our class. Finally, we still do need to register the class (and in this case, we’re creating a sealed class).

The initialize method is the main responsible for creating the inheritance illusion introduced by ATLAS. You see, instead of relying in the use of the prototype type, the team uses some fancy tricks which rely on the apply method of the Function Javascript object. What happens during initialization is that the base class (which as we saw, is represented by a method) will be applied to the current instance with code similar to this (this is the most important line):

this.bases[ i ].apply(p_objInstance, p_objArgs);

When ATLAS reaches this phase of initialization, it already has the bases property correctly filled up (with a pointer to the constructor of the base class) and what the previous line of code is doing is applying the constructor to the current instance that is being created (and passing some args maintained in the p_objArgs parameter – which, btw, is the array which is being sent to the initializeBase method called from the base class). In practice, this means that after calling the previous line, our new object will have all the fields and members defined by the base class appended to them as if they’ve been set in the derived class. All this happens when you call the initializeBase method (and do take in mind that this will propagate through all the classes in the hierarchy).

This is really cool and solves most of the problems associated with traditional inheritance offered by Javascript.

Using a derived class is really simple: you start by creating a new instance and then you access the properties of that object. Here’s a quick example on how that can be achieved:

var std = new LA.UniStudent( 30, "Luis", "UMa" );
alert( std.get_name() + " - " + std.get_age()+ " - " + std.get_uni() );

On the next post we’ll see how we can create and use interfaces.

by luisabreu | 1 comment(s)
Filed under:
Writing better OO with ATLAS – part I
Tue, Jul 25 2006 3:41

ATLAS makes writing OO Javascript a lot easier than it used to be. It’s fair to say that Javascript has supported OO for some time now; however, we’re talking about limited support only which is based on the use of the prototype property. In practice, this means that we can use inheritance, but in a very limited way. With ATLAS, we have access to

  • Namespaces;
  • Abstract and sealed classes;
  • “Virtual” methods;
  • Interfaces;
  • Enums and flags;

This may not seem much, but when compared to what Javascript offers by default, it’s really a lot. Enough talk, it’s time to see some examples on what we can do with ATLAS. Let’s start by the basics: adding the client ATLAS files. This might be done in two ways: you can use the ScriptManager control or you can add the files by hand. Adding the files through the ScriptManager control is easy and you get automatic browse detection. However, it’s not something you can do if you’re not using an ASP.NET page. Since this post is about the client approach, I’ll just use a script element to drop the necessary files on my page:

<script type="text/javascript" src="Atlas.js">
</script>

Now we can proceed and start talking about important things. Let’s start with namespaces.

Namespaces diminish type collision. When we’re writing Javascript OO code, we should always start by creating a new namespace which will host our types. This can be achieved through the registerNamespace method:

Type.registerNamespace( “LA” );

Namespaces are objects that are added as properties to the object window. Nested namespaces are also represented by objects; however, they’re added as a property to the parent namespace (instead of being added directly to the window object). So, if you write something like this:

Type.registerNamespace( “LA.Controls” )

It’ll result in the addition of a new property of type Object (LA) to the window Object. On the other hand, this new LA object will also have a new property attached to it (you guessed it: Controls, of type Object).

Back to the important stuff: classes. Namespaces are only useful because they’ll be used as a container for several types. Most of the time, you’ll find yourself creating new classes. The following example shows how to create a new class called Student, with two fields (exposed by what we can call properties – though they’re really methods):

Type.registerNamespace( "LA" );
LA.Student = function( age, name ){
 var _name = name;
 var _age = age;

 this.get_name = function(){ return _name; }
 this.set_name = function( value ){ _name = value; }

 this.get_age = function(){ return _age; }
 this.set_age = function( value ){ _age = value; }

}
LA.Student.registerClass( "LA.Student" );

As you might expect, classes in “ATLAS Javascript” still rely heavily in the use of functions. Internally, the previous class has two fields which aren’t available to the “outside world”. That’s why it was necessary to add those methods to retrieve and/or change the values of those fields (I normally call this methods properties). After creating the class, it’s really important to register it (as we’ll see in a future post).

Creating a new instance of the class can be achieved by using the new operator:

var std = new LA.Student( 30, "Luis" );
alert( std.get_name() + " - " + std.get_age() );

If you’re as curious as me, then you’re probably wondering why we must call the registerClass method. That will be explained in the next post .

by luisabreu | 2 comment(s)
Filed under:
Hello world!
Tue, Jul 25 2006 3:40

Hello.

My name is Luis Abreu, and I'm a Portuguese ASP.NET MVP. In this blog, you can expect to find several posts related with .NET and programming in general. Thanks for reading.

by luisabreu | 2 comment(s)
Filed under:

Search

This Blog

Tags

Community

Archives

Syndication

Email Notifications

News




  • View Luis Abreu's profile on LinkedIn


    Follow me at Twitter

    My books

    Silverlight 4.0: Curso Completo

    ASP.NET 4.0: Curso Completo

    Portuguese LINQ book cover

    Portuguese ASP.NET 3.5 book cover

    Portuguese ASP.NET AJAX book cover

    Portuguese ASP.NET AJAX book cover