Difference between revisions of "Recital Object-Oriented Programming"

From Recital Documentation Wiki
Jump to: navigation, search
(Overriding methods)
(Methods)
 
(29 intermediate revisions by 2 users not shown)
Line 16: Line 16:
 
To define a class use the following syntax.  
 
To define a class use the following syntax.  
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     // class members
 
     // class members
endclass
+
enddefine
 
</code>
 
</code>
 
Then, to create an object based on this class use the following syntax.  
 
Then, to create an object based on this class use the following syntax.  
Line 24: Line 24:
 
<code lang="recital">
 
<code lang="recital">
 
myobject = new myclass()
 
myobject = new myclass()
 +
</code>
 +
Or alternatively.
 +
<code lang="recital">
 +
myobject = createobject("myclass")
 
</code>
 
</code>
  
Line 35: Line 39:
  
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     myprop = "hello world"
 
     myprop = "hello world"
endclass
+
enddefine
  
 
myobject = new myclass()
 
myobject = new myclass()
Line 47: Line 51:
 
Methods are procedures that are associated with objects. Methods are different from normal Recital procedures in that they are bound with an object and are called differently from the way normal Recital procedures are called.  
 
Methods are procedures that are associated with objects. Methods are different from normal Recital procedures in that they are bound with an object and are called differently from the way normal Recital procedures are called.  
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     myprop = "hello world"
 
     myprop = "hello world"
     public procedure mymethod()
+
     procedure mymethod()
 
         echo myprop
 
         echo myprop
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
myobject = new myclass()
 
myobject = new myclass()
Line 62: Line 66:
  
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     myprop = "hello world"
 
     myprop = "hello world"
 
     public procedure mymethod() interface
 
     public procedure mymethod() interface
endclass
+
enddefine
  
 
myobject = new myclass()
 
myobject = new myclass()
 
myobject.mymethod()    // throws an error "Interface method not defined"
 
myobject.mymethod()    // throws an error "Interface method not defined"
  
class myclass2 as myclass
+
define class myclass2 as myclass
 
     procedure mymethod()
 
     procedure mymethod()
 
         echo "hello world"
 
         echo "hello world"
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
myobject = new myclass2()
 
myobject = new myclass2()
Line 118: Line 122:
 
result = myobj.equals(myobj) // returns true
 
result = myobj.equals(myobj) // returns true
 
|-
 
|-
|save||Saves an object to an external file (default extension .obf)
+
|saveobj||Saves an object to an external file (default extension .obf)
 
e.g.<br />
 
e.g.<br />
myobj.save("save_myobj.obf")
+
myobj.saveobj("save_myobj.obf")
 
|-
 
|-
|load||Loads an object from an external file (default extension .obf)
+
|loadobj||Loads an object from an external file (default extension .obf)
 
e.g.<br />
 
e.g.<br />
myobj.load("save_myobj.obf")
+
myobj.loadobj("save_myobj.obf")
 
|}
 
|}
  
Line 130: Line 134:
 
You can 'hide' properties and methods using the access control modifiers protected, hidden, private, or static.
 
You can 'hide' properties and methods using the access control modifiers protected, hidden, private, or static.
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     private myprop = "hello world"
 
     private myprop = "hello world"
 
     public procedure mymethod()
 
     public procedure mymethod()
        return myprop
+
    return myprop
    endproc
+
 
     public static procedure mystaticmethod()
 
     public static procedure mystaticmethod()
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
myobject = new myclass()
 
myobject = new myclass()
Line 172: Line 175:
 
Classes which have a constructor method called '''init''' call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
 
Classes which have a constructor method called '''init''' call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     procedure init()
 
     procedure init()
 
     // insert your own constructor code here
 
     // insert your own constructor code here
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
obj = new myclass()    // the init() method is called with no arguments
 
obj = new myclass()    // the init() method is called with no arguments
Line 182: Line 185:
 
You can pass arguments to the constructor method when the object is created.
 
You can pass arguments to the constructor method when the object is created.
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     public name
 
     public name
 
     public title
 
     public title
Line 189: Line 192:
 
         title = ptitle
 
         title = ptitle
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
obj = new myclass("joe", "developer")    // the init() method is called with arguments
 
obj = new myclass("joe", "developer")    // the init() method is called with arguments
Line 196: Line 199:
 
