Implementing an Abstract Interface
One way to design an interface is to create a class that describes Sub, Function, and Property procedure definitions, including arguments and return types, without adding any code to the procedures. This is referred to as an abstract interface. The advantage to implementing an abstract interface is that the class that implements it can include whatever code you want in the procedures it provides. Two classes that implement the same method can contain entirely different code within that method.
The four different objects that the library offers are all similar, but not identical. Fiction and Nonfiction objects can be checked out, while Periodical and Reference objects cannot. However, the objects are similar enough to share certain common properties. In addition, as the library expands and adds new types of holdings, such as Audio or Film, those new holdings will also have common properties. All of these objects implement the LibraryItem interface, which supplies the common properties that each object must have. Within each object's class module, a property procedure can contain custom code tailored to that object's requirements.
The interface described by the LibraryItem class defines four read-write properties: Name, ItemType, AllowCheckOut, and CheckedOut. The Name property is a simple public module-level variable; the others are paired Property Let and Property Get procedures. Because this is an abstract interface, none of the Property procedures contains any code.
The LibraryItem class also contains a public enumeration that defines numeric constants that represent each of the four existing object types. In the interest of organization, the enumeration appears in the LibraryItem class; however, it could also be defined in a standard module. The enumeration itself does not form part of the interface.
The Fiction, Nonfiction, Periodical, and Reference objects all implement the LibraryItem interface, so each of them has its own property procedures for each property defined by the interface. You can customize these procedures according to the requirements of the object. For example, the AllowCheckOut property indicates whether a particular item can be checked out. For Fiction and Nonfiction objects, this property should return True. For Periodical and Reference objects, it should return False. Compare the LibraryItem_AllowCheckOut property procedures for the Fiction and Periodical classes:
' AllowCheckOut property for Fiction object. Private Property Get LibraryItem_AllowCheckOut() As Boolean ' Fiction can be checked out. LibraryItem_AllowCheckOut = True End Property ' AllowCheckOut property for Periodical object. Private Property Get LibraryItem_AllowCheckOut() As Boolean ' Don't allow periodical to be checked out. LibraryItem_AllowCheckOut = False End Property
Although it is practical for both Fiction and Periodical objects to have an AllowCheckOut property, it is not practical for both properties to return the same value.
The real power of implementing an abstract class becomes apparent when you begin using the objects to construct an application. The library system must be designed to handle any type of holding, and yet to distinguish between different types at the same time. Because the LibraryItem class is implemented in all four of the specific classes, an item of type
LibraryItem can represent any of those objects, just as a Control object can represent any type of control generically.
The concepts of implementing an abstract class are more important than the details of the library application, so the discussion here will focus on explaining the concepts. When the user clicks the Catalog New Items button on the switchboard form (frmLibrary), another form loads (frmCatalog). This form makes it possible for the user to enter a name for a new library holding and select the type of holding from a combo box. When the user clicks the Catalog button, a new object of the appropriate type is created and added to the collection.
Here's the code that creates a new library item in the cmdCatalog_Click event procedure. Note that this code fragment calls the AddLibraryItem procedure, passing in the name of the library item and its type, and returns a reference to an object that is assigned to a variable of type
Dim colItem As LibraryItem Set colItem = AddLibraryItem(Me.txtItemName, Me.cboItemType.ListIndex)
Next, take a look at the AddLibraryItem procedure. This procedure examines the value passed in for the lngType argument and creates a new object of type
Periodical. It then adds this reference to a global Microsoft® Visual Basic® for Applications (VBA) Collection object to store it. Finally, the procedure returns a reference to the new object. Because the procedure's return type is declared as type
LibraryItem, it is this type of object that is returned to the calling procedure and assigned to a variable of type
Public Function AddLibraryItem(strName As String, _ lngType As opgItemType) As LibraryItem ' This function creates a library item of the specified type, ' sets its Name property, adds it to the collection, ' and returns a reference to the object. ' Note that a variable of type LibraryItem is used to store the ' reference, even though the code creates a specific object type. ' In a more sophisticated application, you would probably include ' this method in a collection class as the Add method. Dim colNew As LibraryItem ' Determine what type of library item this is and ' create a new object of the appropriate type. Select Case lngType Case opgItemType.ITEM_REFERENCE Set colNew = New Reference Case opgItemType.ITEM_FICTION Set colNew = New Fiction Case opgItemType.ITEM_NONFICTION Set colNew = New NonFiction Case opgItemType.ITEM_PERIODICAL Set colNew = New Periodical End Select ' Set object's Name property. colNew.Name = strName ' Add new object to collection, specifying its name as ' the key for the item. The key can be used to retrieve ' the item from the collection. gcolLibItems.Add colNew, strName ' Return reference to new object. Set AddLibraryItem = colNew End Function
As you can see from this procedure, implementing the LibraryItem interface in the Reference, Fiction, Nonfiction, and Periodical classes means that a reference to any of those objects can be assigned to a variable of type
LibraryItem. You do not have to know ahead of time exactly which object your code will be dealing with. When you set or return a property on the LibraryItem object, the property procedure for the appropriate object runs.
For example, the following event procedure determines whether an item can be checked out, and, if so, checks it out by setting the CheckedOut property to True. All of the available object types have an AllowCheckOut property. For Fiction and Nonfiction objects, this property returns True. For Periodical and Reference objects, it returns False. Without knowing exactly which type of object the
colLibItem variable refers to, you can check the property and respond appropriately:
Private Sub cmdCheckOut_Click() ' Attempt to check out library item. Dim colLibItem As LibraryItem Dim strMsg As String ' Locate item in collection and return reference to it. ' Assign reference to variable of type LibraryItem. ' Note that although the object has a specific type, ' you don't need to know what it is to use it. ' Value of selected text in combo box is same as ' item's key in collection. The key can be used to retrieve ' the item from the collection. Set colLibItem = gcolLibItems.Item(cboItems.Text) ' Check whether this item can be checked out. If colLibItem.AllowCheckOut Then If colLibItem.CheckedOut Then ' If item is already checked out, apologize. strMsg = "Sorry, this item is already checked out." Else ' Otherwise, check out and give return date. colLibItem.CheckedOut = True strMsg = "Item is checked out to you; due back " & Date + 14 End If Else ' If item can't be checked out, apologize. strMsg = "Sorry, this item can't be checked out." End If ' Display message. MsgBox strMsg End Sub
In addition, you can add properties or methods that are not part of the interface to any object. For example, you could add a Volume property to the Periodical and Reference classes to set and return the volume number for the object. To set that property, however, you must use an object of type
Reference, because the Volume property is not part of the LibraryItem interface. To determine whether the type of object you are working with is a Periodical or Reference object, you can use the If TypeOf construct. For example, the following procedure sets the volume number if the object passed in is a Periodical or Reference object:
Public Sub SetVolume(colItem As LibraryItem, _ strVolNum As String) ' Declare object variables of specific types. Dim perItem As Periodical Dim refItem As Reference ' Use Is TypeOf... construct to check whether ' library item is a periodical. If TypeOf colItem Is Periodical Then ' Assign library item reference to object variable ' of type Periodical. Set perItem = colItem ' Set Volume property on Periodical object. perItem.Volume = strVolNum Else ' Check whether library item is reference material. If TypeOf colItem Is Reference Then ' Assign library item reference to object variable ' of type Reference. Set refItem = colItem ' Set Volume property on Reference object. refItem.Volume = strVolNum Else ' Library item must be fiction or nonfiction. MsgBox "You can't enter a volume label for " _ & "a fiction or nonfiction item." End If End If End Sub
Note You could also implement the Volume property shown in the previous example as part of the LibraryItem interface, and simply not add any code to it for the Fiction and Nonfiction classes. This might be a better approach, because then you do not have to use the If TypeOf construct or create an additional object variable.
Why Build Your Own Objects? | Basic Class Concepts | Creating Property Procedures | Creating Events and Event Procedures | Extending Objects Through Interfaces | Designing Object Models | Creating Custom Objects for Web Pages