Generalization is an analysis technique used to find common ground between a number of objects. Once we've established that a group of objects have a set of common behaviors and properties, we can create a more generalized class to represent all those common elements. We can then use that generalized class as a base from which we can create all the other, more specialized, classes.
We can view this new, general class as a sort of parent to all the more specialized classes. The process of creating the specialized classes from the general class is called inheritance - something we discussed in a different context in Chapter 1. When we view our generalized class as a parent of the specialized classes, we're creating what's called a class hierarchy.
A class hierarchy is like a family tree for our objects. As we trace back from one object to the next, we become more and more general. Let's look at a quick example to see what this means:
This diagram demonstrates how a class hierarchy might appear for various types of Person. If we look at an Hourly employee, we can see that they're a specialized type of Employee. In other words, the Employee class is a parent to Hourly and Salaried. We can follow the flow all the way back to the most general class, Person. All of these classes are derived from the Person class.
Generalization is a very powerful analysis technique. By identifying the commonalities between various objects, we provide ourselves with an opportunity to achieve a high level of reuse. Since we can implement all the common functionality in one class, we can then use that functionality in all the more specialized classes. The more specialized classes inherit the functionality from the generalized class.
Inheritance is the technique used to implement objects that are identified using the generalization technique. Visual Basic 5.0 doesn't provide a direct mechanism to support inheritance, but it's such an important concept that we need to provide it. As we discussed in Chapter 1, we can use containment and delegation together to simulate inheritance in Visual Basic.
To see how we can implement inheritance, let's use an example from our video rental store. Suppose the store decides to not only rent videos, but also to sell popcorn, candy bars and other snacks.
We've already considered that our Invoice object has LineItem objects to reflect the videos that are rented. Now we're saying that we've got two different types of line item - a video rental line item and a line item for snacks the customer wants to purchase.
Let's look at the possible properties and methods for the video rental line item, which we'll call RentalItem
:
Properties | Methods |
Title | CheckOut |
Price | MarkAsPaid |
Now let's look at the possible properties and methods we might find in the line item to purchase a snack (PurchaseItem
):
Properties | Methods |
Price | ReduceInventory |
TaxableFlag | MarkAsPaid |
We have a couple of indications that we may be able to come up with a more general class from these two. First off, we know they are both used in a similar way - they're both line items of an Invoice
object. Secondly, in looking at their respective properties and methods, we can see that they share some interface elements.
Taking the common properties and methods from each, we derive the following list:
Properties | Methods |
Price | MarkAsPaid |
We can look at this as being a new, more general line item object, called LineItem
. If we then use LineItem
as a base from which to implement both the RentalItem
and PurchaseItem
objects,
we'll have a class hierarchy.
This diagram illustrates how each of our specialized line item classes, RentalItem
and PurchaseItem
, will inherit behaviors from the more general LineItem
class.
Now let's implement this relationship using Visual Basic. To start with, we'll create our general class, LineItem
. Once that's done, we'll use it as a base class to implement the RentalItem
class. Once we've seen how to implement RentalItem
, the implementation of the PurchaseItem
class would be trivial, so we won't rehash the same material again.
Our general LineItem
class will implement the properties and methods that are common to all line items. In this case, we're talking about a Price
property and a MarkAsPaid
method. Let's take a look at the code for the LineItem
class:
Option Explicit
Private dblPrice As Double
Private flgPaid As Boolean
Public Property Get Price() As Double
Price = dblPrice
End Property
Public Property Let Price(dblValue As Double)
dblPrice = dblValue
End Property
Public Sub MarkAsPaid()
flgPaid = True
End Sub
Private Sub Class_Initialize()
flgPaid = False
End Sub
This code is very straightforward. Our Price
property merely stores and retrieves its value from the dblPrice
variable, while the MarkAsPaid
method simply sets the flgPaid
variable to True
. Obviously, any real business object would be more complex, but this should be enough code to demonstrate how inheritance works.
Now we're getting into the interesting part. Since Visual Basic doesn't directly support inheritance, we'll use a combination of containment and delegation to simulate it. We covered this briefly in Chapter 1, but we'll get a good taste for the process as we implement the RentalItem
class.
Before we inherit the behaviors of the LineItem
class, let's set up the basics of our RentalItem
class. We don't need to worry about the Price
property or the MarkAsPaid
method, since we'll inherit them from the LineItem
class. We do need to implement the Title
property and CheckOut
method however:
Option Explicit
Private strTitle As String
Private flgCheckedOut As Boolean
Public Property Get Title() As String
Title = strTitle
End Property
Public Property Let Title(strValue As String)
strTitle = strValue
End Property
Public Sub CheckOut()
flgCheckedOut = True
End Sub
Private Sub Class_Initialize()
flgCheckedOut = False
End Sub
We've kept this code very simple as well. The Title
property just stores and retrieves its value from the strTitle
variable, with the CheckOut
method setting the flgCheckedOut
variable to True
.
With the basics of the class down, we can move on to inherit the LineItem
class's functionality. The first thing we need to do is use the technique of containment to make our RentalItem
object contain a LineItem
object. This means declaring a Private
variable to hold the reference to our LineItem
object. Add the following line to the RentalItem
class:
Option Explicit
Private strTitle As String
Private flgCheckedOut As Boolean
Private objLineItem As LineItem
We also need to create an instance of the LineItem
class. The best place to do this is in the Class_Initialize
routine, so the object is created right up front and is available any time we need it. Add this line to the RentalItem
class:
Private Sub Class_Initialize()
flgCheckedOut = False
Set objLineItem = New LineItem
End Sub
Our RentalItem
object now has access to its own private LineItem
object. Rather than implementing its own Price
property, it can rely on its LineItem
object's Price
property to do the work. The same goes for the MarkAsPaid
method, where the RentalItem
object can delegate the work to its private LineItem
object.
Of course, the LineItem
object is Private
, so any client code working with our RentalItem
object won't be able to get at it. In order to make a Price
property and MarkAsPaid
method available to the client code, our RentalItem
object will need to implement them. The implementation is simple however, since we'll just delegate the work down to our private LineItem
object.
Add this code to the RentalItem
class:
Public Property Get Price() As Double
Price = objLineItem.Price
End Property
Public Property Let Price(dblValue As Double)
objLineItem.Price = dblValue
End Property
Public Sub MarkAsPaid()
objLineItem.MarkAsPaid
End Sub
When the client code calls the MarkAsPaid
method, for instance, our RentalItem
code merely echoes the call to the LineItem
object's MarkAsPaid
method, rather than trying to do any work itself.
Let's take a quick look at some client code that we might use to work with the RentalItem
object at this point. Rather than creating a UI for such a simple example, let's look at how we might test this object in Visual Basic's Immediate debug window. With our two classes in place, we can enter the following into the Immediate window:
set x=New RentalItem
x.Price=1.99
print x.Price
1.99
x.MarkAsPaid
x.CheckOut
x.Title="Video X"
print x.Title
Video X
This example shows how we're able to work with the properties and methods from the RentalItem
object itself, as well as those from the LineItem
object. To the client code, there is no difference at all. This is exactly the behavior that we'd get using actual inheritance, were it available.
This would, of course, be a lot easier if Visual Basic directly supported inheritance; but, at the same time, this shows that we can do inheritance in Visual Basic - after a fashion. Since generalization is such an important part of object design, it's very important for us to be able to implement it in some manner.