Object+Oriented+Programming+(LBB)

//by Richard Russell, February 2015//

Version 3.00 of //LB Booster// (LBB) incorporates some extensions to support **Object Oriented Programming** (OOP). I should emphasise that the use of these extensions is entirely optional, and their presence should not impair compatibility with non-OOP Liberty BASIC programs. But please don't stop reading now: OOP has a lot to offer!

Many people find the whole idea of Object Oriented Programming unfamiliar, new-fangled and frightening. They tend to flinch at the very mention of it! This is unfortunate because OOP is intended to make programming easier and result in more reliable and easy-to-maintain code.

The basic concept behind OOP is that of **encapsulation**. This is one of those long words which can seem rather intimidating, but all it really means is keeping related things together. The idea is that programs consist of //code// which operates on //data// - the code only makes sense in association with the data on which it operates, and the data is only useful in association with the code which operates on it.

In traditional programming languages there is usually no way of 'bundling together' related code and data: the data (typically numbers, strings and arrays) are declared somewhere near the start of the program - or worse, scattered throughout it - and the subroutines and functions which work on that data are somewhere near the end. The programmer knows which subroutine/function is related to what data (at least, he once did!) but this may not be at all obvious to somebody else trying to understand how the program works.

Classes and Objects
Object Oriented Programming has the concept of a **class**. A class is just a bundled collection of data along with the code which operates on it (the data are often referred to as the **properties** of the class and the code, consisting of subroutines and functions, as the **methods** of the class). Not only does the class bundle the related items together, it (typically) isolates the data from being accessed from outside the class - rather as variables (other than GLOBALS) referenced inside a function or subroutine are inaccessible from outside.

Hopefully you can see how this is helpful. It is obvious at a glance what data is related to what code, and the 'methods' can only operate on the 'properties' in the same class (see later for how classes can be combined). This makes the code more understandable and more reliable, and allows modifications to be made with more confidence.

But this is all rather theoretical, and it may be easier to understand by way of example. Imagine that in a program we want to represent a **vehicle**; all vehicles have certain properties in common, these might be things like the speed with which it is moving. We also want to be able to perform certain operations on that vehicle, such as increase its speed (accelerate) or decrease its speed (decelerate).

In Object Oriented Liberty BASIC (OOLB) we can define a class to represent a vehicle as follows: code format="LB" CLASS Vehicle DIM speed

SUB accelerate rate speed += rate END SUB

SUB decelerate rate speed -= rate END SUB

FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code