Similarly, Recital uses a destructor concept similar to that of other object-oriented languages, such as Java. The destructor method will be called as soon as all references to a particular object are removed or when the object is explicitly destroyed or in any order in shutdown sequence. The Recital destuctor method is called '''destroy'''.
 
Similarly, Recital uses a destructor concept similar to that of other object-oriented languages, such as Java. The destructor method will be called as soon as all references to a particular object are removed or when the object is explicitly destroyed or in any order in shutdown sequence. The Recital destuctor method is called '''destroy'''.
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     procedure destroy()
 
     procedure destroy()
 
     // insert your own destructor code here
 
     // insert your own destructor code here
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
obj = new myclass()
 
obj = new myclass()
Line 209: Line 212:
 
There are many benefits of inheritance with Recital, the most common is simplifying and reducing instances of redundant code.  Recital supports multiple levels of inheritance. A class that inherits a parent class is said to be a 'subclass' of the parent.  
 
There are many benefits of inheritance with Recital, the most common is simplifying and reducing instances of redundant code.  Recital supports multiple levels of inheritance. A class that inherits a parent class is said to be a 'subclass' of the parent.  
 
<code lang="recital">
 
<code lang="recital">
class product
+
define class product
 
     public name
 
     public name
 
     public price
 
     public price
Line 219: Line 222:
 
         echo "Product name is " + name + ", price is " + price
 
         echo "Product name is " + name + ", price is " + price
 
     endproc
 
     endproc
endclass
+
enddefine
  
class food extends product
+
define class food extends product
 
     private weight
 
     private weight
endclass
+
enddefine
  
class car extends product
+
define class car extends product
 
     private color
 
     private color
endclass
+
enddefine
  
class motorbike extends product
+
define class motorbike extends product
 
     private size
 
     private size
endclass
+
enddefine
  
 
// now define a vehicle class that inherits from multiple classes (car and motorbike)
 
// now define a vehicle class that inherits from multiple classes (car and motorbike)
class vehicle extends car, motorbike
+
define class vehicle extends car, motorbike
 
     public manufacturer = "Ford"
 
     public manufacturer = "Ford"
 
     public yearDesigned
 
     public yearDesigned
Line 244: Line 247:
 
         echo "Manufacturer is " + manufacturer + ", price is " + price
 
         echo "Manufacturer is " + manufacturer + ", price is " + price
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
// create a new object
 
// create a new object
Line 257: Line 260:
 
<code lang="recital">
 
<code lang="recital">
 
// now define a vehicle class that inherits from multiple classes (car and motorbike)
 
// now define a vehicle class that inherits from multiple classes (car and motorbike)
class vehicle extends car,motorbike
+
define class vehicle extends car,motorbike
 
     public manufacturer = "Ford"
 
     public manufacturer = "Ford"
 
     public yearDesigned
 
     public yearDesigned
Line 269: Line 272:
 
         echo "Manufacturer is " + manufacturer + ", price is " + price
 
         echo "Manufacturer is " + manufacturer + ", price is " + price
 
     endproc
 
     endproc
endclass</code>
+
enddefine</code>
  
 
===Scope resolution operator===
 
===Scope resolution operator===
The scope resolution operator :: can be used to reference static methods and properties in classes.  
+
The scope resolution operator :: can be used to reference static methods and properties in classes or those in the super class.
 
<code lang="recital">
 
<code lang="recital">
class myclasslib
+
define class myclasslib
 
     public static mydata = array()
 
     public static mydata = array()
 
     public static proc display(arg)
 
     public static proc display(arg)
 
         echo arg
 
         echo arg
 
     endproc
 
     endproc
endclass
+
enddefine
 
</code>
 
</code>
 
We can call the display method without instantiating an object because it is declared static e.g.  
 
We can call the display method without instantiating an object because it is declared static e.g.  
Line 286: Line 289:
 
We can access "mydata" without instantiating an object because it is declared static e.g.  
 
We can access "mydata" without instantiating an object because it is declared static e.g.  
 
<code lang="recital">
 
<code lang="recital">
myclasslib::mydata("key") = "value"</code>
+
myclasslib::mydata["key"] = "value"</code>
  
 
===Special variables===
 
===Special variables===
Line 358: Line 361:
  
 
<code lang="recital">
 
