IMPLEMENTING OBJECT ORIENTED PROGRAMMING IN VISUAL BASIC

(Read and print this with NotePad, left margin .25, right margin .25)

SUMMARY

This is a short document describing how some of the key features of Object
Oriented (OO) programming can be implemented in a language that is not OO,
such as Visual Basic. The key OO features covered are Encapsulation,
Polymorphism and Inheritance. Using these allows an application to be more
component based than use of procedural approaches alone.

FORWARD

The goal of these three features is improved understandability and
reusability as a result of subdividing an application into components. Each
component can be easily understood because it is small and serves a single,
simple purpose. Component use is easily understood because all components
have a similar interface. Component relationships are vastly simplified if,
as each component is created, it is assigned a place in the "application
reuse hierarchy". Thus the conceptual integrity of an application remains
more understandable and therefore under control as the application grows
in size.

Numerous developers have asked me about some of the techiniques and terms
I've mentioned. Well, here it is for all to poke around. Now take it easy!

These examples are in VB 3.0


=============================  ENCAPSULATION  ========================

Much of this will change due to classes in VB4. However the concept remains
the same.

Encapsulation is creating a component that does one thing and contains (or
calls) everything it needs to do it. It may be treated as a black box - the
caller only needs to know how to use it. The caller cannot manipulate the
component's internals directly, as this would violate the black box concept.

The standard OO encapsulated component interface is object name, properties
and methods. This is used for VB forms and controls, and throughout the
Visual Basic language whenever objects are manipulated.

Here is a useful example:   ("_" is line continuation character)

--------------------------------------------------------------------------
Sub Hourglass(Method)
   
Static Count As Integer ' Set to 0 when initialized
Static OldPointer As Integer
    
    ' Perform Method
    Select Case Method
        Case "Show"
            ' Increment count
            Count = Count + 1
            ' If this is the first call then save pointer before changing
            If Count = 1 Then OldPointer = Screen.MousePointer
            ' Change cursor if not already an hourglass
            If Screen.MousePointer <> HOURGLASS_SHAPE Then _
		 Screen.MousePointer = HOURGLASS_SHAPE

        Case "Hide"
            ' Decrement count
            Count = Count - 1
            ' If no more requests to Show hourglass then set back to old
            If Count < 1 Then Screen.MousePointer = OldPointer: Count = 0

        Case Else
            ' (put your error handling here)

    End Select

End Sub
---------------------------------------------------------------------------

Which is typically used like:

Hourglass "Show"
(do something that takes awhile)
Hourglass "Hide"

One advantage is that no problems occur when you nest procedures using
Hourglass. Another is that anytime you want to you can implement your own
application wide hourglass animation, such as a sleeping elf that winks at
you every second, while your logo dances around somewhere. HOURGLASS_SHAPE
is a global constant equal to 11.

The alternative to encapulating this is Count and OldPointer as module
variables and ShowHourglass and HideHourglass as separate subs. This makes
the logic MUCH more difficult to understand and maintain.


=============================  POLYMORPHISM  ========================

Polymorphism in OO is the ability to send a message (method) to an object
and, depending on what kind of object it is, have it respond correctly. A
simple example is Debug.Print "Test" and Printer.Print "Test" will print
"Test" to the immediate window or the Printer object.

Polymorphism allows huge amounts of code reusability and simplification. It
requires a standard component interface, such as the 
"Object.Method Argument1" example used above.

This may change in VB4, but my technique for implementing polymorphism in
VB3 follows. For each class of objects, define a standard interface, such
as: Function ObjectName(Method, Argument1, Argument2, Options)

Here is a highly simplified example:

---------------------------------------------------------------------------
Function Certification_O(Method, Arg1, Arg2, Options)