The **properties** of the class are listed in one or more DIM statements; they may include numbers, strings and arrays (we will see later that it's also possible to include 'child' classes). The **methods** of the class provide the means to manipulate those properties, and a means to discover the value of a property (if it needs to be known outside the class).

So we have a simple class which represents a vehicle. But in the program there may be several different vehicles, with different speeds and other properties. This is where the real power of Objects comes in. The 'class' is like a template: it describes a vehicle **in general**. An 'object' is a specific case of that class (it's usually called an **instance** of the class) and describes a particular vehicle.

In OOLB we create an object (an instance of a class) as follows: code format="LB" NEW MyVehicle1 AS Vehicle code

The object **MyVehicle1** has a speed (and other properties, if any) independent of any other objects in the program. Let's put all this together into our first complete Object Oriented Liberty BASIC program: code format="LB" NEW MyVehicle1 AS Vehicle

CALL MyVehicle1::accelerate 10 PRINT MyVehicle1::get.speed CALL MyVehicle1::accelerate 5 PRINT MyVehicle1::get.speed CALL MyVehicle1::decelerate 10 PRINT MyVehicle1::get.speed

DISCARD MyVehicle1 END

CLASS Vehicle DIM speed

SUB accelerate rate speed += rate END SUB

SUB decelerate rate speed -= rate END SUB

FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code Here we have introduced a couple of things you haven't seen before. Firstly, to call a method (subroutine or function) in an object we use the familiar LB syntax but the name consists of the name of the object followed by two colons (this is called the //scope resolution operator//) and then the name of the method within that object, i.e. **Object::Method**.

Secondly, when we have finished with an object we **DISCARD** it. This frees the memory resources which were allocated to the object with **NEW**.

Private methods
Some methods may be intended to be called only from other methods within the same class, rather than from outside the class. It is possible to specify such methods as **PRIVATE** in which case an attempt to call them from outside the class will fail at run-time. Here is an example: code format="LB" NEW MyVehicle1 AS Vehicle CALL MyVehicle1::accelerate 10 PRINT MyVehicle1::get.speed CALL MyVehicle1::accelerate 5 PRINT MyVehicle1::get.speed CALL MyVehicle1::decelerate 10 PRINT MyVehicle1::get.speed DISCARD MyVehicle1 END CLASS Vehicle DIM speed SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code Here, instead of the **accelerate** and **decelerate** methods altering the speed value directly, they each call a PRIVATE method **adjust** which does so. Note the syntax for calling a method in the same class: specify the object name as **this**.

Constructors and Destructors
You will notice that in the class **Vehicle** the value of **speed** is not initialised. Just as with ordinary LB variables and arrays, the value is assumed to be zero or an empty string if not previously set. Of course we could have provided an explicit method for initialising the speed, but there is another approach.

It is possible to incorporate a special method called the **constructor**. The constructor takes the form of an ordinary subroutine but instead of being deliberately executed using CALL it is //automatically// called when the object is created. The constructor is distinguished by having a name which is the same as the name of the class. Let's modify our program accordingly: code format="LB" NEW MyVehicle1 AS Vehicle CALL MyVehicle1::accelerate 10 PRINT MyVehicle1::get.speed CALL MyVehicle1::accelerate 5 PRINT MyVehicle1::get.speed CALL MyVehicle1::decelerate 10 PRINT MyVehicle1::get.speed DISCARD MyVehicle1 END CLASS Vehicle DIM speed SUB Vehicle ' constructor speed = 2 END SUB

SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code Here the speed is automatically initialised to 2 when the object is created.

Suppose we want to be able to initialise the speed to a different value for each instance of the class. Again we could do that by calling a method, but it's also possible to pass one or more parameters to the constructor: code format="LB" NEW MyVehicle1 AS Vehicle 3 ' parameter passed to constructor CALL MyVehicle1::accelerate 10 PRINT MyVehicle1::get.speed CALL MyVehicle1::accelerate 5 PRINT MyVehicle1::get.speed CALL MyVehicle1::decelerate 10 PRINT MyVehicle1::get.speed DISCARD MyVehicle1 END CLASS Vehicle DIM speed SUB Vehicle init ' constructor with parameter speed = init END SUB

SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code

You probably won't be surprised to learn that as well as a constructor, which is called automatically when an object is created, we can also have a **destructor** which is called automatically when the object is discarded. A destructor has a name consisting of a tilde (~) followed by the name of the class; in this case there is nothing useful for it to do so we will just print a message: code format="LB" NEW MyVehicle1 AS Vehicle 3 ' parameter passed to constructor CALL MyVehicle1::accelerate 10 PRINT MyVehicle1::get.speed CALL MyVehicle1::accelerate 5 PRINT MyVehicle1::get.speed CALL MyVehicle1::decelerate 10 PRINT MyVehicle1::get.speed DISCARD MyVehicle1 END CLASS Vehicle DIM speed SUB Vehicle init ' constructor with parameter speed = init END SUB

SUB ~Vehicle ' destructor print "Destructor called" END SUB

SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code

Inheritance
We have seen how to create a class which represents a vehicle, but there are different kinds of vehicles (cars, bicycles etc.). Suppose we want to create a class to represent a **bicycle**; we could start from scratch and define the properties and methods that a bicycle needs. But a better way is to recognise that a bicycle is a kind of vehicle, so any property or method relevant to a vehicle should also be relevant to a bicycle - although a bicycle may have properties and methods of its own.

We can do that using a variation of the CLASS statement as follows: code format="LB" CLASS Bicycle INHERITS Vehicle code The INHERITS keyword specifies that the class **Bicycle** inherits all the properties and methods of the class **Vehicle**. You can then specify additional properties and additional methods which are needed by a bicycle but not by vehicles in general. Here I have chosen the current gear and the gear ratios: code format="LB" NEW MyBicycle1 AS Bicycle 2,3,5,7,10 ' parameters passed to constructor CALL MyBicycle1::change.up   PRINT MyBicycle1::get.ratio CALL MyBicycle1::accelerate 10 PRINT MyBicycle1::get.speed DISCARD MyBicycle1 END CLASS Vehicle DIM speed SUB Vehicle init ' constructor with parameter speed = init END SUB SUB ~Vehicle ' destructor print "Destructor called" END SUB SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS CLASS Bicycle INHERITS Vehicle DIM gear, ratio(5) SUB Bicycle r1, r2, r3, r4, r5 ' constructor with five parameters ratio(1) = r1       ratio(2) = r2        ratio(3) = r3        ratio(4) = r4        ratio(5) = r5        gear = 1 speed = 3 END SUB SUB change.up       IF gear < 5 THEN gear += 1 END SUB SUB change.down IF gear > 1 THEN gear -= 1 END SUB FUNCTION get.ratio get.ratio = ratio(gear) END FUNCTION END CLASS code The order of declaration is important: ancestor classes must be declared before their descendant classes.

Overriding methods
As we have seen, when a class INHERITS another class it acquires the properties and methods of its ancestor class. However it is still possible to declare a method in the descendant class which has the same name as one in the ancestor class. In that case the declaration in the descendant class takes precedence. For example: code format="LB" NEW MyBicycle1 AS Bicycle 2,3,5,7,10 ' parameters passed to constructor CALL MyBicycle1::change.up   PRINT MyBicycle1::get.ratio CALL MyBicycle1::accelerate 10 PRINT MyBicycle1::get.speed DISCARD MyBicycle1 END CLASS Vehicle DIM speed SUB Vehicle init ' constructor with parameter speed = init END SUB SUB ~Vehicle ' destructor print "Destructor called" END SUB SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS CLASS Bicycle INHERITS Vehicle DIM gear, ratio(5) SUB Bicycle r1, r2, r3, r4, r5 ' constructor with five parameters ratio(1) = r1       ratio(2) = r2        ratio(3) = r3        ratio(4) = r4        ratio(5) = r5        gear = 1 speed = 3 END SUB SUB change.up       IF gear < 5 THEN gear += 1 END SUB SUB change.down IF gear > 1 THEN gear -= 1 END SUB SUB accelerate rate speed += rate * ratio(gear) END SUB

FUNCTION get.ratio get.ratio = ratio(gear) END FUNCTION END CLASS code Here I have re-defined the **accelerate** method so that it takes account of the gear ratio.

Containment
As described in the previous section, inheritance represents an 'is a' relationship: a bicycle **is a** vehicle. A related concept is that of containment, which represents the 'has a' relationship. For example a bicycle **has** wheels. A class can only //inherit// from one ancestor class, but it can //contain// several child classes.

Here is an example. We will first declare a new class **Wheel** which has a property **diameter**, a method to set its value, and a method to get its value: code format="LB" CLASS Wheel DIM diameter

SUB set.diameter d       diameter = d      END SUB

FUNCTION get.diameter get.diameter = diameter END FUNCTION END CLASS code Note that a method which sets the value of a property is sometimes called a **setter** and a method which gets the value of a property a **getter**; together they are known as **accessors**.

We can now add some wheels to our class **Bicycle**: code format="LB" NEW MyBicycle1 AS Bicycle 2,3,5,7,10 ' parameters passed to constructor CALL MyBicycle1::change.up   CALL MyBicycle1::accelerate 10 PRINT MyBicycle1::get.speed PRINT MyBicycle1::get.wheel.diameter(1) DISCARD MyBicycle1 END CLASS Vehicle DIM speed SUB Vehicle init ' constructor with parameter speed = init END SUB SUB ~Vehicle ' destructor print "Destructor called" END SUB SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS CLASS Wheel DIM diameter

SUB set.diameter d       diameter = d      END SUB

FUNCTION get.diameter get.diameter = diameter END FUNCTION END CLASS

CLASS Bicycle INHERITS Vehicle DIM gear, ratio(5), Front AS Wheel, Rear AS Wheel SUB Bicycle r1, r2, r3, r4, r5 ' constructor with five parameters ratio(1) = r1       ratio(2) = r2        ratio(3) = r3        ratio(4) = r4        ratio(5) = r5        gear = 1 speed = 3 CALL Front::set.diameter 12 CALL Rear::set.diameter 12 END SUB SUB change.up       IF gear < 5 THEN gear += 1 END SUB SUB change.down IF gear > 1 THEN gear -= 1 END SUB SUB accelerate rate speed += rate * ratio(gear) END SUB

FUNCTION get.ratio get.ratio = ratio(gear) END FUNCTION

FUNCTION get.wheel.diameter(wheel) SELECT CASE wheel CASE 1: get.wheel.diameter = Front::get.diameter CASE 2: get.wheel.diameter = Rear::get.diameter END SELECT END FUNCTION END CLASS code Note that to call a method in a contained class the //scope resolution operator// is once again used, i.e. **Child::Method**.

Arrays of objects
Up to now we have //instantiated// objects individually, but it is possible to instantiate an **array** of objects. For example if we have five bicycles we can use an array for them: code format="LB" NEW MyBikes(4) AS Bicycle CALL MyBikes(2)::change.up   CALL MyBikes(2)::accelerate 10 PRINT MyBikes(2)::get.speed PRINT MyBikes(2)::get.wheel.diameter(1) DISCARD MyBikes END CLASS Vehicle DIM speed SUB Vehicle init ' constructor with parameter speed = init END SUB SUB ~Vehicle ' destructor print "Destructor called" END SUB SUB accelerate rate CALL this::adjust rate END SUB SUB decelerate rate CALL this::adjust -rate END SUB PRIVATE SUB adjust adj speed += adj END SUB FUNCTION get.speed get.speed = speed END FUNCTION END CLASS CLASS Wheel DIM diameter

SUB set.diameter d       diameter = d      END SUB

FUNCTION get.diameter get.diameter = diameter END FUNCTION END CLASS

CLASS Bicycle INHERITS Vehicle DIM gear, ratio(5), Front AS Wheel, Rear AS Wheel SUB Bicycle ' default constructor ratio(1) = 2 ratio(2) = 3 ratio(3) = 5 ratio(4) = 7 ratio(5) = 10 gear = 1 speed = 3 CALL Front::set.diameter 12 CALL Rear::set.diameter 12 END SUB SUB change.up       IF gear < 5 THEN gear += 1 END SUB SUB change.down IF gear > 1 THEN gear -= 1 END SUB SUB accelerate rate speed += rate * ratio(gear) END SUB

FUNCTION get.ratio get.ratio = ratio(gear) END FUNCTION

FUNCTION get.wheel.diameter(wheel) SELECT CASE wheel CASE 1: get.wheel.diameter = Front::get.diameter CASE 2: get.wheel.diameter = Rear::get.diameter END SELECT END FUNCTION END CLASS code There is one limitation of this technique: you cannot pass parameters to the constructor. The **default** constructor (a constructor with no parameters) will still be called however, and in the above program the necessary initialisation has been done there.

Method Overloading
OOP languages commonly allow you to have multiple methods with the same name but with different **signatures**; in this context a 'signature' means the number of parameters and their types. LBB supports this too, although only the number of parameters is distinguished. One use for this facility is to have multiple constructors; which constructor is called will depend on how many parameters are specified in the NEW statement: code format="LB" NEW MyVehicle1 AS Vehicle NEW MyVehicle2 AS Vehicle 5

PRINT MyVehicle1::get.speed PRINT MyVehicle2::get.speed

DISCARD MyVehicle1 DISCARD MyVehicle2 END

CLASS Vehicle DIM speed

SUB Vehicle  ' constructor with no parameters speed = 2 END SUB

SUB Vehicle s ' constructor with one parameter speed = s     END SUB

SUB accelerate rate speed += rate END SUB

SUB decelerate rate speed -= rate END SUB

FUNCTION get.speed get.speed = speed END FUNCTION END CLASS code Note that if you supply a **default** constructor, which takes no parameters, this will always be called, even if another constructor is called as well.