The Generalization Relationship - Containing Objects

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.

Looking for Common Ground

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.

LineItem Class

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.

RentalItem Class

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.

Setting up 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.

Containing a LineItem Object

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

Inheriting Behavior Using Delegation

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.

Trying It Out

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.