Object oriented programming

From Spheriki

Jump to: navigation, search

Note: This tutorial assumes you have a basic knowledge of javascript commands and syntax.

Object Oriented Programming (OOP) is a programming paradigm that uses "objects" to design applications and computer programs. It is based on several techniques, including inheritance, modularity, polymorphism, and encapsulation. It was not commonly used in mainstream software application development until the early 1990s. Many modern programming languages now support OOP.

If you don't understand what all these terms mean, that's where I come in!

Firstly, new programmers often look at all of this, understand it, and then think: "What's the Point!?", so, here's why you should use object oriented programming.

Object Oriented Programming is a very natural, easy and efficient way to make a program. In real life, we think of everything in terms of objects. Games lend themselves towards Object Oriented Programming as well, because of the amount of Objects in them - Sword, Shield, Window, Menu etc.

Using advanced OOP, it's very easy to extend and expand on other code. By inheriting objects, you don't need to reinvent the wheel and type out code all day. Less typing means you make less mistakes, which means your headaches go away.

Contents

Introduction

In the earlier days of programming, programs started at the beginning, finished at the end, and the only way you could possibly redirect program flow was to use a series of statements such as goto to jump to another line in the program. For larger projects, this quickly became unworkable, as one would end up with a vast amount of goto statements going everywhere, a condition known as spaghetti code, that was almost impossible to understand.

To remedy this, some very smart people invented procedural programming. Procedural programming is typically characterized by the use of user-defined subroutines and functions, that could be called upon by the program to perform a specific task or calculate a value. A subroutine does not return a value, whereas a function does (see the following example).

// JavaScript Example of Subroutines and Functions

function AddTwoNumbers (argument1, argument2) { //function
  return argument1 + argument2;
}

function DisplaySumOfTwoNumbers (argument1, argument2) { //subroutine
  Abort(argument1 + argument2);
}

//now we can see that we can call the function and it will return the value to the main program:
  Abort(AddTwoNumbers(1,2)); // will return 3 to abort.

//The subroutine, if the program were to get this far (which it wouldn't) could be used to achieve the same task with:
  DisplaySumOfTwoNumbers(1,2); // will abort with value 3

// End Example

This procedural programming is still very much in use today. Procedural languages include C, some BASIC variants, and more. Procedural language constructs are also available in most modern Object Oriented Languages such as C++.

In this procedural programming paradigm, large amounts of data would be stored in complex data structures, and subroutines and functions would manipulate that data.

Object Oriented programming takes this one step further, by, basically, integrating specific subroutines and functions into complex data structures.

Scope

Scope is a term used to describe whether certain variables, functions (and objects) are accessible from a particular point in a program. Loosely speaking, there are two major types:

  • Global Scope - The identifier is available to all parts of the program
  • Local Scope - The identifier is available to only specific parts of the program.
//JavaScript Example for Basic Scope
var myinteger = 1; // This variable can be accessed anywhere

function game() {
  var mynextinteger = 2; //This variable can be accessed only within the game function
}
//End Example

Most of the time, if you are writing in purely procedural JavaScript, functions will be in the global scope, however, you can put functions within functions, and those sub-functions will only be accessible from within the function in which they are placed.

//JavaScript Example for Function Nesting
function game() {
  function exitgame() {
    //Some Stuff
    Abort();
  }
  exitgame(); //This call will work
}
exitgame(); //This call will NOT work
//End Example


Instantiation and Prototyping

Now that we have the basics of procedural programming out of the way, we can move on to some basic Object Oriented Programming. An object (or class) is a structure that contains the following two elements:

  • Properties (members) - Variables stored within the object that are accessible from anything in the same scope as the object.
  • Methods - Subroutines or Functions that are part of the object, that can be accessed by anything in the same scope.

In JavaScript, and most languages, we must create a prototype (sometimes ambiguously called a class) of the object that describes the objects properties and methods. Once we create the prototype, we must make an instance of a particular prototype, that will form the object we intend to use. You can make more than one instance of a prototype (i.e multiple objects of the same type) and can also make arrays of instances and so on. This process of creating an instance is called Instantiation. Objects being independent of their prototype and of each other is called Encapsulation.

//JavaScript Example of a Prototype and Instantiation

function MyObject() {  //prototype, not a function
  this.property = 1; // creates a property called "property" and gives it an initial value of 1 for all instances of this object
  this.method = function () { 
                  //Do some thing here
                }; //Here we have created a method within the object. These are like functions that can be accessed as members of the object.
}

//Now that we've made a prototype, we can make a instances of the prototype as follows:
var MyObjectInstance1 = new MyObject();
var MyObjectInstance2 = new MyObject();
MyObjectInstance1.property = 2; //sets the property of the first instance to 2
MyObjectInstance2.property = MyObjectInstance2.property * 2;  //sets the property of the second instance to 4
 
