Chapter 3

Objects and Visual Basic


CONTENTS

I remember a course from my sophomore year in college titled ICS2 (Information and Computer Science #2-go figure). It was several weeks into the course, and I was struggling to understand the concept of class in a language called Simula. For those of you who are curious, Simula is one of those languages college professors seem to love, but they never catch on, and nobody can quite understand why. Think of it as a "better" kind of Pascal (before Pascal fragmented into almost as many dialects as there are programmers).

Anyway, up until then I'd been programming in Basic, which also has fragmented into many dialects, but nobody seems to care. The professor must have explained the idea of class a dozen times, but I just didn't get it. How can a data structure contain a function? It just didn't make any sense.

Then one day lightning struck, figuratively speaking, and I got it. I not only understood what classes were all about, but I realized they were a phenomenal tool for building applications. I had discovered object-oriented programming and have used it ever since, at least where it was supported by the language I was using.

When people talk about the relative importance of features introduced in Visual Basic 4.0, they often talk about the 32-bit support, the support for OLE automation (excuse me, Active X) .DLLs, performance improvements, or other features. For me, the single most important feature in Visual Basic 4.0 was the introduction of the class module, not because it supported servers or OLE automation, but because I had classes back. I could finally apply object-oriented techniques in my favorite language.

Now, if you are comfortable programming with classes, if you appreciate their importance, and if your first step in designing any VB application is to consider what classes abstract the data and functionality of your project, then you can skim through and perhaps even skip this chapter. But if you routinely create applications that do not use class modules, if you think they are only important for creating programmable objects (EXE or DLL servers), or if you have no idea why I would be so excited about class modules that I would call them the single most important advance from VB3 to VB4, this chapter is for you. Even if you are an absolutely beginning Visual Basic programmer.

The Theory of Object-Oriented Programming

An object, in the context of object-oriented programming, exhibits the following three characteristics:

Whoa.... Let's back up for a moment.

Object-oriented programming is one of those terms in computer science that have a number of unfortunate side effects.

Wrong on both counts.

It's not surprising that the term produces this reaction when people so often use terms like encapsulation, polymorphism, and inheritance to describe it.

You know, I probably have read about the true definition of object-oriented programming, the necessity for polymorphism, and the trade-offs between inheritance and aggregation dozens of times. But I never seem to be able to remember all of the academic arguments. And since VB5 isn't a true object-oriented language anyway (by the strict academic definition), I don't think it really matters.

In fact, now that I think about it, it seems to me that studying the theory of object-oriented programming has to be about the worst possible way to learn it. Perhaps we can find a different approach and come back to the theory later if necessary.

So let's just skip the rest of this section.

Objectless Programming

I have an ambitious goal here. I don't want to just explain object-oriented programming. I want to convert you to this way of programming. I want to convince you it is the only way to program. I believe this will not only make your ActiveX programming efforts more successful, it will also allow you to improve all of your software efforts.

Theory is very interesting, but in this case I don't trust it to prove my point. Instead, let's start by looking at the way many people program and examining how object-oriented programming techniques can improve on that approach.

In other words, let's code.

A simple application should do. Consider a simple retail sales application-the kind that you might create for a fast-food restaurant. Figure 3.1 shows the main form for the Retail1 project. There are four buttons, each for a different type of food. Clicking on a button adds the specified item to the bill. The clear button clears the current order. When you enter an amount paid, the change box will show the amount of change due. You can double-click on an item in the list box to reduce the order for the specified item.

Figure 3.1 : Main form for Retail1 application.

It's a simple application and relatively foolproof, even though it lacks some of the data validation one would add to a commercial quality application. For example, if you add a $ sign to the Paid box, it will not parse the value correctly.

This sample was coded by drawing the user interface, adding a couple of arrays to hold the price and item count, then attaching code to events where it makes sense. Listing 3.1 shows the code for this implementation. Two zero-based arrays are used in this application. Zero-based simply means that index zero of the array contains useful information. The prices(3) menu thus contains four entries (0 to 3), each of which will be loaded with the price of a food item. The four command buttons are a zero-based control array. The captions of the buttons contain the name of the menu items. The items array contains a count representing the quantity of each menu item ordered.


Listing 3.1: Listing for Form frmRetail1.frm
' Retail example #1
' ActiveX: Guide to the Perplexed
' Copyright (c) 1997 by Desaware Inc.


Option Explicit


' Zero based array of items
Dim prices(3) As Currency  ' Prices of menu entries
' Zero based quantities
Dim items(3) As Integer


Private Sub Form_Load()
   prices(0) = 1.99
   prices(1) = 0.89
   prices(2) = 1.29
   prices(3) = 8.94  ' A bargain at any price
   UpdateAll
End Sub


' Add an item to the order
Private Sub cmdMenu_Click(Index As Integer)
   items(Index) = items(Index) + 1
   UpdateAll
End Sub


' Clear this order
Private Sub cmdClear_Click()
   Dim itemnum%
   For itemnum = 0 To 3
      items(itemnum) = 0
   Next itemnum
   UpdateAll
End Sub


' Updates the contents of the list box
Public Sub UpdateListBox()
   Dim itemnum%
   lstItems.Clear
   For itemnum = 0 To 3
      If items(itemnum) > 0 Then
         lstItems.AddItem items(itemnum) & " x " & cmdMenu(itemnum).Caption
         lstItems.ItemData(lstItems.NewIndex) = itemnum
      End If
   Next itemnum
End Sub


' Delete an item on double click
Private Sub lstItems_DblClick()
   Dim thisitem%
   thisitem = lstItems.ItemData(lstItems.ListIndex)
   items(thisitem) = items(thisitem) - 1
   UpdateAll
End Sub


' Calculate the total price
Public Function CalculateTotal() As Currency
   Dim itemnum%
   Dim total As Currency

   For itemnum = 0 To 3
      total = total + items(itemnum) * prices(itemnum)
   Next itemnum
   CalculateTotal = total
End Function


' Display the total and change
Public Sub UpdateTotals()
   Dim total As Currency
   Dim paid As Currency
   total = CalculateTotal()
   lblTotal.Caption = Format(total, "Currency")
   paid = Val(txtPaid.Text)
   If paid - total > 0 Then
      lblChange.Caption = Format(paid - total, "Currency")
   Else
      lblChange.Caption = "Please pay"
   End If
End Sub


Private Sub txtPaid_Change()
   UpdateTotals
End Sub


Public Sub UpdateAll()
   UpdateListBox
   UpdateTotals
End Sub

How is this code flawed? The major obvious error is that the number of items on the menu is hard coded. This means not only can the menu items not be changed dynamically, but changing them requires recoding the application. This may be fine for a quick and dirty prototype, but it's terrible design.

We won't even discuss the user interface. Its only saving grace is that it is simple. It wouldn't take you more than a few minutes to train someone to use the program.

And it could have been worse-much worse.

You can see that even in this quick throw-together code, there are some good ideas. The prices are in an array. True, its size is hard coded, but it suggests the possibility of run-time price changes in the future. The command buttons are in an array as well. Perhaps run-time menus will be possible? What about custom pricing-you might want to have a separate senior menu? Having the pricing in an array makes this possible.

The code has some modularity. The list box updating is centralized, as is the totaling. This means that anytime code changes something that might affect the current order, it need only call the UpdateAll function-it does not have to worry about dealing with the list box or total fields directly.

Let's implement some of these obvious improvements as shown in Listing 3.2.


Listing 3.2: Listing Form frmRetail2.frm
' Retail example #2
' ActiveX: Guide to the Perplexed
' Copyright (c) 1997 by Desaware Inc.


Option Explicit


' Zero based array of items
Dim prices() As Currency  ' Prices of menu entries
' Zero based quantities
Dim items() As Integer
' ID of the next menu item
Dim NextItem As Integer


' Clear this order
Private Sub cmdClear_Click()
   Dim itemnum%
   For itemnum = 0 To UBound(items)
      items(itemnum) = 0
   Next itemnum
   UpdateAll
End Sub


' Add an item to the order
Private Sub cmdMenu_Click(Index As Integer)
   items(Index) = items(Index) + 1
   UpdateAll
End Sub


Private Sub Form_Load()
   mnuStandard_Click
End Sub


' Updates the contents of the list box
Public Sub UpdateListBox()
   Dim itemnum%
   lstItems.Clear
   For itemnum = 0 To UBound(items)
      If items(itemnum) > 0 Then
         lstItems.AddItem items(itemnum) & " x " & cmdMenu(itemnum).Caption
         lstItems.ItemData(lstItems.NewIndex) = itemnum
      End If
   Next itemnum
End Sub


' Calculate the total price
Public Function CalculateTotal() As Currency
   Dim itemnum%
   Dim total As Currency

   For itemnum = 0 To UBound(items)
      total = total + items(itemnum) * prices(itemnum)
   Next itemnum
   CalculateTotal = total
End Function


' Display the total and change
Public Sub UpdateTotals()
   Dim total As Currency
   Dim paid As Currency
   total = CalculateTotal()
   lblTotal.Caption = Format(total, "Currency")
   paid = Val(txtPaid.Text)
   If paid - total > 0 Then
      lblChange.Caption = Format(paid - total, "Currency")
   Else
      lblChange.Caption = "Please pay"
   End If
End Sub


' Delete an item on double click
Private Sub lstItems_DblClick()
   Dim thisitem%
   thisitem = lstItems.ItemData(lstItems.ListIndex)
   items(thisitem) = items(thisitem) - 1
   UpdateAll
End Sub


' Set a standard menu
Private Sub mnuStandard_Click()
   ClearMenuItems
   AddMenuItem "Hamburger", 1.99
   AddMenuItem "Curly Fries", 1.32
   AddMenuItem "Side Salad", 2.39
   AddMenuItem "Chicken", 2.32
   AddMenuItem "Stuff Delux", 5.39
   UpdateAll
End Sub


' And a senior citizen's menu
Private Sub mnuSenior_Click()
   ClearMenuItems
   AddMenuItem "Hamburger", 1.79
   AddMenuItem "Fries", 1.12
   AddMenuItem "Chicken Soup", 2.39
   AddMenuItem "Salad", 2.12
   AddMenuItem "LoCal Delux", 5.19
   UpdateAll
End Sub


' and, of course, a programmer's menu
Private Sub mnuProgrammer_Click()
   ClearMenuItems
   AddMenuItem "Hamburger", 2.19
   AddMenuItem "Pizza", 2.52
   AddMenuItem "Cheetos", 0.89
   AddMenuItem "Jolt", 1.19
   AddMenuItem "More Jolt", 2.19
   UpdateAll
End Sub


Private Sub txtPaid_Change()
   UpdateTotals
End Sub


Public Sub UpdateAll()
   UpdateListBox
   UpdateTotals
End Sub


' Create a menu item
Public Sub AddMenuItem(ByVal itemname$, ByVal itemprice As Currency)
   Dim currenttop%
   If NextItem > 0 Then
      ' Don't bother creating button 0
      Load cmdMenu(NextItem)
   End If
   ReDim Preserve items(NextItem)
   ReDim Preserve prices(NextItem)
   prices(NextItem) = itemprice
   items(NextItem) = 0
   cmdMenu(NextItem).Caption = itemname
   cmdMenu(NextItem).Top = cmdMenu(0).Top + NextItem * 450
   cmdMenu(NextItem).Visible = True

   NextItem = NextItem + 1
End Sub


' Clear all of the menu items.
Public Sub ClearMenuItems()
   Dim itemnum%
   Dim currenttop%
   If NextItem = 0 Then Exit Sub ' Already clear
   currenttop = UBound(items)
   For itemnum = 1 To currenttop
      Unload cmdMenu(itemnum)
   Next itemnum
   ReDim items(0)
   ReDim prices(0)
   NextItem = 0
End Sub

Some of the changes here are obvious. All of the looping routines are now based on the upper bound of the arrays, so they will work correctly regardless of the number of items on the menu. The menu items themselves are created dynamically using the AddMenuItem and ClearMenuItems functions. A pop-up menu (Windows menu, not food menu) has been added to support multiple menus.

Take a few minutes to look over these two samples. Try running them from the CD. You'll want to be sure you understand how they work when considering the scenario in the next section.

Has This Ever Happened to You?

So, you've just delivered your simple point-of-sale program, on time and under budget, of course. The customer (or your manager) looks it over, tests it out using real employees, and everybody is raving about it. Then you hear those words that send chills down every programmer's spine: "I'd just like a few minor changes."

They continue, "You see, we think we can really improve efficiency by having each register hold several orders at once. That way the cashier can take a second order while waiting on the first. All you need to do is add a few buttons so the cashier can switch between orders. But don't worry, we don't need to support more than three or four menus at a time, and I can see there is plenty of space on the form."

What do you need to do to change the retail2.vbp example to support this "minor" modification?

Well, you need multiple orders, and since each order is kept in an array, that means multiple array-perhaps a two-dimensional array? But then how can we get the redimensioning to work properly? Remember that each order may have a different menu associated with it. And every time you switch between orders you need to switch the menu.

You suddenly realize you are in deep trouble, because your program simply wasn't designed with this possibility in mind. Sure, you can fix the problem-but the changes will affect virtually every function in the program. It will almost be like writing the program from scratch.

Once Again, with Objects

Let's look at the problem again, but this time with some thought toward proper design.

This time, we'll use Visual Basic's ability to define objects when designing the application. This section does assume that you have at least a beginner's familiarity with how to add a class module to your application, and how to add methods and properties to your class module. If this is completely new to you, you may want to take a look at the Visual Basic 5.0 programmer's guide in the VB5 books online.

In this book, the chapter "Programming with Objects" in Part 2 includes a section called "Class Module Step by Step" that walks you through the process of creating a class module and adding a method. The process of adding functions and subroutines to a class module is identical to adding them to a form, so you should find the code in the following examples easy to follow even if you have never used class modules before.

The register application allows a cashier to select items from a fixed menu to build an order. Each item on the menu has a price and a name. The order may contain one or more items from the menu. An order also has a total price that can be derived from the list of items. Conceptually, we're looking at three objects: a menu, menu items, and an order.

Each menu contains one or more menu items, each of which has a price and a name. An order contains one or more menu items, with an associated quantity for each one.

Keep in mind here that I'm referring in this section to a food menu in a fast-food cash register application. This has nothing to do with Visual Basic menus and menu objects, the kind that drop down from the top of a window. (I realize that this may seem obvious to you, but I assure you that if I did not clarify this here, I would be destined to receive any number of e-mail messages from people wondering about the relationship between hamburgers and pop-up menus.)

On a more serious note, as with most of the examples in this book, there is no one right design for a given application. In most cases there will be many different solutions, even if you take an object-oriented approach.

One critical decision to make when implementing the menu object is to decide whether a menu item will be an object or an entry in an array. In the Retail1 and Retail2 samples, each menu item had its price stored in the prices array and the name stored in the button caption. This was a rather awkward solution, to put it kindly. Two obvious solutions come to mind:

  1. Keep the menu items in an array of prices and names. This approach is efficient-redimensioning an array is a fast process. But it makes it difficult or impossible to work with individual menu items outside of the menu class.
  2. Define a new class to hold the menu items. This approach makes it possible to expose the menu item object as a subobject for a menu or to easily pass it as parameters to other routines in the application, should it become necessary.

In this case, I decided that menu items will only be an internal construct for the clsMenu1 class. As such, it really doesn't matter to the rest of the program how they are implemented. The item list exists as an array of a user-defined type. This eliminates the need to manipulate separate arrays for each item. Using a user-defined type is also consistent with an object-oriented methodology. You can think of a user-defined type as a very simple type of object that only has properties, though it is not implemented as a COM object. Listing 3.3 shows the implementation for a menu object.


Listing 3.3: Listing for clsMenu1, the Menu Object
' Retail example #3
' ActiveX: Guide to the Perplexed
' Copyright (c) 1997 by Desaware Inc.


Option Explicit
' A type describing the item
Private Type MenuItem
   ItemName As String
   ItemPrice As Currency
End Type


Private MenuList() As MenuItem   ' Array of menu items
Private NextItem As Integer      ' Next item to load
' Method to add menu items to the clsMenu object
Public Sub AddMenuItem(ByVal ItemName$, ByVal ItemPrice As Currency)
   Dim currenttop%
   ReDim Preserve MenuList(NextItem)
   MenuList(NextItem).ItemPrice = ItemPrice
   MenuList(NextItem).ItemName = ItemName
   NextItem = NextItem + 1
End Sub


' Retrieve the number of items in this menu
Public Function ItemCount() As Integer
   ItemCount = NextItem
End Function


' Expose the price and name as functions
Public Function ItemPrice(ByVal itemnum As Integer)
   ' We can do error checking
   If itemnum >= NextItem Then Exit Function
   ItemPrice = MenuList(itemnum).ItemPrice
End Function


Public Function ItemName(ByVal itemnum As Integer)
   ' We can do error checking
   If itemnum >= NextItem Then Exit Function
   ItemName = MenuList(itemnum).ItemName
End Function

The clsMenu1 object has an AddMenuItem method for adding individual menu items. The ItemName and ItemPrice functions allow you to retrieve the name and price for any item.

An order consists of an array of quantities for each item on a menu. This means each order must have an associated menu, which in this case is whichever menu is currently in use by the application. This was implemented by the "items" array in the Retail1 and Retail2 example, but we can isolate that implementation from the rest of the program by encapsulating it into a class, as shown in Listing 3.4.


Listing 3.4: Listing for clsOrder1, the Order Object
' Retail example #3
' ActiveX: Guide to the Perplexed
' Copyright (c) 1997 by Desaware Inc.


Option Explicit
Dim ItemList() As Integer
' Start the array off with at least one entry
Private Sub Class_Initialize()
   ReDim ItemList(0)
End Sub


' Clear the current order
Public Sub Clear()
   Dim entry%
   For entry = 0 To UBound(ItemList)
      ItemList(entry) = 0
   Next entry
End Sub


' The quantity of a particular menu item


Public Property Get Quantity(ByVal itemnum%) As Integer
   ' Invalid value, just return zero
   If itemnum > UBound(ItemList) Then Exit Property
   Quantity = ItemList(itemnum)
End Property


Public Property Let Quantity(ByVal itemnum%, ByVal iNewQuantity As Integer)
   If itemnum > UBound(ItemList) Then
      ReDim Preserve ItemList(itemnum)
   End If
   ItemList(itemnum) = iNewQuantity
End Property

The clsOrder1 class has a number of methods and properties that expose the internal array to the rest of the program. The Quantity property allows you to set or retrieve the quantity for any given menu item. Using Get/Let functions to access the property allows error checking and array management to be handled automatically during the property access.

The Clear method clears the order. Could you create a function at the form level or module level to clear an order simply by setting all of the Quantity property values to zero? Of course you could. But the whole idea of object-oriented programming is to associate code with a set of data. If you have an operation that needs to be performed on the data within an object, you should almost always implement it as a method for the object.

From the user's perspective, the Retail3 and Retail2 projects are identical. They work exactly the same way. However, the Retail3 project is implemented using the clsMenu1 and clsOrder1 projects, as shown in Listing 3.5.


Listing 3.5: Listing for frmRetail3.frm, the Main Form for the Retail3 Project
' Retail example #3
' ActiveX: Guide to the Perplexed
' Copyright (c) 1997 by Desaware Inc.


Option Explicit


Dim HighestButtonIndex As Integer
' The menu to use
Dim CurrentMenu As clsMenu1


' The three standard menus
Dim StandardMenu As New clsMenu1
Dim SeniorMenu As New clsMenu1
Dim ProgrammerMenu As New clsMenu1


' The current order
Dim CurrentOrder As New clsOrder1


' Load the menu selection here.
' It goes without saying that in a real application
' these would probably be loaded from a database.
Private Sub Form_Load()
   With StandardMenu
      .AddMenuItem "Hamburger", 1.99
      .AddMenuItem "Curly Fries", 1.32
      .AddMenuItem "Side Salad", 2.39
      .AddMenuItem "Chicken", 2.32
      .AddMenuItem "Stuff Delux", 5.39
   End With
   With SeniorMenu
      .AddMenuItem "Hamburger", 1.79
      .AddMenuItem "Fries", 1.12
      .AddMenuItem "Chicken Soup", 2.39
      .AddMenuItem "Salad", 2.12
      .AddMenuItem "LoCal Delux", 5.19
   End With


   With ProgrammerMenu
      .AddMenuItem "Hamburger", 2.19
      .AddMenuItem "Pizza", 2.52
      .AddMenuItem "Cheetos", 0.89
      .AddMenuItem "Jolt", 1.19
      .AddMenuItem "More Jolt", 2.19
   End With


   ' Default to the standard menu
   Set CurrentMenu = StandardMenu
   ' Load the menu buttons for the current menu
   LoadCommandButtons CurrentMenu
   UpdateAll
End Sub


' Clear this order
Private Sub cmdClear_Click()
   CurrentOrder.Clear
   UpdateAll
End Sub


' Add an item to the order
Private Sub cmdMenu_Click(Index As Integer)
   CurrentOrder.Quantity(Index) = CurrentOrder.Quantity(Index) + 1
   UpdateAll
End Sub


' Updates the contents of the list box
Public Sub UpdateListBox()
   Dim itemnum%
   lstItems.Clear
   For itemnum = 0 To CurrentMenu.ItemCount
      If CurrentOrder.Quantity(itemnum) > 0 Then
         lstItems.AddItem CurrentOrder.Quantity(itemnum) & " x " &_
         CurrentMenu.ItemName(itemnum)
         lstItems.ItemData(lstItems.NewIndex) = itemnum
      End If
   Next itemnum
End Sub


' Calculate the total price
Public Function CalculateTotal() As Currency
   Dim itemnum%
   Dim total As Currency

   For itemnum = 0 To CurrentMenu.ItemCount
      total = total + CurrentOrder.Quantity(itemnum) *_
      CurrentMenu.ItemPrice(itemnum)
   Next itemnum
   CalculateTotal = total
End Function


' Display the total and change
Public Sub UpdateTotals()
   Dim total As Currency
   Dim paid As Currency
   total = CalculateTotal()
   lblTotal.Caption = Format(total, "Currency")
   paid = Val(txtPaid.Text)
   If paid - total > 0 Then
      lblChange.Caption = Format(paid - total, "Currency")
   Else
      lblChange.Caption = "Please pay"
   End If
End Sub


' Delete an item on double click
Private Sub lstItems_DblClick()
   Dim thisitem%
   thisitem = lstItems.ItemData(lstItems.ListIndex)
   With CurrentOrder
      .Quantity(thisitem) = .Quantity(thisitem) - 1
   End With
   UpdateAll
End Sub


' Select the appropriate menu
Private Sub mnuStandard_Click()
   Set CurrentMenu = StandardMenu
   MenuChanged
End Sub


Private Sub mnuSenior_Click()
   Set CurrentMenu = SeniorMenu
   MenuChanged
End Sub


Private Sub mnuProgrammer_Click()
   Set CurrentMenu = ProgrammerMenu
   MenuChanged
End Sub


Private Sub txtPaid_Change()
   UpdateTotals
End Sub


Public Sub UpdateAll()
   UpdateListBox
   UpdateTotals
End Sub


' Loads the command buttons needed for a specified
' menu object
Public Sub LoadCommandButtons(mnu As clsMenu1)
   Dim buttonindex%
   buttonindex = 1
   ' Unload current buttons
   Do While buttonindex <= HighestButtonIndex
      Unload cmdMenu(buttonindex)
      buttonindex = buttonindex + 1
   Loop
   ' Now load new buttons
   cmdMenu(0).Visible = False ' In case menu is empty

   ' Load and display the menu items
   For HighestButtonIndex = 0 To mnu.ItemCount - 1
      If HighestButtonIndex > 0 Then Load cmdMenu(HighestButtonIndex)
      With cmdMenu(HighestButtonIndex)
         .Visible = True
         .Top = cmdMenu(0).Top + HighestButtonIndex * 450
         .Caption = mnu.ItemName(HighestButtonIndex)
      End With
   Next HighestButtonIndex



   ' Decrement HighestButtonIndex if any were added
   If HighestButtonIndex > 0 Then HighestButtonIndex = HighestButtonIndex - 1
End Sub


' Update the menu command buttons and clear the current
' order.
Public Sub MenuChanged()
   CurrentOrder.Clear
   LoadCommandButtons CurrentMenu
   UpdateAll
End Sub

The application defines a current clsMenu1 object and a current clsOrder1 object. In this example, only a single Order object exists, but three different menus are defined. In this example the menus are loaded during the form load event. However, it should be clear that you could just as easily load the menu object from a database or external file, eliminating the need to hard code the items. In fact, you would probably implement the code to load the object as a class method, not as part of the form.

Most of the code was intentionally kept as similar as possible to the Retail2 project. The one big difference is in the command button handling. The form needs a way to define the command buttons for a given menu. This is accomplished by the LoadCommandButtons function.

Minor Changes, Revisited

When you look at the Retail3 example, you may still wonder what all of the fuss is about with regard to object-oriented programming. Perhaps the code is slightly more organized and easier to read, but it also looks like a bit more work. Where is the benefit?

Well, let's go back to our friendly manager or client who has suddenly realized that efficiency would double if only you could store several orders in the register at once. Modifying the Retail2 application to support this change would be a major hassle. Are things different with the Retail3 sample?

Here are the changes that were made to the Retail3 application to create the Retail4 example. First, all references to clsOrder1 were changed to clsOrder2 and clsMenu1 to clsMenu2. This has nothing to do with the functionality of the application. It was done only to distinguish between the example files. Then, add the following line to the clsOrder2 module:

Public AssociatedMenu As clsMenu2

This property is needed so that the clsOrder2 object can keep track of which menu it is using.

In the form declarations section, change the CurrentOrder declaration and add an array to hold the three orders that this example will implement:

Dim CurrentOrder As clsOrder2
   Dim Orders(2) As New clsOrder2

In the form load event, add the following:

Set CurrentOrder = Orders(0)
   Set CurrentOrder.AssociatedMenu = CurrentMenu

This initializes the current order.

Now add a label control to the form called lblOrder and three command buttons in a control array called cmdOrder1, as shown in Figure 3.2.

Figure 3.2 : Runtime view of form frmRetail4.

Add the following code to the form:

' Switch to a different order
Private Sub cmdOrder1_Click(Index As Integer)
   Set CurrentOrder = Orders(Index)
   If CurrentOrder.AssociatedMenu Is Nothing Then
      ' First time the order is selected, use the default menu
      Set CurrentOrder.AssociatedMenu = CurrentMenu
   End If
   Set CurrentMenu = CurrentOrder.AssociatedMenu
   LoadCommandButtons CurrentMenu
   UpdateAll
   lblOrder.Caption = "Current order: " & Index + 1
End Sub

When you change orders, the current menu is set based on the order, the menu command buttons are set according to the menu in use, and the list box and totals are updated to reflect the selected order. (Adding a checkbox by the appropriate pop-up menu entry is left as an exercise for the reader.)

That's it.

All of the code that works with orders deals with the clsOrder2 object. Switching between orders is thus a simple matter of switching objects.

You would probably make a number of other changes to this design to make it more robust in a real application. For example, you can change the menu to an array or dynamically allocate and load menu objects from a database. These objects could be kept in an array or a collection. The VB menu can be defined as a menu array, which could reflect the objects in the collection. This eliminates all of the hard-coded clsMenu1 objects.

Now that the clsOrder2 object contains a reference to the menu it uses, you can easily implement the CalculateTotal function as a function in the clsOrder2 class. This is left as an exercise for the reader.

The clsOrder2 object can be extended to hold the item description and price instead of just referencing an entry in the current menu. This makes it possible to change the price of an individual item instead of always using the menu price. You could thus sell one hamburger for $1.99, and another at $1.12 (though whether you want to do this or not may depend on how much you trust your cashiers).

The Theory of Object-Oriented Programming Revisited

Object-oriented programming is not just a buzzword. It is not a marketing term. Well, actually it is both of these, but don't let that get in the way of what's important. Object-oriented programming is a practical methodology.

This was demonstrated in the Retail2 and Retail3 examples. The two programs are virtually identical except for their use of objects. Yet they are worlds apart in design. A major enhancement that would have been extremely difficult to implement with one becomes almost trivial with the other.

What are the characteristics of object-oriented programming that make this possible? Think of it this way: Software has become increasingly complex. The process of developing software consists largely of managing complexity. Anything you can do to break a large problem into manageable tasks is good. And object-oriented programming is fundamentally a tool for breaking large problems into small objects.

When we created the clsMenu1 object, we effectively defined a type of data and a set of functions that could work with the data. The class functions AddMenuItem, ItemCount, ItemPrice, and ItemName provide everything we need to manage menu objects. You might think of these functions as the interface for the object (in fact, you will see later that interface is the actual technical term for a set of functions exposed by an object).

The object itself contains data structures (an array of MenuItem structures) and code to manage those data structures. An object may also contain its own internal functions to work on the object's data, though clsMenu1 does not take advantage of this capability.

Now here's the payoff. Once you implement an object, design its data structures, and write (and test) the code to work with those data structures, you are at liberty to forget all about it! You need never worry again about the code and data structures within that class. All you need to know about is the interface-the functions the class exposes.

Not only can you forget about this code in order to concentrate on other matters, you can also stop worrying that changes in one part of your program might somehow interfere with the functioning of the object, or that some other function or object might accidentally corrupt the data structures in the object. The object data is accessible only through the interface functions. This separation between objects can contribute greatly to the long-term reliability of an application, especially if it has multiple authors. It can also make it easier to add features to an application, by extending the interface of a few classes instead of rewriting global application code.

This ability to hide data and functionality behind a set of methods and properties is a characteristic of object-oriented programming called encapsulation. A second characteristic of object-oriented programming can be seen by answering this question: What does the Clear command do in Visual Basic?

Well, if you are referring to the Clear method for a list box, the command is used to clear a list box. But if you are referring to the Clear method of the clsOrder1 object, the command is used to clear the order object. In fact, you can have as many Clear methods as you wish. Each object will perform on its own internal data whatever operation is defined for the command. This ability to have a single command name shared by multiple objects is called Polymorphism.

Once again, the benefit of polymorphism is its contribution to reducing complexity. Without it VB would need separate commands for each object. For example, ClearListBox to clear a list box and ClearClsOrder1 to clear the Order1 structure. If 20 different objects had a Clear command, you would need 20 different functions. That's 20 functions to learn and remember and perhaps look up in the documentation when necessary. A single polymorphic Clear command is much easier to manage.

A third characteristic of object-oriented programming is called inheritance. Inheritance in the classic sense is not implemented in Visual Basic. Thus, Visual Basic does not meet the classic definition of a true object-oriented language. But don't worry about that now. Visual Basic allows you to accomplish many of the tasks that inheritance usually supports. You'll read more about this in Chapter 5.