Static Initialized As Integer
Static F as Form

   Select Case Method
      Case "Show"
         If Not Initialized Then
            Initialized = True
            Set F = frmCertification
            (more as necessary)
         End If
         F.Show
      Case "cmdSave_Click"
         (put your save logic here)
      Case "cmdCancel_Click"
         (put your cancel logic here)
      Case "Destroy"
         If Initialized Then
            Initialized = False
            Unload F
            Set F = Nothing
         End If
      Case Else
         (put your error handler here)
   End Select

End Function
---------------------------------------------------------------------------
Function DoMethod(ObjectName, Method, Arg1, Arg2, Options)
   Select Case ObjectName
      Case "Certification_O"
         DoMethod = Certification_O(Method, Arg1, Arg2, Options)
      Case "Registration_O"
         DoMethod = Registration_O(Method, Arg1, Arg2, Options)
      Case Else
         (put your error handler here)
   End Select
End Function
---------------------------------------------------------------------------
Function EditPages_O(Method, Arg1, Arg2, Options)

Static CurrentPage as Integer
Static Page() as String

Dim I as Integer

   Select Case Method
      Case "Start"
         CurrentPage = 1
         Redim Page(1 to 2)
         Page(1) = "Certification_O"
         Page(2) = "Registration_O"
         Dummy = DoMethod(Page(CurrentPage), "Show", "", "", "")
      Case "PreviousPage"
         If CurrentPage > 1 Then
            CurrentPage = CurrentPage - 1
            Dummy = DoMethod(Page(CurrentPage), "Show", "", "", "")
         Else
            Beep
         End If
      Case "NextPage"
         If CurrentPage < UBound(Page) Then
            CurrentPage = CurrentPage + 1
            Dummy = DoMethod(Page(CurrentPage), "Show", "", "", "")
         Else
            Beep
         End If
      Case "Finish"
         For I = LBound(Page) to UBound(Page)
            Dummy = DoMethod(Page(CurrentPage), "Destroy", "", "", "")
         Next
         Erase Page
      Case Else
         (put your error handler here)
   End Select

End Function
---------------------------------------------------------------------------

Oh no, now the cat's out of the bag! The implications of the above are very
powerful. Let's make a few observations:

- The logic of Certification_O is very clear.
- DoMethod is a "registry" of all "object functions" having the standard
    interface ObjectName(Method, Arg1, Arg2, Options). Note the standard
    suffix of "_O". 
- The logic of EditPages_O is very clear.
- EditPages_O is very clear because it is concise due to polymorphism which 
    is implemented with the DoMethod call, remaining clear with 20 pages.
- EditPages_O can be made reusable by modifying the "Start" method. Pass
    the object names in Arg1 as a comma delimited list.
- Certification_O is a reusable "template" for similar objects.
- Arg1 and Arg2 can be used to pass KeyCode, Index, etc from events.
- DoMethod also supports the ObjectName being unknown until runtime.
     This is very powerful and is known as late binding.
- Dummy is a global variant used to indicate an irrelevant return value.


=============================  INHERITANCE  ========================
This is more difficult to implement. Suppose Certification_O and
Registration_O are so similar you can use the same Save and Cancel logic.
This is very easy, especially as your code become more parameter driven.

One important rule of good programming is "Never do the same thing more
than one place in the code". This of course is why procedure calls were
invented. But this rule becomes difficult to follow for procedures like
Certification_O unless it is implemented "automatically" with inheritance.
An example follows:

---------------------------------------------------------------------------
Function Certification_O(Method, Arg1, Arg2, Options)

Static Initialized As Integer
Static F as Form

   Select Case Method
      Case "Show"
         If Not Initialized Then
            Initialized = True
            Set F = frmCertification
            (more as necessary)
         End If
         F.Show
      Case "Destroy"
         If Initialized Then
            Initialized = False
            Unload F
            Set F = Nothing
         End If
      Case Else
         Certification_O = MasterEdit(F, Method, Arg1, Arg2, Options)
   End Select

