Creating the Company Stock business object (ActiveX EXE)

Having identified the public objects required, we now need to set up a Visual Basic project to implement them. When developing ActiveX EXE components, I find it easier to define and code the object in a layered approach from top to bottom, then bottom to top, adding detail each time I iterate through the cycle. Adopting this technique, you’d create the business object in the following manner.

Define the top-level business entity within the business object. In this case, it’s Company Details. I create a class named CompanyDetails (plural, because it can contain many different company details). This class acts as the primary public interface and will hold the structure of the object.

Work out your key data components, and add two classes for each data component. The first class holds the data item, and the other acts as a collection for the first, allowing for the addition of more than one data item. In this example, I add six classes: Company, Companies, Stock, Stocks, CField, and CFields. It is good practice to name the collection class in the plural form of the data item class because this is the de facto standard Microsoft uses.

Now define the structure of the object from the structure in Figure 3-7 on page 107. I adopt a hierarchical class approach because a company rather than the set of companies owns stock. The Company class has a public reference to the Stocks class. Stocks contains a private collection of the Stock class. The Companies class contains a private collection of the Company class. The CField class contains a private collection of the CField class, and the CompanyDetails class has a public reference to the Companies and CFields classes. To accomplish this hierarchy, the following code is added to each class Declaration section:

Class Declaration Details
CompanyDetails
Public Companies As New Companies 
Public Fields As New CFields
Companies
Private colCompany As New Collection
Company
Public Stocks As New Stocks
Stocks
Private colStock As New Collection
Stock
(Nothing added)
CFields
Private colField As New Collection
CField
(Nothing added)

We now consider how to create instances of the classes. This is easy to do because the only class that the user can create is the top level of the hierarchy, CompanyDetails, which we set to GlobalSingleUse. The other classes are set to PublicNotCreatable. This ensures that a separate instance of the CompanyDetails object is created each time it is used. The other classes are visible to the developer but cannot be created independently of the CompanyDetails object.

Let’s now look at the public methods and properties in each class by working our way back up the class structure, starting with CField:

Class Properties Methods
CField
SQLFieldName, 
ReportFieldName, 
TableName, 
TableShortName

Stock
ID, 
DateOfChange, 
Description, 
NoOfUnits, 
Price

Company
ID, 
Description, 
InceptionDate, 
Category 
GetStockList
CFields
Count
Item
Stocks
Count
Item
Companies
Count
Item
CompanyDetails
DataPassFormat
BuildCompanyCriteria, 
BuildCompanyList, 
GetCompanyList, 
RemoveCompanyList

In the collection classes CFields, Stocks, and Companies, the Add, Remove, and RemoveAll methods are restricted to use within the project by the Friend statement. This is an intentional design feature to ensure that items can be added or removed only by the business object rather than by the calling applications, which can lead to unpredictable results. The Friend statement is ideal for this purpose because it ensures that a function is global to the object functions and subroutines but hidden from the public interface for the object.

All that is left now is to define the public properties. The DataPassFormat property allows the object to pass information back to the caller in a number of different formats. In the following code (found on the companion CD in CompanyDetails.cls), this property has been implemented using Property Get and Property Let procedures, with a predefined range of values in the form of global constants:

' The three methods for passing data
' Place this code in the Declaration section of
' the CompanyDetails class.

' We can now refer to related constant values using a single name by 
‘ grouping them in an enumerator; we can expose them publicly in type 
‘ libraries.
Public Enum Constants
    csPassByProperties = 1
    csPassByVariant = 2
    csPassByFile = 3
End Enum

' Return the current setting.
Public Property Get DataPassFormat()
    DataPassFormat = nPassByProperties
End Property

' Validate and set the data pass format.
Public Property Let DataPassFormat(ByVal Method As Variant)
    If Method <> csPassByProperties And _
        Method <> csPassByVariant And _
        Method <> csPassByFile Then
        Err.Raise vbObjectError + 1, _
        "CompanyStock.CompanyDetails", "Invalid Method"
    Else
        nPassByProperties = CInt(Method)
    End If

End Property

The last important point is to ensure that you have terminated a class in a structured fashion. Never use an End statement, and always close all references to objects. In the following code, notice that all database objects and collections items are closed or removed before termination:

' Close all references and objects.
Private Sub Class_Terminate()
    On Error Resume Next
    Companies.RemoveAll
    Set Companies = Nothing
    Set dbDatabase = Nothing
    Set wsDefault = Nothing
End Sub

Tip When developing ActiveX servers, use the project group capabilities of Visual Basic 5 for testing. Once the first project is set up, add another project by selecting Add Project from the File menu. Remember to set the second project, your test harness, as the start-up project.

The rest of the coding is straightforward. Pertinent methods are listed in Table 3-5.

Table 3-5

Additional Methods of the Business Object

Method Description
BuildPortionOfCriteria
Extracts information out of the criteria string.
InsertItemIntoCriteria
Adds criteria information to an SQL string.
LoadCompanyList
Executes the SQL and loads the data into the classes.
BuildCompanyList
Creates the SQL needed. The parameters are all passed as string arrays, held in variants, allowing us to determine whether a field descriptor has been used more than once.
GetCompanyList
Illustrates three methods of passing information to the calling program. The ReportInGrid example (see page 129) shows the timing differences between the different methods.