MyObjectInstance1.method(); //executes the method for the first instance
MyObjectInstance2.method(); //executes the method for the second instance

Abort(MyObjectInstance1.property + MyObjectInstance2.property); //Gives 6
//End JavaScript Example

Note: JavaScript ambiguously uses the function control structure to refer to objects. Don't let it fool you though, we are creating a prototype there, not a function.

We can also access the prototype of an object and add/remove to it outside the prototype definition. This is particularly handy for defining methods as nesting functions like above can get confusing and awkward.

//JavaScript Example of Further Prototyping

function MyObject() { //ignore the use of the word "function" here, it's not really a function.
  this.property = 1; // creates a property called "property" and gives it an initial value of 1 for all instances of this object
}

MyObject.prototype.method = function () { //We are adding a method to the prototype after it has been defined.
  //Do Something Here
}

//Now that we've made a prototype, we can make a instances of the prototype as follows:
var MyObjectInstance1 = new MyObject();
var MyObjectInstance2 = new MyObject();
MyObjectInstance1.property = 2; //sets the property of the first instance to 2
MyObjectInstance2.property = MyObjectInstance2.property * 2;  //sets the property of the second instance to 4
 
MyObjectInstance1.method(); //executes the method for the first instance
MyObjectInstance2.method(); //executes the method for the second instance

Abort(MyObjectInstance1.property + MyObjectInstance2.property); //Gives 6
//End JavaScript Example

We can also make the object accept arguments to it's constructor prototype as shown:

//JavaScript Example of Further Prototyping

function MyObject(valueForProperty) { //ignore the use of the word "function" here, it's not really a function.
  this.property = valueForProperty; // creates a property called "property" and gives it the value of the parameter when instantiated.
}

MyObject.prototype.method = function () { //We are adding a method to the prototype after it has been defined.
  //Do Something Here
}

//Now that we've made a prototype, we can make a instances of the prototype as follows:
var MyObjectInstance1 = new MyObject(1); //Creates an instance with a property value of 1.
var MyObjectInstance2 = new MyObject(4); //Creates an instance with a property value of 4.
MyObjectInstance1.property = 2; //sets the property of the first instance to 2
MyObjectInstance2.property = MyObjectInstance2.property * 2;  //sets the property of the second instance to 8
 
MyObjectInstance1.method(); //executes the method for the first instance
MyObjectInstance2.method(); //executes the method for the second instance

Abort(MyObjectInstance1.property + MyObjectInstance2.property); //Gives 10
//End JavaScript Example

Instantiation and prototyping is the first major step towards using Object Oriented Programming in a powerful and efficient manner.

Objects as Properties

You can make an object a property of another object. This is useful when your object requires sub-objects, for example, a hand object might have five finger objects.

We define Objects as Properties much like we define methods (because JavaScript uses function statements for both).

//JavaScript Example for defining Objects as properties of another object.
function MyObject() {
 this.subobject = function () {
  this.property = 1;
 }
 this.subobject.call(this.subobject);
}

//We can then instantiate and access as normal:
var a = new MyObject();
Abort(a.subobject.property); //Gives 1


The only difference, in fact, is this line:

 this.subobject.call(this.subobject);

This line makes JavaScript execute the function so as to create the prototype. We use the call function to specify that this should apply to the subobject when within the subobject, not the parent object (which is what it does by default).

Inheritance

Inheritance means creating an object prototype that inherits all properties and methods from another prototype. You can use this to good effect - A talking window could inherit from a generic window class.

Before I launch into examples, some terminology may be useful: A subclass is a prototype that inherits from a superclass. These are sometimes called inheritor and inherent classes respectively.

You can make methods and properties in a subclass override methods and properties in the superclass. This lets you define a series of subclasses that have the same set of methods, but behave differently (basic polymorphism). Below is a basic example of inheritance and overriding.

   //JavaScript Example of Inheritance and Ovverriding

   function superClass() {
     this.bye = function () {
                return "Bye from superClass";
                }
     this.hello = function () {
                  return "Hello from superClass ";
                  }
   }

   function subClass() {
     this.inheritFrom = superClass; //Here we take on the properties and methods of superClass()
     this.inheritFrom(); //This performs the inheritance
     this.bye = function () {   
                return "Bye from subClass";
                } //We have overridden the bye function of the superclass
   }

   var newClass = new subClass();
   abort(newClass.hello() + newClass.bye()); //Will output: "Hello from SuperClass Bye from SubClass"

  //End Example