End Function
---------------------------------------------------------------------------
Function MasterEdit(F as Form, Method, Arg1, Arg2, Options)
   Select Case Method
      Case "Save"
         (put your save logic here)
      Case "Cancel"
         (put your cancel logic here)
      Case Else
         (put your error handler here)
   End Select
End Function
---------------------------------------------------------------------------

My gosh! Will this work? I've used it extensively with no problems. Note
that inhertance argument lists should be of the format:

([Instance], Method, [usual standard arguments])

[Instance] is what the superclass needs to perform its delicate operations
on. It is one or more arguments, and may include recordsets, user defined
types, etc.

[usual standard arguments] in this case is Arg1, Arg2, Options.


=============================  A BIT MORE  ========================

That covers "faking OO in VB" in a nutshell. Here's a bit more....

For increased reusability and extendability Options is a "named string
list" that is of the format "|Name1=Value1|Name2=Value2|". "|" or any other
delimiter may be used. NameX is the name of an optional parameter, such as
"FontSize", "Required", "Visible", etc. ValueX is the string value for
NameX.

I use Options as the last argument in about 80% of my argument lists. It
has saved me time and time again when modifying code, keeping argument
lists short, and most importantly keeping argument lists identical so I can
use DoMethod. VB4 has something like it.

A good programming standard is to use a named string list in Options,
Form.Tag, Control.Tag, Command$, and everywhere extendability or properties
are desired. When used in form and control tags it allows giving them
custom properties.

To extract ValueX use Value = GetValue(Options, "NameX"). GetValue() has
become the most used procedure in my code. The listing follows. Note the
use of RetVal, another good programming standard. Any delimiter may be used.
Note that the delimiter must be at the beginning and end of the named
string list. ValueX may equal "". Of course SetValue() quickly becomes
useful as you implement named string lists. To keep this document short,
SetValue() is not included.

---------------------------------------------------------------------------
Function GetValue (List, ElementName)

Dim Delimiter As String
Dim Position As Integer
Dim RetVal As String
Dim WorkList As String
    
    Delimiter = Left(List, 1)
    If Right(List, 1) <> Delimiter Then
        RetVal = "#Error#"
    Else

        ' Extract all starting at ElementName
        Position = InStr(List, Delimiter & ElementName & "=")
        If Position = 0 Then
            RetVal = "#Error#"
        Else
            WorkList = Mid(List, Position + 1)
        
            ' Extract all beyond "="
            Position = InStr(WorkList, "=")
            WorkList = Mid(WorkList, Position + 1)
        
            ' Extract all before next delimiter
            Position = InStr(WorkList, Delimiter)
            RetVal = Left(WorkList, Position - 1)
        End If

    End If

    ' Return value. It may be "".
    GetValue = RetVal

End Function
---------------------------------------------------------------------------

>>>=====>  STANDARDS  <=====<<<

The use of the above techniques requires the use of programming standards.
This is especially true since we are "faking" OO, rather than using
constructs built into the language. Standards work best if they are
developed and maintained by and for the teams that use them. Examples of 
standards are:

- Using primitives and subsystems from the library instead of building
      your own
- Using standard skeletons as templates for certain types of code
- Adhering to established application architecture
- Producing reentrant code whenever possible
- Visual: Color, screen layout, standard bitmaps to use, etc
- Which 3rd party components and tools to use, such as VBX's
- Readability: Naming conventions, indentation, white space, comments, etc
- Producing reusable components whenever possible even if it takes 100%
      longer. But don't go to extremes.
- When to use encapsulation, polymorphism, inheritence and when not to
- Examples of how to use the above OO techniques. This is best done by
      referring to existing code and using templates.


>>>=====>  EPILOGUE  <=====<<<

I have very productively used the above in commercial applications. The
code is easier to design, develop and maintain. The number and power of
reusable primitives, components and sub-systems is MUCH greater than with
procedural code alone.

