PureBasic and the Object-Oriented Programming

Concepts Implementation


In this section, I present how the object concepts can be simply implemented in PureBasic.
This implementation doesn't refer what is programmed in object-oriented languages. Furthermore, the goal of an implementation is to be improved or to be adapted to the need.
Thus, I propose here one of these implementations with its own advantages and limits.

First Implementation


Concrete Class and Abstract Class
As seen, the Class defines the contents of an object:

  • Its attributes (each variable type)
  • Its methods (Names, implementation)

For example, if I want to represent Rectangle objects and to display them on the screen, I shall define a Class Rectangle including a Draw() method.

The Class Rectangle could have the following construction:

Structure Rectangle
*Draw
x1.l
x2.l
y1.l
y2.l
EndStructure

Procedure Draw_Rectangle(*this.Rectangle)

EndProcedure

where x1, x2, y1 and y2 are four attributes (diametrically opposite points coordinates of the rectangle) and *Draw is a pointer referencing to the drawing function which displays Rectangles.
*Draw is here a function pointer used to contain the address of the wished function: @Draw_Rectangle().
By using CallFunctionFast(), a such referenced function can be used.

Thus, the proposed Structure is completely adapted to the Class notion:
the structure stores the definition of the object attributes: x1, x2, y1 and y2 are here Long variables.
the structure stores the definition of the object method: here the Draw() function thanks to a function pointer.

If a such defined Class is followed by the method implementations (in our example it is the Draw_Rectangle() Procedure/EndProcedure block statement), the Class will be a concrete Class.
In the opposite, the Class will be a abstract Class.

*this always refers to the object on which the method must be applied. This notation is applied in our example with the method Draw_Rectangle().

Instanciation
Now, to create an object called Rect1 from the Rectangle Class, write:

Rect1.Rectangle

To initialize it, simple write:

Rect1\Draw = @Draw_Rectangle()
Rect1\x1 = 0
Rect1\x2 = 10
Rect1\y1 = 0
Rect1\y2 = 20

Next, to draw Rect1 object, do:

CallFunctionFast(Rect1\Draw, @Rect1)

Encapsulation
In this implementation, the encapsulation doesn't exist, simply because there is no way to hide the attributes or the methods of such an object.

By writing Rect1\x1, the user can access to the attribute x1 of the object. This way was used to initialize the object.
The next implementation (second implementation chapter) will show how to fix this.
Although importing, this notion is not the most essential to practice OOP.

Inheritance
Now I want to create a new Class with the capability to Erase rectangles from the screen.
I can perform this new Rectangle2 Class by using the existing Rectangle Class and by providing to it a new method called Erase().

A Class being a Structure, let me take advantage of the extension property from structure. So, the new Class Rectangle2 can be:

Structure Rectangle2 Extends Rectangle
*Erase
EndStructure

Procedure Erase_Rectangle(*this.Rectangle2)

EndProcedure

The Rectangle2 Class includes as well members of the previous Rectangle Class and the new Erase() method.
To instanciate an object from this new Class write:

Rect2.Rectangle2

Rect2\Draw = @Draw_Rectangle()
Rect2\Erase = @Erase_Rectangle()
Rect2\x1 = 0
Rect2\x2 = 10
Rect2\y1 = 0
Rect2\y2 = 20

To use Draw() and Erase() methods of Rect2, I shall proceed in the same way as previously: thanks to CallFunctionFast()

That shows that Rectangle2 Class inherited properties of Rectangle Class.

The inheritance is a category of polymorphism. The object Rect2 can be seen also as an Object from the Class Rectangle. For this, just don't use the Erase() method! By inheritance, the object carries several forms: those of the objects coming from the Mothers Classes. It is named as polymorphism inheritance .

Overload
During the initialization of an object, the function pointers are initialized by assigning to them the method addresses, which are convenient for the object.

So, for an object Rect from Rectangle Class, by writing:

Rect1\Draw = @Draw_Rectangle()

I can use the Draw() method as following:

CallFunctionFast(Rect1\Draw, @Rect1)

Now, imagine that it is possible to implement another method for a rectangle display (by using a different algorithm than for the first method).

Let us call this implementation as Draw_Rectangle2():

Procedure Draw_Rectangle2(*this.Rectangle)

EndProcedure

It is possible to initialize our object Rect1 with this new method without effort:

Rect1\Draw = @Draw_Rectangle2()

To use the method, write again:

CallFunctionFast(Rect1\Draw, @Rect1)

In a hand (Draw_Rectangle() method) as in the other hand (Draw_Rectangle2() method) the use of the Rect1 method is strictly identical.
By the only line " CallFunctionFast (Rect1\Draw, @Rect1) ", it isn't possible to distinguish the Draw() method that the Rect1 object really uses.
To know this, it is necessary to go back to the initialization of the object.

The notion of function pointer allows the overloading of the Draw() method.

One limitation: the use of the CallFunctionFast() instruction implies to pay attention among the parameter numbers.

Conclusion:
In this first implementation, an object is able to answer to the main object-oriented concepts with some limitations.
In fact I have just put the bases for realizing a more complete object, this thanks to the PureBasic Interface instruction.

 
Contents

[1-2-3-4-5-6-7-8-9]

Top of the page