Like simpler prototyping, you can also do this outside the prototype definition (although this is more unclear) as shown:


   //JavaScript Example of Inheritance and Ovverriding with External Prototyping

   function superClass() {
     this.bye = function () {
                return "Bye from superClass";
                }
     this.hello = function () {
                  return "Hello from superClass ";
                  }
   }

   function subClass() {
     //Any stuff that isn't going to be overridden by the superclass can go here
   }
   subClass.prototype = new superClass(); //inheritance now done here
   subClass.prototype.bye = function () {   
                              return "Bye from subClass";
                            } 
   
   var newClass = new subClass();
   abort(newClass.hello() + newClass.bye()); //Will output: "Hello from SuperClass Bye from SubClass"

  //End Example

Often, that's all you'll need for simple OOP-based programs.

Polymorphism

Polymorphism describes the treatment of a set of objects as if they were another type of object (and have it compile/execute). It most commonly refers to objects that have methods of the same name and thus can be effectively treated as the same object in a loop. Usually, this means each object in question inherits from the same superclass, however this is not always the case.

Situation where Polymorphism/Inheritance is useful: You have one NPC superclass, and two different subclasses for two different NPCs. All three objects have a walk() method. The superclass is prototyped with a random-walking script for the method, but you want to make one of the NPC's remain stationary. So you create an object heirarchy as shown:

//JavaScript Example of an Object Heirarchy

function NPC() {
    this.walk = function () {
                 //Random Walking Script goes in here
                 }
 }

function NPC_1() {
    this.inheritFrom = NPC; 
    this.inheritFrom(); 
    this.walk = function () {
                 //Remain stationary
                 }
 }


function NPC_2() {
    this.inheritFrom = NPC;
    this.inheritFrom(); 
 }

 //End Example

Now, you have chosen to store all your NPC instances in an array, and, for some reason, in your script, you would like to have it instruct all NPC's to walk. Because of polymorphism, you can treat all these different classes as the same, as you can see in the following example. This example also shows other useful things such as arrays of objects, and For...In loops.

//JavaScript example of Polymorphism
function NPC() {
    this.walk = function () {
                 //Random Walking Script goes in here
                 }
 }

function NPC_1() {
    this.inheritFrom = NPC; 
    this.inheritFrom(); 
    this.walk = function () {
                 //Remain stationary
                 }
 }


function NPC_2() {
    this.inheritFrom = NPC;
    this.inheritFrom(); 

 }
 
 //Here we use a for...in structure to iterate through our array of objects

var NPCs = new Array();
NPCs[0] = new NPC_1();
NPCs[1] = new NPC_2();

 for (x in NPCs)
 {
  objects[x].walk();  
 }
 //End Example 

In this example, because both NPC types have a "walk" function, you can treat them both as if they were instances of the NPC superclass, but have them perform different actions.

I hope you can now appreciate the power of inheritance and polymorphism.

Accessing the Superclass

One of the limitations of traditional JavaScript OOP is that you can't access the original methods that have since been overridden from within a subclass. One of the major uses of this is to add code to a function when overriding without removing the code that is already there. So, you can "patch in" additional code before or after the existing code from the superclass within a specific function.

To access the superclass (and therefore the methods), you need to use a little work-around. This little-work around requires you to use a "class Extension approach" to create objects, rather than the traditional JavaScript OOP. Kamatsu has created a library for this purpose called Spheritype, part of his Kamatsu's Class Library package.

I personally think this approach is cleaner, but it is less widely known and used.

Spheritype

In order to use this, first download spheritype from the "Kamatsu's Class Library" project thread in the forums, located here:

http://www.spheredev.org/smforums/index.php/topic,2325.45/topicseen.html

Then, put kclSpheritype.js somewhere in your game folder, and include it, as shown:

RequireScript('kclSpheritype.js');

Include this somewhere towards the beginning of your script, where you define your global variables.

Defining Prototypes

Rather that define prototypes using the ambiguous function structure, we can now define prototypes by extending spheritype's generic Class prototype. This approach produces a much cleaner definition system that is alot more like other languages.

//JavaScript Example for defining prototypes with Spheritype
var BaseClass = Class.extend({   //The BaseClass prototype is now inheriting from the generic Class prototype. Pretty cool, huh!


   constructor: function() { 
    //This code is executed when the object is instantiated - commonly known as the 'Constructor'. 
    //We will deal with this more thoroughly later. 
    //This function is actually entirely optional, but I have included it for educational purposes
   },
       

   getNameID: function() {    //A Method, with the new definition system
       return "BaseClass ID " + this.id + " Name " + this.name;
   },
 

   id: 1,          // A Property, with default value 1.


   name: "Bob"  //Note the lack of a comma in the final item

});
//End Example

