=====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:\\
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
\\ 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:\\
NEW MyVehicle1 AS Vehicle
\\ 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:\\
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
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:\\
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
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:\\
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
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:\\
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
\\ 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:\\
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
\\
===== 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:\\
CLASS Bicycle INHERITS Vehicle
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:\\
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
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:\\
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
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:\\
CLASS Wheel
DIM diameter
SUB set.diameter d
diameter = d
END SUB
FUNCTION get.diameter
get.diameter = diameter
END FUNCTION
END CLASS
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**:\\
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
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:\\
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
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:\\
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
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.