<code lang="recital">
class myclass
+
define class myclass
 
     public myprop as character = "hello world"
 
     public myprop as character = "hello world"
 
     public procedure mymethod()
 
     public procedure mymethod()
        return myprop
+
    return myprop
    endproc
+
 
     static procedure mystaticmethod()
 
     static procedure mystaticmethod()
 
     endproc
 
     endproc
endclass
+
enddefine
  
 
// create a new object
 
// create a new object
Line 403: Line 405:
  
 
<code code lang="recital">
 
<code code lang="recital">
require_once("myclasslib.prg")</code>
+
require_once("myclasslib")</code>
  
 
The require_once( ) function will include the specified class library into your program only if it has not already been included. This will make all classes (also variables, procedures and functions) available to the calling program and all programs or procedures that it calls.  
 
The require_once( ) function will include the specified class library into your program only if it has not already been included. This will make all classes (also variables, procedures and functions) available to the calling program and all programs or procedures that it calls.  
  
 
The specified class library is compiled into a Recital object file and loaded into shared global memory. This provides for optimal memory usage and also improved application performance as the class library is only loaded once. When class/procedure libraries (and in fact all Recital web .rsp files) are loaded like this in Recital Web memory usage will be reduced dramatically.
 
The specified class library is compiled into a Recital object file and loaded into shared global memory. This provides for optimal memory usage and also improved application performance as the class library is only loaded once. When class/procedure libraries (and in fact all Recital web .rsp files) are loaded like this in Recital Web memory usage will be reduced dramatically.

Latest revision as of 04:15, 15 September 2010

Recital Object-Oriented Programming

Recital is a dynamic programming language particularly well suited to the development of database applications. While Recital still supports standard procedural programming, new extensions to the language give you the power and flexibility of object-oriented programming. Object-oriented design and object-oriented programming represent a change in focus from standard procedural programming. This short primer will give you a good understanding of how to program object-oriented Recital.

Classes and Objects

Classes and objects are closely related, but they are not the same. A class contains information about how an object should look and behave. A class is the blueprint or template for an object.

All of the properties and methods for an object are specified in the class definition. In addition, classes have the following characteristics that make them especially useful for creating reusable, easily maintained code:

  • Encapsulation
  • Subclasses
  • Inheritance
  • Interfaces

To define a class use the following syntax.

define class myclass
    // class members
enddefine

Then, to create an object based on this class use the following syntax.

myobject = new myclass()

Or alternatively.

myobject = createobject("myclass")

Class members

Class members consists of properties (variables) and methods (procedures) that are encapsulated within the class declaration.

Properties

An object has certain properties, or attributes that are specific to the object. Properties are the equivalent of variables that can only be accessed by referencing them inside an object. By encapsulating the data (variables) and procedures within a class, programming becomes less error prone and applications easier to maintain.

Objects you create in Recital have properties that are determined by the class the object is based on. As Recital is a dynamic language, classes in Recital allow properties and methods to be added at run time.

define class myclass
    myprop = "hello world"
enddefine
 
myobject = new myclass()
echo myobject.myprop    // displays "hello world"

Properties defined within classes can be simple variables, fixed arrays, dynamic arrays or objects.

Methods

Methods are procedures that are associated with objects. Methods are different from normal Recital procedures in that they are bound with an object and are called differently from the way normal Recital procedures are called.

define class myclass
    myprop = "hello world"
    procedure mymethod()
        echo myprop
    endproc
enddefine 
 
myobject = new myclass()
myobject.mymethod()    // displays "hello world"

Interface Methods

Interface Methods are template procedure definitions that must be implemented in any derived classes.

define class myclass
    myprop = "hello world"
    public procedure mymethod() interface
enddefine 
 
myobject = new myclass()
myobject.mymethod()    // throws an error "Interface method not defined"
 
define class myclass2 as myclass
    procedure mymethod()
        echo "hello world"
    endproc
enddefine
 
myobject = new myclass2()
myobject.mymethod()  // displays "hello world"

Base Methods

All classes created inherit from the object class. The object class has a set of built-in methods that are available to all classes.

Method Description
addproperty Dynamically add a property to an object.

e.g.
object.addproperty("newproperty", expression)

removeproperty Dynamically remove a property from an object.

e.g.
object.removeproperty("property")

clone Clone this object.

e.g.
newobj = myobject.clone()

