Introduction to the Extended Types

Introduction

FreeBasic is moving towards implementing Object Oriented programming. While classes have not yet been added to the language, the Type definition has been extended to include some Object Oriented constructs as a first step towards full class support. This article introduces some of the concepts of Object Oriented design and explains some of the extended type constructs.

Object Oriented Programming

Object Oriented Programming, usually shortened to OOP, is a methodology that enables the programmer to build code units called objects. An object is a thing; it is a unit of code that represents something that needs to be manipulated in a program. You can think of an object as a noun: a person, place or thing. An object could be a sprite, a drawing primitive or something more elaborate like a tank in a game. Any concrete entity that has a set of characteristics and actions can be represented as an object.

An object contains both the data needed by the object, and the methods (subroutines and functions) that act on the data. This grouping of data and methods into a single entity is called encapsulation. Encapsulation allows you to create modular units that can be reused in multiple programs. This idea of code reuse was the main motivation in the creation of the OOP paradigm.

Another beneficial consequence of encapsulation is information hiding. The data inside the object is shielded from the outside world so that unwanted changes to the data cannot occur. Instead of accessing a variable directly, the object has a public interface that the external program should use to access and change data members. By using an interface, you can control how the object behaves and ensure that its operation is consistent across many programs.

The interface also allows you to make internal changes to the code, without changing the way the object is accessed. As long as you don’t change the published interface, that is change any existing public methods, you can improve the object without breaking any existing code that relies on the object. In the cases where a program my need an improved method, you can leave the old method in place to maintain compatibility, and just add a new method with the improved functionality. New programs can use the new method, while old programs can still use the old method.

Another advantage of using a public interface is so that other programmers can use your object without worrying about the internal details of the object. As long as the published interface is stable and well documented, anyone should be able to use your object, even beginners.

The Published Contract

As already stated, OOP was designed to enable code reuse among programmers. In order for code reuse to be helpful, the published interface must remain stable. That is, once an object has been released and is being used in programs, the published interface should not change so that programs that use the object continue to work correctly. There is an implicit contract between you as the author of the object and the end-user of your object that you will maintain the published interface across changes that may need to be made to the object. This implicit contract between author and user is the main strength of the OOP paradigm, and is the main reason that OOP has become such a powerful programming methodology.

The Characteristics of an Object

As already mentioned, an object contains both data and methods. The data describes the properties of an object, while the methods describe what the object can do. A simple, and not-really-useful example will illustrate this concept.

Suppose you want to create an object that draws a rectangle on the screen. A rectangle can have several properties that would be contained within the data members of the object. A rectangle has an origin on the screen, normally the top left corner, which can be represented by x and y data members. A rectangle has a width and a height, so the object would have width and height data members. A rectangle can either be outlined or filled, so a filled flag data member can be added to the object. Of course, if you are going to draw a rectangle, you will want to draw it in a particular color, so the object will need to have a color data member, and to have the object be a bit more flexible, you can add a color member for the outline and a different color member for the fill. Of course you will need a method to actually draw the rectangle on the screen, so you can add a draw routine to the object definition.

So our rectangle object has the following preliminary properties and methods:

  • Property: x and y origin
  • Property: width
  • Property: height
  • Property: filled
  • Property: outline color
  • Property: fill color
  • Method: DrawRectangle

This list is called the object definition. In FreeBasic you define an object using the extended Type definition. The extended Type is similar to the standard Type, with some added language constructs that implements a subset of OOP features.

A Rectangle Type Definition

The following code snippet is a partial rectangle definition:

Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
End Type

As you can see, the extended Type looks much like a standard Type except for the Private: and Public: keywords and the sub declaration. The Private: keyword tells the compiler that the data members that follow are private to the type, that is cannot be accessed outside of the type. The private state extends to all object members until a new qualifier is encountered, which in this case is the Public: qualifier just above the Sub declaration. All of the data members are hidden from the outside world and cannot be changed from outside the scope of the Type, a process called information hiding. The underscore appended to the private variables is the common way to define private variables.