In a trial test of OO versus procedural the procedural project took about
1,440 hours while the OO project took exactly 614 hours. Both implemented
the same requirements. However the code behind them was very different.
The OO project resulted in an excellent 38% reusable code, 22% reusable
forms and 25% reusable tables. The procedural project reusables were less
than 10% code and 0% forms and tables. Metrics for the OO project were:
   40 forms
   2 Access databases totaling 24 tables
   28 API's
   17 modules totaling 199 procedures and 14,250 lines of code
      (42% of this code was comments and white space. Very understandable.)

However the trial was biased. The procedural project was developed first.
The OO programmer used the procedural project as guidance and different
programmers were used. The OO programmer was more organized and productive.
In spite of this bias the OO project results were very impressive.

Of interest is the procedural project was never finished due to a high
defect rate in testing. (Defects were runtime errors and low usability) It
was unshippable and deemed impossible to patch, and ultimately declared a
throwaway prototype, much to the programmer's and management's surprise.
The OO project had a very low defect rate in all alpha and beta releases,
and was easily shippable.

One might object (no pun intended) that a rewrite is always faster and
better. Very true. However in this case some controls on four forms were
reused. Seven tables were reused with modifications. Everything else was
sadly developed from scratch including 100% of the code. The functionality
presented to the user was very different. The procedural project only
implemented about 2/3 of the requirements and was stopped at that point.

Of additional interest is that when the OO programmer showed his code to
the procedural programmer, the procedural programmer went ballistic. He
showed the OO code to a third party (the GUI department of a 60 person
shop), getting them to say that it was terrible, not up to their standards,
and would be unacceptable in their shop. Armed with this ammunition he took
his case to management, adding that the OO code was hard to understand.
Management directed the OO programmer to stop using OO techniques. He
secretly refused, and nervously continued the rewrite using OO. In a few
more weeks management noticed the OO project was proceeding with blinding
speed, high usability and near zero runtime errors. The OO programmer was
given carte blanche to continue the project as he saw fit, and the
procedural programmer was told to study the OO code. The procedural
programmer could not swallow this, and left two months later.

Such is life in the OO lane. It is a shame that change is sometimes so
traumatic. The reader is hearby warned that the OO techinques presented
here are novel and their introduction should be approached thoughtfully.



>>>=====>  REUSABILITY  <=====<<<

Note that you don't get substantial reuse until you have reusable subsystems,
not just primitives. Some of the generic OO reusable subsystems were:
   AppForm - Allows banded forms: Title bar, optional upper button bar,
     middle form (the main form), optional lower button bar, status bar.
     Supports form types and handles all housekeeping: Dialogue, lone
     dialogue, middle, button bars, etc. Handles all form positioning and
     resolution independence. Handles online help system.
   SystemParameter - Allows get/set of application configuration parameters,
     such as Develop mode, test mode, file locations, database locations,
     various passwords, defaults, positions, etc. Table driven.
   HelpfulHints - Uses paragraphs of text in table and reusable form to
     show helpful hints to user. More friendly than online Help system.
     Uses Mabrey's FLabel.VBX to allow text formatting.
   EF - Edit Form - Suports groups of fields on a form, associates each
     with recordset, handles all manipulation given RecordSource and control
     parameters, which are stored in control tags. Supports field types and
     validation, form update, recordset update, required, special edit
     events, etc per field type, which are implemented as objects. The
     viable alternative to bound controls, which are very limiting.
   Select_O - Reusable code driving multiple forms, each of which allows
     selection from a listbox or input if not listed.
   Reports - Uses Printer object, API's to get portions or all of form, prints
     reports. Much better than Crystal for reports that are not table oriented.
     Uses paragraph styles for extreme reusability.
   EditPages_O - Allows a group of forms to be presented and edited as pages.
     Handles dirty state, button availablity, showing forms, etc.


---------------------------------------------------------------------------
Comments and suggestions are welcome. We shall see how much of the above
changes with VB4 and beyond.

With a little help from my friends,

Happy Jack <bg>


        Jack Harich    9/10/95   (76741.163@Compuserve.com)