I'm sure you can already see the clarity present in this new syntax as opposed to the traditional JavaScript OOP.

Instantiating Objects

Now, having defined a BaseClass prototype as shown above, we can instantiate it just like an ordinary JavaScript object:

var a = new BaseClass();

Using the Constructor

It seems that with this new system, you can't pass arguments to objects as you instantiate them, i.e:

var a = new BaseClass(5);

You can, however, do this using the constructor (that you saw in the object definition earlier). The following code shows an object defined using the new system that accepts arguments on instantiation.

//JavaScript Example for Constructors with Spheritype
var BaseClass = Class.extend({   //The BaseClass prototype is now inheriting from the generic Class prototype. Pretty cool, huh!

   constructor: function(id) { 
     if (id) { //If the user specifies an argument
       this.id = id;
     }
   },

   id: 1,          // A Property, with default value 1.

});

var object1 = new BaseClass(); //object1.id = 1, the default value
var object2 = new BaseClass(5); //object2.id = 5
//End Example

Single-instance objects (null constructor)

In some cases, you may wish for an object to only be instantiated once - when the prototype is defined. An example of this would be a Game object that stores certain global data items. Using spheritype, you can make a prototype instantiate itself automatically, by defining the constructor as null. Behold:

var SingleInstanceClass = Class.extend({ 

   constructor: null,

   property: 4,
   method: function (arg) {
       Abort(arg);
   }

});

SingleInstanceClass.method(SingleInstanceClass.property); //Will abort with 4.

Objects as Properties

You can add objects as properties in much the same way that you would expect.


//JavaScript Example for defining traditional OOP object properties with Spheritype
var BaseClass = Class.extend({   //The BaseClass prototype is now inheriting from the generic Class prototype. Pretty cool, huh!
       

  getNameID: function() {    //A Method, with the new definition system
      
  },
 

  id: Class.extend({
      subproperty: 1,
      subproperty2: 2
  }),          


  name: "Bob"  //Note the lack of a comma in the final item

});


var a = new BaseClass();

Abort(a.id.subproperty);

//End Example

This example will return 1.

Inheritance

Inheritance is also something that is done very easily with the class extension system. See the following example, and note what classes extend what.

//JavaScript Example for Inheritance with Spheritype
var SuperClass = Class.extend({   
 
   //The constructor is actually optional.
 
   id: 1,          // A Property, with default value 1.

});

var SubClass = SuperClass.extend({

  id: 2;          //Overrides SuperClass id.

});

var Object = new SubClass();

Abort(Object.id); //Gives 2
//End Example


Polymorphism

Seeing as polymorphism is done exactly the same way as in standard JavaScript OOP, I won't bother requoting the example.

Accessing the Superclass

Here's the big advantage of the class extension system. You CAN access the superclass. When you make one object inherit from another, and then override a method, often you will only want to add extra bits of code to the method, rather than replace it entirely. Access of the superclass allows this. How? Well, Spheritype makes a function called '.base()' available to use within the object's scope. A call to base() is like including the code from the overridden method in your new one.


A good example of this is as follows.

//JavaScript Example for Superclass Access with Spheritype.
var BaseClass = Class.extend({
   construct: function() { /* optional constructor method */ },
       
   getName: function() {
       return "BaseClass";
   },
   
});

var SubClass = BaseClass.extend({
   getName: function() {
       //Calls the getName() method of BaseClass
       return "SubClass extends " + this.base();
   },

});

var TopClass = SubClass.extend({
   getName: function() {
       //Calls the getName() method of SubClass
       return "TopClass extends " + this.base();
   },
   
});


Abort(new TopClass().getName()); //Gives "TopClass extends SubClass extends BaseClass".
//End Example

This example is good because it shows how you can use Base() to move up the object heirarchy.

Tada! Superclass accessed!

Using Superclass Access to Extend Functions

Now that we have the superclass available, we can unlock another powerful feature in inheritance. You can now add or remove code to methods in a sub class. This means you can patch in specific changes to the function without overriding the entire thing. See the following example for further information:

//Begin JavaScript Example to Extend Functions using Spheritype
var MySuperClass = Class.extend({
   printInfo: function() {
           alert("From SuperClass");            
   },
   
});

var MySubClass = MySuperClass.extend({
   printInfo: function() {
           this.base(); //call the superclass equivalent function
           alert("From SubClass");                        
   },
   
});
//End Example

In this example, we have added this line:

alert("From SubClass");

to the existing printInfo function present in the superclass in our subclass.

Credits

This article was (almost) entirely written by Kamatsu.

Cheers,

Kamatsu 13:10, 21 July 2007 (IST)

Personal tools