This section concentrates on what you need to know in order to create your own classes. Remember from Chapter 2, "The Basics of Object-Oriented Programming," that classes provide the means by which objects can be created. As you already know, virtually everything in Visual Basic .NET is based on objects. Indeed, there are thousands of objects available to you in Visual Basic .NET. The goal of this section is to show how to add to Visual Basic .NET's list of objects. Being able to create your own classes allows you to extend Visual Basic .NET to meet your own individual needs.
Creating your own classes requires you to think through what you want your new class to accomplish. The following sections present a series of steps that help you design and write the code for a new class.
Step 1: Creating a Rough-Draft Design
For purposes of illustration, this section describes how to create a password class. Most network environments require you to log on with a username and password before you can access the system. Many programs work in a similar manner. In this section, you will learn how to do what I call a rough-draft design of a class you want to create.
A rough-draft design is an outline of what you want a class to be able to do. It is a very informal process. In a classroom setting, during the rough-draft design, I state the topic in general terms and then let the students call out a wish list of features the class should have. I write each of these features on the board without any discussion. It's a brainstorming process at this stage, and the goal is to include as many features as possible. This process works well with a programming team, too.
If you're writing code by yourself, you can write your own wish list on a piece of paper. You should write everything that comes to mind. If you have a programmer friend, you can give her a call, tell her the topic, and ask what features she think the class should have. When you cannot think of any more features, stop writing and take a break ”preferably a long one of at least several hours. When you return to your work, review the list. Add anything else that might have come to mind while you were away from your work. You should now have a pretty complete laundry list of what you want your class to do.
In this chapter you're going to create the CPassword class. You might want to write down your own version of a wish list for the CPassword class before you read on. Chances are pretty good that your list will differ from mine. If that's the case and you have a longer list, you should try implementing your version after you've read this chapter. That would be a great way to learn how to program a new class. It would also be a good way to learn whether certain features are worth adding.
The following is a wish list for the CPassword class that was taken from a class session of beginning programming students (some really goofy ideas have been deleted):
It's a fairly short list, isn't it? That's exactly the way it should be. If you ever have a really long wish list for a class, chances are pretty good that the functionality needed from the class is not defined clearly. The purpose of a class should be to do one thing well. If you see too many trees, you probably need to create another forest.
The wish list for the CPassword class describes the essentials. Can you think of any other items for the list? Some programs need a security clearance level so that different features of a program can be excluded from certain users. For example, viewing the payroll records in an accounting package might require a higher security level than would viewing the inventory records. Therefore, we should add a simple security level to the password program.
At this point we should simplify the wish list a bit, so let's exclude the editing features. We will add a little more realism to this program in Chapter 25, "Database Programming with Visual Basic .NET," when we discuss database programming. For now, however, we will just work on a bare-bones password system.
Step 2: Exploring Your Options for Creating a Class
Now that you have created a wish list for what you want the CPassword class to do, you're ready to take the second (often overlooked) step: exploring your options for creating the class. To begin, check the Visual Basic .NET online help feature to make sure an existing class does not fulfill the design needs of the CPassword class. To do this, from the Visual Basic .NET main menu, you select Help, Index and type in a word that describes the primary purpose of the class. For example, you might enter password and see what comes up.
If the online help shows an entry that seems to fit your needs, you should use it. In most cases, there is no reason to rewrite a class that already exists. (An exception might be when a class has the functionality that you need but you want greater control over the process.) Although an existing class might not be a perfect fit, it might be close enough to serve your needs.
If a class is not a perfect fit, perhaps you can salvage it by grafting some additional functionality onto it. A real advantage of OOP is that you can extend an existing class to fit your specific needs. (The process of extending a class is the subject of Chapter 17, "Inheritance.")
Another alternative is to purchase class libraries from third-party vendors . There are a number of journals and magazines that describe the libraries that are available. You can also check the Web for Visual Basic .NET class libraries. Chances are, someone has already written a library that has the functionality you are looking for. If you value your time at more than a couple bucks an hour , most of these libraries are very cost-effective .
If you can't buy the functionality you need or modify an existing class to suit your needs, then you need to write the class yourself from scratch. Let's assume that this is the case for the CPassword class.
After you have explored your options and decided to write your own class, you need to refine the class design. Given the design so far, you know you need variables to represent the following data items:
Each object of the CPassword class would be required to have these items. Because these data items are all related to the user, it would make sense to treat them as a single data item. Remember from Chapter 13, "More Loops," that you can use Structure to collect these individual data items into a single data structure. Therefore, you can reorganize the data as follows :
Private Structure Security ' Hold relevant info in a structure Dim User As String ' A list of the users for this program Dim Password As String ' The password Dim SecurityLevel As Integer ' Each user has a security level End Structure
It also makes sense that, because each user has this information, you should have an array of these structures to hold the list of authorized users on the system. Therefore, you might have this as part of the class to hold the list of users:
Private Shared mUserList() As Security
Finally, it would also make sense to track the total number of users on the system. Therefore, you should also add this:
Private Shared mUserCount As Integer ' How many users
Notice that all the class members are Private and Shared . Why?
First of all, the whole concept of encapsulation is to hide the data from the outside world. The outside world is filled with evil things that can contaminate your data. Therefore, you want to limit the scope of the data to the class in which it is defined. That's why the members are Private .
If you want the data to be hidden within the class, why do you use the word Shared as part of the access specifier ? First, remember from Chapter 8, "Scope and Lifetime of Variables," what the word Shared means. It does not mean the data is shared with everything else in the program; Private assures you of that. What Shared does mean is that only one copy of the data item is shared among all instances of the class objects that are created. Therefore, even if you create 1,000 objects of this class, every one of those objects shares the same mUserList() and mUserCount data items.
This sharing of the two data items makes sense if you think about it. After all, why should every instance of the CPassword object have its own copy of the list of users and how many users are in the list? There is no need to duplicate these data items. Declaring them to be Private and Shared has the dual benefit of hiding (that is, encapsulating) the data and not wasting memory by creating unnecessary copies of the data. Also, if changes are made to the user list, having just one copy makes the change available to all objects. Makes sense, right?
Private Versus Public Shared Class Data Members
What would happen if you made the mUserCount and mUserList() data items Public Shared data instead of Private Shared data? You would be able to access the Shared class data member without even creating an object! That is, you could have the following statement in your program before any CPassword object is created:
TheUserCount = CPassword.mUserCount
Visual Basic .NET would not issue an error message. This works because Public Shared class members are created when the class code is first loaded into memory. Because of the Public access specifier for the Shared member, using the dot operator in conjunction with the class name and the class member makes everything work just fine. You might think this makes mUserCount functionally equivalent to a global variable. That's really not true because you must still prefix the variable with the class name and the dot operator.
Now you know that it would be possible to have a Public Shared class member. But simply because something works doesn't make it a good idea. Whenever possible, you should try to make all your class members private. That's the whole idea behind encapsulation. If you need to access a Private Shared class member before any object is instantiated , you need to write a Public Shared method for the member.
A UML Description of CPassword
Next you might want to use the Unified Modeling Language (UML) notation discussed in Chapter 3, "Thinking About Programs," to summarize the CPassword class. A UML description often helps crystallize the design of a new class. An example of this is shown in Figure 15.1.
Figure 15.1. A UML class diagram for the CPassword class.
The top box in Figure 15.1 is labeled CPassword , the name of the class. The second box describes the data members, or properties, of the class. (UML notation refers to those data members as attributes. This chapter uses the more common Visual Basic .NET term properties instead.) Recall that the minus sign before a data item means that the data item is Private to the class. The plus sign means that the item is Public and, hence, can be accessed from outside the class. All the properties are private to the CPassword class.
The final box in Figure 15.1 is a list of the methods that act on the data. (Although the UML notation prefers the term operations, this chapter uses the word methods instead because it is more common to Visual Basic .NET.) Note that the first five methods are Public methods, which means you have access to the class members. The last three methods are Private to the class. This means you cannot access these methods outside the class; they are "helper" methods that perform tasks that are required internally by the class.
One nice feature of the UML notation is that it gives a concise summary of all the properties and methods associated with the class. It also provides a summary of how each Public method is called by the object and the data type the method returns. A final benefit of the UML notation is that it forces you to think about the design of the class before you start writing the code for it.
At this point, you have a fairly good, albeit rough, idea of what the CPassword class should do and the data you need to create the class. Now you can write the code for the class.