Information hiding is a way to maintain the integrity of the object. You should never allow an outside process to directly access a data member. All data access should be through the use of Property members so that you can control what is being passed to your object. Strict control over your object’s data will help prevent many errors that may occur when a programmer uses your object.

Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(Byval xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(Byval yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(Byval w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(Byval h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(Byval f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(Byval oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(Byval fc_ As Integer)
    Declare Property FillColor() As Integer
End Type

The Declare statements following the Public: qualifier comprises the public interface to your object. Since the variables of the type are defined with the Private: keyword, the only way to access the variables is through the Property members maintaining the integrity of the object. Since you define the code in each Property member, you have full control over what is being put into your object. A common example of this is to put range checking code in your property members so that the object does not contain invalid data.

In this example, the variables can be both written and read. The compiler distinguishes between a read Property and a write Property by the type of the method. A subroutine-formatted Property is a write property since you are passing a value that will be saved in a private variable. A function-formatted Property is a read property since a private variable will be returned to the caller. You can create read-only Properties by adding just a function-formatted Property or write-only Properties by just adding a subroutine-formatted Property.

Creating Well-Behaved Objects

The definition looks complete at this point, but there is a problem. What would happen if some or all of the variables were not initialized? The object would not perform correctly and potentially generate a runtime error. It would be better to have a set of default values for the object variables just in case one or more variables did not get initialized. You can initialize the object at the moment of creation by using a Constructor.

A Constructor is a subroutine that is called when the object is created using the Dim (or New) statement. Constructors are useful for initializing an object, either with default values, or values you pass to the Constructor. The updated type definition now looks like the following:

Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(Byval xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(Byval yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(Byval w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(Byval h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(Byval f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(Byval oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(Byval fc_ As Integer)
    Declare Property FillColor() As Integer    Declare Constructor()
    Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)

End Type

You will notice in the definition that we have two Constructors, one that takes a set of parameters and one that doesn’t. This is called overloading and can be used not only with Constructors but also with other subroutines and functions. Overloading is useful for situations where you need to handle different parameter types with a single method call. The compiler will determine which method to call based on the parameters passed to the method. You can overload as many methods as you want, as long as the number and type of parameters for each method is unique.

In this instance, if the Constructor is not passed any parameter values, it will initialize the variables to a set of default values. If the Constructor is called with parameters, then it will use the passed values to initialize the object’s variables.

There is also a Destructor method that is called when the object is destroyed. You can use the Destructor to perform any cleanup tasks that must be carried out before the object is released from memory. If the object created any pointer references, or opened any files, then you would clean up those references in the Destructor. Since the Rectangle object doesn’t create any outside references, a Destructor is not needed.

Filling in the Object Methods

The type definition is a template for the object type and tells the compiler how to set up the object in memory. However, in order to actually use the object, you need to create the actual method calls, which is shown in the next listing.

Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(Byval xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(Byval yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(Byval w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(Byval h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(Byval f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(Byval oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(Byval fc_ As Integer)
    Declare Property FillColor() As Integer
    Declare Constructor()
    Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)
End Type

Sub myRect.DrawRect()
    Line (this.x_, this.y_)-(this.x_ + Width - 1, this.y_ + this.height_ - 1), this.Otlncolor_, B
    If this.Filled_ <> 0 Then
        Paint (this.x_ + 1, this.y_ + 1), this.Fillcolor_, this.Otlncolor_
    End If   
End Sub

Property myRect.x(Byval xx_ As Integer)
    this.X_ = xx_
End Property

Property myRect.x() As Integer
    Return this.X_
End Property

Property myRect.y(Byval yy_ As Integer)
    this.Y_ = yy_
End Property

Property myRect.y() As Integer
     Return this.y_
End Property

Property myRect.Width(Byval w_ As Integer)
    this.Width_ = w_
End Property

Property myRect.Width() As Integer
    Return this.Width_
End Property

Property myRect.Height(Byval h_ As Integer)
    this.Height_ = h_
End Property

Property myRect.Height() As Integer
    Return this.Height_
End Property

Property myRect.Filled(Byval f_ As Integer)
    this.Filled_ = f_
End Property

Property myRect.Filled() As Integer
    Return this.Filled_
End Property

Property myRect.Otlncolor(Byval oc_ As Integer)
    this.Otlncolor_ = oc_
End Property

Property myRect.Otlncolor() As Integer
    Return this.Otlncolor_
End Property

Property myRect.FillColor(Byval fc_ As Integer)
    this.Fillcolor_ = fc_
End Property

Property myRect.FillColor() As Integer
    Return this.Fillcolor_
End Property

Constructor myRect
    this.X_ = 0
    this.Y_ = 0
    this.Width_ = 10
    this.Height_ = 10
    this.Filled_ = 0  
    this.Otlncolor_ = 15
    this.Fillcolor_ = 7
End Constructor

Constructor MyRect (xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)

    this.X_ = xx_
    this.Y_ = yy_
    this.Width_ = w_
    this.Height_ = h_
    this.Filled_ = f_  
    this.Otlncolor_ = oc_
    this.Fillcolor_ = fc_
End Constructor

The Methods and Properties are defined using the Sub/Function/Property methodname.TypeName syntax. This tells the compiler how to match up methods with the proper type definition. The Constructors are defined with the type name for the same reason. The this identifier is a hidden parameter that is passed to the methods that refers to the defined type. You use the this identifier to specify that you want to access the type constructs.

Using Your Object

The object is now complete can be used in a program which is listed below.

Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(Byval xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(Byval yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(Byval w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(Byval h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(Byval f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(Byval oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(Byval fc_ As Integer)
    Declare Property FillColor() As Integer
    Declare Constructor()
    Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)
End Type

Sub myRect.DrawRect()
    Line (this.x_, this.y_)-(this.x_ + this.Width_ - 1, this.y_ + this.height_ - 1), this.Otlncolor_, B
    If this.Filled_ <> 0 Then
        Paint (this.x_ + 1, this.y_ + 1), this.Fillcolor_, this.Otlncolor_
    End If   
End Sub

Property myRect.x(Byval xx_ As Integer)
    this.X_ = xx_
End Property

Property myRect.x() As Integer
    Return this.X_
End Property

Property myRect.y(Byval yy_ As Integer)
    this.Y_ = yy_
End Property

Property myRect.y() As Integer
     Return this.y_
End Property

Property myRect.Width(Byval w_ As Integer)
    this.Width_ = w_
End Property

Property myRect.Width() As Integer
    Return this.Width_
End Property

Property myRect.Height(Byval h_ As Integer)
    this.Height_ = h_
End Property

Property myRect.Height() As Integer
    Return this.Height_
End Property

Property myRect.Filled(Byval f_ As Integer)
    this.Filled_ = f_
End Property

Property myRect.Filled() As Integer
    Return this.Filled_
End Property

Property myRect.Otlncolor(Byval oc_ As Integer)
    this.Otlncolor_ = oc_
End Property

Property myRect.Otlncolor() As Integer
    Return this.Otlncolor_
End Property

Property myRect.FillColor(Byval fc_ As Integer)
    this.Fillcolor_ = fc_
End Property

Property myRect.FillColor() As Integer
    Return this.Fillcolor_
End Property

Constructor myRect
    this.X_ = 0
    this.Y_ = 0
    this.Width_ = 10
    this.Height_ = 10
    this.Filled_ = 0  
    this.Otlncolor_ = 15
    this.Fillcolor_ = 7
End Constructor

Constructor MyRect (xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)

    this.X_ = xx_
    this.Y_ = yy_
    this.Width_ = w_
    this.Height_ = h_
    this.Filled_ = f_  
    this.Otlncolor_ = oc_
    this.Fillcolor_ = fc_
End Constructor

'Create a graphic screen
Screen 18

'Create an object using the default constrcutor
Dim aRect As myRect
'Create an object by explicitly setting the constructor values
Dim bRect As myRect = myRect(200, 200, 200, 100, 1, 15, 9)

'Draw the rectangles on the screen
aRect.DrawRect
bRect.DrawRect

'Update aRect properties
aRect.X = 90
aRect.Y = 20
aRect.Filled = 1
aRect.FillColor = 15

'Draw new rect
aRect.DrawRect
Sleep
End

To initialize the object using the default Constructor, you simply Dim the extended Type just as you would the standard type. If the Constructor only takes a single value then you can use the Dim var as Typename = value syntax. To initialize the object with a set of values, you Dim the type and then use the = typename(par1m parm1…) syntax. You can see that accessing the members of the object is just like accessing the member of a standard type.

Thanks to cha0s at the FreeBasic forums for the information regarding Properties.