tostring Returns a string describing the object

e.g.
// displays "myobj::myclass(n)"
// where (n) shows the reference count.
echo myobj.tostring()

list Lists the object's properties.

e.g.
myobject.list()

class Returns the classname of this object in upper case</td>
instanceof Returns True if this object is  an instance of the specified class

e.g.
result = myobj.instanceof("myclass")

equals Compares the contents of two objects and returns True if they contain the same property values. If properties are arrays or other objects then the comparison is done recursively.

e.g.
result = myobj.equals(myobj) // returns true

saveobj Saves an object to an external file (default extension .obf)

e.g.
myobj.saveobj("save_myobj.obf")

loadobj Loads an object from an external file (default extension .obf)

e.g.
myobj.loadobj("save_myobj.obf")

Member access control modifiers

You can 'hide' properties and methods using the access control modifiers protected, hidden, private, or static.

define class myclass
    private myprop = "hello world"
    public procedure mymethod()
    return myprop
    public static procedure mystaticmethod()
    endproc
enddefine
 
myobject = new myclass()
// throws an error as "myprop" is private and can only be accessed by methods inside the class
echo myobject.myprop    
myobject = new myclass()
// this will work. displays "hello world"
echo myobject.mymethod()

Special methods

Several methods hold special significance in a class.

Name Description
init Called after an object is first created. This is a known as the constructor method.
destroy Called just prior to an object being destroyed. This is known as the destructor method.
<property>_access Property access notification for the property <property>.
<property>_assign Property assignment notification for the property <property>.
this_access Property access notification. This will be called for any property which does not have its own <property>_access method.
this_assign Property assignment notification. This will be called for any property which does not have its own <property>_assign method.
error Class specific error handler.

Constructors and Destructors

Recital allows developers to declare constructor and destructor methods for classes. Classes which have a constructor method called init call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.

define class myclass
    procedure init()
    // insert your own constructor code here
    endproc
enddefine
 
obj = new myclass()    // the init() method is called with no arguments

You can pass arguments to the constructor method when the object is created.

define class myclass
    public name
    public title
    procedure init(pname, ptitle)
        name = pname
        title = ptitle
    endproc
enddefine
 
obj = new myclass("joe", "developer")    // the init() method is called with arguments

Similarly, Recital uses a destructor concept similar to that of other object-oriented languages, such as Java. The destructor method will be called as soon as all references to a particular object are removed or when the object is explicitly destroyed or in any order in shutdown sequence. The Recital destuctor method is called destroy.

define class myclass
    procedure destroy()
    // insert your own destructor code here
    endproc
enddefine
 
obj = new myclass()
obj = null        // the destroy() method will be called

Class inheritance and subclassing

There are many benefits of inheritance with Recital, the most common is simplifying and reducing instances of redundant code. Recital supports multiple levels of inheritance. A class that inherits a parent class is said to be a 'subclass' of the parent.

define class product
    public name
    public price
    procedure init(cName, nPrice)    // constructor
        name = cName
        price = nPrice
    endproc
    procedure display()
        echo "Product name is " + name + ", price is " + price
    endproc
enddefine
 
define class food extends product
    private weight
enddefine
 
define class car extends product
    private color
enddefine
 
define class motorbike extends product
    private size
enddefine
 
// now define a vehicle class that inherits from multiple classes (car and motorbike)
define class vehicle extends car, motorbike
    public manufacturer = "Ford"
    public yearDesigned
    procedure init(cName, nPrice)    // constructor
        manufacturer = cName
        price = nPrice
    endproc
    procedure display()
        echo "Manufacturer is " + manufacturer + ", price is " + price
    endproc
enddefine
 
// create a new object
myobject = new vehicle("Ford", 20000)
myobject.display()    // displays "Manufacturer is Ford, price is 20000
 
myobject = new motorbike("Honda", 1500)
myobject.display()    // displays "Product name is Honda, price is 1500

Overriding methods

As can be seen by the above example of class inheritance, you can override methods of parent classes that you inherit. If you want to call the parent class method of the same name from within a subclass, then use the dodefault() method. Note that because Recital handles multiple inheritance, and because a subclass can only have one 'parent' the last class inherited is denoted as the parent and is called by dodefault().

