A programming language provides a way to accomplish tasks and also a structure for thinking about the tasks that you can accomplish. Visual Basic's new object-oriented language features change the way that you think about what you can accomplish. Not only are the new horizons broader, the landscape is fundamentally different.
This chapter introduces you to object-oriented programming with Visual Basic and readies your thinking for brave new vistas.
In this chapter, you learn about the following:
Visual Basic Standard Edition prevents you from creating Public class modules. Therefore, objects that you create with the Standard Edition are available only inside the application in which you define them. To create applications that share objects across applications, you need the Professional Edition or Enterprise Edition. The Enterprise edition also enables you to create objects that can be used remotely across the network.
A well-designed object is like a black box; it handles a particular task without exposing the solution's details. The advantage of using "black boxes" is that you can stack an infinite number of them without worrying that some internal quirk within one of them will cause the whole stack to tumble.
The quirks that destabilize large applications are almost always related to global variables. The problem occurs when two unrelated procedures inadvertently use the same variable name for different purposes. If the variable is local to the procedure, no problem occurs. But if the variable is global to the application, you can get completely unpredictable results. This problem is fairly specific to large applications, simply because smaller applications have fewer variables. The fewer variables, the less likely they are to collide.
You have a better chance of avoiding problems with global variables if you observe rigorous variable-naming conventions. Some organizations even have personnel whose sole job is to allocate variable and procedure names. (Although their job may sound trivial, these people are actually quite powerful within their organizations; if you get on their wrong side, you'll be prefixing all your variable names with ImSoSorry.)
Visual Basic 4.0 enables you to keep sensitive variables and procedures inside a black box called an object. Objects are more than just another level of scope�they are a programming structure that provides a new form of containment, as shown in figure 17.1.
Visual Basic adds objects to its traditional programming structures.
Objects protect procedures and variables through their instances. Calling procedures must get an instance of the object from another procedure or create a new instance of the object themselves to have access to items in the object. Each instance of an object provides its own private storage, which other instances of the same object do not affect.
Objects also benefit programmers by molding the design process around things rather than processes. Windows applications tend to promote nonlinear user interaction. Object-oriented design focuses on creating self-contained objects that manage nonlinear tasks better than applications that you create using structured programming techniques, as shown in figure 17.2.
Object-oriented programming focuses on "what" rather than "how."
For most Visual Basic programmers, forms are the best visible example of objects. Each form has a definition that the programmer creates at design time and stores in a form module (.FRM). At run time, Visual Basic creates an instance of the form, and that instance has predefined methods and properties. Methods and properties always apply to a single instance of the form, as shown in figure 17.3.
The Move method applies to a single instance of a form, no matter how many instances of the same form exist.
Until Visual Basic 4.0, forms were not considered full objects, because you couldn't add methods or properties to them. All data and procedures in a form module were local to that form�you had to use global variables or built-in Form properties to get data in or out of a form.
Visual Basic 4.0 lets you declare Public variables and procedures within a form module. Items that you declare as Public look just like a form's built-in properties and methods. With this new feature, Visual Basic forms are now true objects. Visual Basic 4.0 also adds a new type of file, the class module. Class modules are similar to form modules, but lack the built-in user interface.
Use form modules to define objects that have a user interface. Use class modules to define objects that lack a user interface or that must be available outside of an application.
You create the definition of an object at design time as a form module or a class module. The actual object is an instance of the class that Visual Basic creates at run time. This distinction is important, and causes much confusion among people learning object-oriented programming (see fig. 17.4).
A class defines an object; an object is an instance of a class.
Another important detail is that form modules are self-instantiating, but class modules are not. In other words, Visual Basic automatically creates an instance of a form when you refer to the form's properties or methods in code. You've probably run into this as a bug in your code�inadvertently referencing a form's Visible property loads the form invisibly, as shown in figure 17.5.
Referring to a form property automatically creates a "ghost" instance of the form.
This self-instantiation doesn't happen with class modules. To create an instance of a class module, You must use the New keyword in a declaration as shown in figure 17.6.
You must declare a variable as a New class to create an object from a class module.
Objects that you create from form modules exist until you call the Unload statement on the form. Objects that you create from class modules exist as long as one or more variables refer to the object. Visual Basic objects follow the same reference-counting conventions as all OLE objects:
Listing 17.1 demonstrates reference counting. When Proc1 runs, Visual Basic creates a new object based on the Class1 class module (references = 1). At the end, Proc1 sets the vModule module-level variable to refer to the new object (references = 2). When clsProcedure loses scope, Visual Basic does not destroy the object (references = 1). Later, Proc2 shows that the object still exists by displaying its IExist property. Setting vModule to Nothing finally destroys the object (references = 0).
Listing 17.1 A Demonstration of Reference Counting
An object that displays forms or controls does not automatically unload those forms or controls when Visual Basic destroys the object. If you hide a form or control, you can create a "lost reference" that stays around as long as the application is running�consuming memory and resources. Figure 17.7 shows the flow of control that can create a lost reference to a loaded form.
Object1 creates a new, hidden Form1. After Object1 loses scope, Form1 remains in memory.
Classes that create forms or controls should use the class module Initialize and Terminate events to create and delete those objects. Listing 17.2 shows the Initialize and Terminate event procedures for a simple class that displays a form. The Initialize event procedure creates a new instance of a form. The Terminate method unloads the form and sets the newForm variable to Nothing.
Listing 17.2 The Initialize and Terminate Event Procedures for a Simple Class
The following code creates and then deletes a new object by using the preceding methods. Although Delete correctly unloads the form from memory, you still must set the object variable to Nothing or let the variable lose scope to destroy the object.
The Initialize and Terminate events in listing 17.2 are similar to C++ constructor and destructor functions. However, C++ constructor and destructor functions can take arguments to initialize data in the object. The Visual Basic Initialize and Terminate events do not take arguments; you must initialize data by using methods and properties that you define in the class or form module.
Objects in themselves don't solve any of your programming problems. For objects to be helpful, you must observe some general rules. Because programming is a subjective and creative process, the rules aren't absolute or immutable. They give you a starting point from which you can work to develop your own style and preferences.
The following two rules of thumb are adapted from The C++ Programming Language by Bjarne Stroustrup, the father of object-oriented programming. The following adaptation modifies some rules to use terms familiar to Visual Basic programmers, and others have been changed or omitted because of limitations in Visual Basic's approach to objects (Visual Basic is not yet fully object-oriented):
The previous section mentioned that Visual Basic is not yet fully object-oriented. That isn't an excuse for C++ or SmallTalk programmers to dismiss the language; Visual Basic is still the most productive tool for creating Windows programs. The limitations bear mentioning, however, because they are the language features that you can expect Microsoft to add in future releases:
Applications that use objects can be large or small. They can be completely stand-alone or used as components in other applications. Table 17.1 lists the types of applications that you can create with Visual Basic's new object-oriented features.
Table 17.1 Types of Applications Creatable with Visual Basic 4.0's Object-Oriented Features
Type of Application | Example(s) | Chapter in Which Discussed |
New general-purpose data types, such as extended Integer or recursive types. | TYPES.VBP, OUTLINE.VBP | 21 |
OLE components for use in other applications as add-ins. | SYSTEM.VBP | 18, 21 |
Stand-alone applications that provide services and data sharing to other applications. | CDPLAYER.VBP | 17 |
Components to run in separate processes for performance reasons. | VBTERMINAL.VBP | 18 |
Internal components used as a structuring technique within an application. | CDPLAYER.VBP, WINDOWS.VBP | 17, 19 |
Extensions to the Visual Basic programming environment. | REGADDIN.VBP | 24 |
Objects can be used internally in a single application or externally among multiple applications. Objects available for external use are sometimes called OLE Automation objects, because they are available through the OLE 2.0 OLE Automation interfaces.
Applications that include one or more OLE Automation objects are called object libraries. Object libraries are similar to conventional dynamic link libraries (DLLs), except that object libraries provide their features through objects rather than functions. Also unlike DLLs, object libraries use the Visual Basic data types, not C-specific types like null-terminated strings and pointers.
To create internal or external objects, you use the same programming techniques. The settings of two class module properties determine whether an object is available to other applications: Creatable and Public. For this reason, most of the procedures and examples in this book apply equally well to internal and external objects.
To create applications that multiple applications can use, you must use Visual Basic 4.0 Professional Edition. The Standard Edition does not enable you to create class modules that have their Public property set to True. Therefore, Standard Edition objects are visible only within the project that defines them.
The Standard Edition also doesn't let you create Visual Basic add-ins, which are OLE Automation objects that enable you to extend the Visual Basic programming system. For example, an add-in might provide templates and wizards for certain types of applications that are commonly created in Visual Basic. These objects contain some special methods for loading and registering the add-in in Visual Basic.
You can create internal objects using Visual Basic Standard Edition. To use any of the object samples in this book with existing Standard Edition projects, simply add the desired class, form, and code module files to the existing project.
You can't create OLE objects that support in-place editing with Visual Basic. Visual Basic applications display information only in the context of forms, which are separate windows or dialog boxes in an application. You can use Visual Basic to insert text, graphics, and other OLE objects into documents in OLE container applications, however.
The CD Player sample application (CDPLAYER.VBP) demonstrates how to build an application out of objects. Analyzing the task to be performed�playing an audio CD�yields three features that you can represent as objects:
In addition, the application has a user interface, or control panel, to the CD player. You can represent this interface or control panel as a form object. You can represent all these objects graphically as shown in figure 17.8.
Your code can represent the physical aspects of a CD player as objects.
Mapping an application's initialization, termination, and interaction graphically, as shown in figures 17.9 and 17.10, is helpful when building applications with objects. As you sketch your application, don't worry about the details of how you will do something; object-oriented design focuses on identifying what each object represents and which objects control other objects.
A separate class or form module in Visual Basic defines each of the preceding objects. Objects can create or destroy each other as events occur. When CDPLAYER.VBP runs, the Application object creates ControlPanel and Title objects. The Title object creates an instance of the Track object for each track on the CD. Figure 17.9 shows the flow of initialization when CDPLAYER.VBP starts.
The Application class creates the initial objects, which in turn create others.
Figure 17.9 indicates that the Application object and Title object both need Initialize event procedures to create subordinate objects: the Application object's Initialize event procedure creates the ControlPanel and Title objects; the Title object's Initialize event procedure creates a collection of Track objects.
When CDPLAYER.VBP shuts down, the Title and Track objects lose scope and are destroyed automatically. The Application object, however, must unload the ControlPanel object because it is a form and does not otherwise go away. Figure 17.10 shows how the application destroys its objects when it terminates.
The Application object must unload the form before terminating; other objects are unloaded as they lose scope.
Figure 17.10 indicates that only the Application object needs a Terminate event procedure. The Application object's Terminate event procedure unloads the ControlPanel that it creates on initialization. Other objects don't require Terminate event procedures, because Visual Basic unloads them as they lose scope when the application ends.
The CDPLAYER.VBP responds to events through its objects. The ControlPanel object receives all user events, because it is the only visible part of the application. The Application object responds to all programmatic requests through the its Public methods and properties, as shown in figure 17.11.
These interactions help determine the various navigational properties and methods that you must create to move among objects. The next section discusses how you create these properties and methods.
After mapping the objects in a cleanly organized way, you can identify the methods and properties for each class. The best way to do so is in two passes:
User events go directly from the form to the device; OLE Automation requests go through the Application object.
Sometimes this process requires that you add or rename objects. The software design process is unavoidably iterative. (You hope that it doesn't become an infinite loop.)
Navigational Methods and Properties
Table 17.2 lists the navigational methods and properties for each class in the CDPLAYER.VBP sample application. The members in bold are unique to this application. The other members are standard navigational items that Microsoft recommends for all Public objects. The standard navigational items are especially useful when debugging an object application. For this reason, you should always include them, even though they are a little boring to cut and paste between classes.
Table 17.2 CDPLAYER.VBP's Navigational Methods and Properties
Object | Member | Return Value |
Application | Application | Application object |
Parent | Application object | |
Name | "CD Player" | |
Title | Title object of the current CD | |
Title | Application | Application object |
Parent | Application object | |
Name | The name of the CD | |
CurrentTrack | The Track object that is playing | |
Tracks | The collection of all Track objects on the CD | |
Track | Application | Application object |
Parent | The Title object that created the track | |
Name | The name of the track |
Functional Methods and Properties
Table 17.3 shows the functional methods and properties for each class in the CDPLAYER.VBP sample application. These are the methods and properties that do the work for the objects.
Table 17.3 CDPLAYER.VBP's Functional Methods and Properties
Object | Member | Description |
Application | Play | Starts playing the CD |
PlayNext | Skips to the next track | |
PlayPrevious | Skips to the previous track | |
StopPlaying | Stops playing the CD | |
Eject | Ejects the CD | |
Quit | Exits the application | |
Visible | Sets or returns the visible state of the application | |
ElapsedTime | Returns the minutes and seconds that the CD has been playing | |
frmControlPanel | CurrentTitle | Keeps track of the name of the CD currently playing |
ElapsedMinutes | Returns the minutes that the CD has been playing | |
ElapsedSeconds | Returns the seconds that the CD has been playing | |
CurrentTrack | Returns the index of the track that currently is playing | |
Tracks | Returns the number of tracks on the CD | |
Track | Sets the tracks about which to get information | |
TrackPosition | Returns the start position of a track on the CD | |
TrackLength | Returns the length of a track on the CD | |
Command | Executes a string command on the MCI control | |
Title | None | Provides a way to organize tracks; doesn't contain functional members |
Track | StartPosition | Returns the track's starting position on the CD |
Length | Returns the length of the track |
The following are some interesting aspects of table 17.3:
Creating an application using objects is a subjective, creative endeavor. In the CDPLAYER.VBP sample, you could have applied the Play method to the Title object just as well as to the Application object. By placing the method in the Application object, however, you make the syntax that you use from other applications a little simpler (Application.Play rather than Application.Title.Play).
CDPLAYER.VBP is not complete. Like all software, it awaits new features. Ideally, it should store CD titles and song names in a data file, then retrieve those names when the appropriate CD is loaded. It should also support play lists, shuffle, and other standard CD player features. Using objects and the framework provided, it's relatively easy to imagine many ways to enhance the sample application by providing all these features.
The challenge of object-oriented programming is to think in terms of objects rather than procedures. Whether you are creating a new PlayList object or serializing the Title object to a file, objects give you a new way to tackle programming problems.
In the words of Brian Kernighan, "The only way to learn a programming language is by writing programs in it." So let's get started!
For more information on the following topics, see the following chapters:
© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.