OOP In Non-OOP Languages
Contrary to popular belief object oriented programming does not require an OO language.
What you get with an OO language is a set of built in constructs that assist you in writing OO programs but in many cases they are unnecessary and sometimes they are counterproductive.
Anyway, this isn't a rant against OO languages but rather a rant against the unquestioning acceptance of the idea that a specifically OO language is necessary to write object oriented programs.
In order to demonstrate that it is not necessary to have an OO language this example presents a technique that is usually presented as an example of class based programming; and so it is but you won't find the word class in this example.
If you have to concatenate a lot of strings in most Basics you usually find that it is a time consuming process. Actually FreeBASIC string operations are remarkably quick but you can still do better using a string builder.
A string builder is simply a class that maintains a string buffer in such a way as to avoid repeated calls to the memory allocation function because this is a relatively expensive operation. The methods of the class provide ways of manipulating the buffer and converting between it and the native string type.
The trick that makes it faster than the built type for large strings and large numbers of appends is that the string is held in a heap allocated buffer that is always larger than the actual length of the string. This means that appending to the end of the string usually simply means copying the contents of the new string to the memory location following the last character of the current string. In this implementation the buffer is a ZString so it is easy to convert it to an ordinary dynamic string.
The FreeBASIC module encapsulates a type definition for a struct. Instances of this struct hold the attributes of the object. The methods are simply normal FreeBASIC public functions and subs defined in the same module. When you want to call a method you use the normal FreeBASIC syntax:
`
s = StringB_ToString(AStringBInstance)
`
By convention all methods names begin with the name of the class and an underscore and the first argument is always the instance of the type. This argument should always be passed by reference to ensure that changes to the state are permanent and also to avoid unnecessary, time-consuming, copying.
To add a new method you simply add a new function or sub following those rules.
You can easily implement composition of objects but inheritance in the usually expected ways can't be done. You can extend classes simply by defining new functions elsewhere that take arguments of the class type. If the original class defines all of its methods as overloaded you can even create new methods of the same name so long as they have different signatures.
Here is the example code:
`
'-----------------------------------------------------------------------------
' Classes without built in oop.
' Define a struct for the properties and a sub or function for each
' method. Pass the struct as the first argument in all calls.
' By convention the argument will be Me as in VB Classic
' Strings in FB are so fast that a string builder class is
' not needed most of the time but if you are concatenating
' thousands of strings to build web pages for instance this might be useful.
' And please don't start complaining about the lack of inheritance; that
' is not a requirement for the use of objects. There is no legal definition of
' Object Oriented Programming but the most important part of any definition
' is the close association between the data and the code that manipulates it.
'You can easily extend this class to provide more methods.
'-----------------------------------------------------------------------------
Type StringB
Len As Integer ' used length
allocated As Integer
s As ZString Ptr ' buffer of at least len characters
End Type
'-----------------------------------------------------------------------------
' Create a new StringB by calling one of these constructors.
'-----------------------------------------------------------------------------
Public Function StringB_New Overload (ByVal InitialSize As Integer) As StringB
Dim sb As StringB
sb.allocated = InitialSize
sb.s = Allocate(InitialSize)
*sb.s = ""
StringB_New = sb
End Function
Public Function StringB_New(ByRef InitialValue As String) As StringB
Dim sb As StringB
sb = StringB_New(Len(InitialValue))
*sb.s = InitialValue
sb.len = Len(InitialValue)
StringB_New = sb
End Function
Public Sub StringB_Dispose(ByRef Me As StringB)
Deallocate Me.s
End Sub
Public Function StringB_ToString(ByRef Me As StringB) As String
StringB_ToString = *Me.s
End Function
Sub StringB_Append Overload(ByRef Me As StringB, ByRef s As String)
Dim i As Integer = Me.len
Me.len += Len(s)
If Me.len >= Me.allocated Then
Me.allocated = 2*Me.len
Dim As ZString Ptr p = Reallocate(Me.s, Me.allocated )
If p=0 Then
' failed to reallocate
Print "StringB_Append failed to reallocate", Me.allocated
Return
End If
Me.s = p
End If
*(Me.s + i) = s
End Sub
Sub StringB_Append(ByRef Me As StringB, ByRef other As StringB)
StringB_Append Me, StringB_ToString(other)
End Sub
`