// now define a vehicle class that inherits from multiple classes (car and motorbike)
define class vehicle extends car,motorbike
    public manufacturer = "Ford"
    public yearDesigned
    proc init(cName, nPrice)    // constructor
        manufacturer = cName
        name = cName
        price = nPrice
    endproc
    proc display()
        dodefault()                   // calls the 'display()' method in the parent class 'product'
        echo "Manufacturer is " + manufacturer + ", price is " + price
    endproc
enddefine

Scope resolution operator

The scope resolution operator :: can be used to reference static methods and properties in classes or those in the super class.

define class myclasslib
    public static mydata = array()
    public static proc display(arg)
        echo arg
    endproc
enddefine

We can call the display method without instantiating an object because it is declared static e.g.

myclasslib::display("hello world")

We can access "mydata" without instantiating an object because it is declared static e.g.

myclasslib::mydata["key"] = "value"

Special variables

There are several special built-in object variables that can be used.

Object Description
this A reference to the currently executing object

Iterating through object properties

You can iterate over the properties of an object using the foreach command like this.

// create a new object
myobject = new myclass()
 
// iterate over its properties
foreach myobject as name => value
    echo "name=" + name + ", value=" + value    // displays "name=MYPROP, value=hello world"
endfor

Dynamically adding and removing properties runtime

As Recital is a dynamic language, object properties can be added and removed dynamically at runtime.

// create a new object
myobject = new myclass()
 
// extend it by adding a property at runtime
myobject.addproperty("date", date())
 
// now remove it
myobject.removeproperty('date')

Dynamically assigning methods at runtime

As Recital is a dynamic language, methods can be assigned to objects dynamically at runtime.

// create a new object
myobject = new myclass()
 
// dynamically add a method
procedure mynewmethod()
    echo "hello world"
endproc
 
myobject.newmethod = mynewmethod
myobject.newmethod()    // displays "hello world"

Runtime data-type checking

You can restrict what types of data can be assigned to properties using the "as datatype" clause.

Type Description
character can assign character data
numeric can assign numeric data
date can assign date data
logical can assign logical data
datetime can assign datetime data
currency can assign currency data
class Name of user-defined class
define class myclass
    public myprop as character = "hello world"
    public procedure mymethod()
    return myprop
    static procedure mystaticmethod()
    endproc
enddefine
 
// create a new object
myobject = new myclass()
myobject.myprop = "this is data"      // works ok
myobject.myprop = 10                      // throws an error because we defined "myprop as character"

Understanding member lookup

Recital variables and procedures are declared in a hashed, block structured symbol table. Each procedure, function or method that is executed increases the runtime execution "level". Any variables or procedures that are referenced are looked up in decreasing runtime execution level order until found. In the case of object methods, the Recital runtime engine will first look in the active object for properties and methods. If not found, then it will carry out a standard symbol table lookup in decreasing runtime level order. This technique allows classes to be easily integrated into an existing application, and provides the ability for object methods to reference global variables and call global procedures.

The life of an object

Recital uses object reference counting to handle automatic garbage collection.

When an object is created its reference count is set to 1. All assignments of objects to other variables or procedure/function arguments cause the reference count to be incremented. An object is released from memory (dereferenced) when its reference count reaches zero.

To dereference an object you simply assign another value to it. e.g.

// create a new object (and set its reference count to 1)
myobject = new myclass()
 
// now free up the object. This will decrement the reference count associated with the object.
// When the reference count is 0 then the object will be released from memory (dereferenced).
myobject = null
 
// In this example the object is not released as it is still referenced by 'myvar'
myobject = new myclass()
myvar = myobject
myobject = null
 
// now it will be released
myvar = null

If an object property (variable) is an object reference, when the object containing the property (variable) is released (dereferenced) than the reference count of all object variables is decremented, and when the reference count reaches zero, then that object is released from memory.

Writing and using class libraries

Once you have completed the development of your class library you can use the classes within your programs like this.

require_once("myclasslib")

The require_once( ) function will include the specified class library into your program only if it has not already been included. This will make all classes (also variables, procedures and functions) available to the calling program and all programs or procedures that it calls.

The specified class library is compiled into a Recital object file and loaded into shared global memory. This provides for optimal memory usage and also improved application performance as the class library is only loaded once. When class/procedure libraries (and in fact all Recital web .rsp files) are loaded like this in Recital Web memory usage will be reduced dramatically.