In this example, we’ll tackle a very real problem by using inheritance. Consider a structure for storing product information; in most applications, this structure is optimized for a specific product type. In my experience, I’ve seen designs that try to capture the “global” product: a structure that can store products of any type. This approach leads to unnecessarily large database tables, name conflicts, and all kinds of problems that surface after the program has been installed at customers with different product types. Here’s my suggestion for handling multiple types of products.
Every company makes money by selling products and services, and every company has different requirements. Even two bookstores don’t store the same information in their databases. However, there are a few pieces of information that any company uses to sell its products: the product’s code, its description, and its price. This is the minimum information you need to sell something (it’s the information that’s actually printed in the invoice). The price is usually stored to a different table, along with the company’s pricing policies. Without being too specific, these are the three pieces of information for ordering and selling products. We use these items to maintain a list of orders and invoices, and keep track of the stock, customer balances, and so on. The specifics of a product can be stored to different tables in the database, and these tables will be implemented upon request. If your customer is a book seller, you’ll design tables for storing data such as publisher and author names, book descriptions, ISBNs, and the like.
You’ll also have to write applications to maintain all this information. To sell the same application to an electronics store, you must write another module for maintaining a different type of product, but the table with the basic data remains the same. Clearly, you can’t design a program for handling all types of products, nor can you edit the same application to fit different products. You just have to write different applications for different types of products, but the parts of the application that deal with buying and selling products, customers, suppliers, and other peripheral entities won’t change.
Let’s look at a custom class for storing products, which is part of the Products sample project. The application’s main form is shown in Figure 7.1.
Figure 7.1 – Exercising the Book and Supply inherited classes
The most basic class stores the information we’ll need in our ordering and invoicing applications: the product’s ID, its name, and its price. Here’s the implementation of a simple Product class:
Public Class Product Public Description As String Public ProductID As String Public ProductBarCode As String Public ListPrice As Decimal End Class
I included the product’s bar code because this is how products are usually sold at cash registers. This class can represent any product for the purposes of buying and selling it. Populate a collection with objects of this type and you’re ready to write a functional interface for creating invoices and purchase orders.
Now we’ll take into consideration the various types of products. To keep the example simple, consider a store that sells books and supplies. Each type of product is implemented with a different class, which inherits from the Product class. Supplies don’t have ISBNs, and books don’t have manufacturers — they have authors and publishers; don’t try to fit everything into a single object, or (even worse) into a single database table.
Figure 7.2 shows the base class, Product, and the two derived classes, Supply and Book, in the Class Diagram Designer. The arrows (if they exist) point to the base class of a derived class, and nested classes (such as the Author and Publisher classes) are contained in the box of their parent class.
Figure 7.2 – Viewing a hierarchy of classes with the Class Diagram Designer
Listing 7.5: Simple Class for Representing Books
Public Class Book Inherits Product Public Subtitle As String Public ISBN As String Public pages As Integer Public PublisherID As Long Public Authors() As Author Public Class Author Public AuthorID As Long Public AuthorLast As String Public AuthorFirst As String End Class Public Class Publisher Public PublisherID As Long Public PublisherName As String Public PublisherPhone As String End Class End Class
In addition to its own properties, the Book class exposes the properties of the Product class as well. Because the book industry has a universal coding scheme (the ISBN), the product’s code is the same as its ISBN. This, however, is not a requirement of the application. You will probably add some extra statements to make sure that the ProductID field of the Product class and the ISBN field of the Book class always have the same value.
The class that represents supplies is shown in Listing 7.6.
Listing 7.6: Simple Class for Representing Supplies
Public Class Supply Inherits Product Public LongDescription As String Public ManufacturerCode As String Public ManufacturerID As Long Public Class Manufacturer Public ManufacturerID As Long Public ManufacturerName As String End Class End Class
To make sure that this class can accommodate all pricing policies for a company, you can implement a GetPrice method, which returns the product’s sale price (which can be different at different outlets or for different customers). The idea is that some piece of code accepts the product’s list (or purchase) price and the ID of the customer who buys it. This code can perform all kinds of calculations, look up tables in the database, or perform any other action, and return the product’s sale price: the price that will appear on the customer’s receipt. We’ll keep our example simple and sell with the list price. To implement any other pricing policy, I recommend implementing a procedure that accepts as arguments the ID of the product being sold and the ID of the buyer, and returns a price. This procedure can be a class method or even a stored procedure in the database.
Let’s write some code to populate a few instances of the Book and Supply classes. The following statements populate a HashTable with books and supplies. The HashTable is a structure for storing objects along with their keys. In this case, the keys are the IDs of the products. The HashTable can locate items by means of their keys very quickly, and this is why I chose this type of collection to store the data. HashTables, as well as other collections, are discussed in detail in Chapter “Storing Data in Collections“.
Code language: PHP (php)
Dim P1 As New Book P1.ListPrice = 13.24D P1.Description = "Book Title 1" P1.ProductID = "EN0101" P1.ISBN = "0172833223" P1.Subtitle = "Book Title 1 Subtitle" Products.Add(P1.ProductID, P1) Dim P2 As New Supply P2.Description = "Supply 1" P2.ListPrice = 2.25D P2.LongDescription = "Long description of item 1" P2.ProductID = "S0001-1" Products.Add(P2.ProductID, P2)
Products is the name of the collection in which the products are stored, and is declared as follows:
Code language: PHP (php)
Dim Products As New Hashtable
Each item in the Products collection is either of the Book or of the Supply type, and you can find out its type with the following expression:
Code language: CSS (css)
If TypeOf Products.Item(key) Is Book ...
Listing 7.7 shows the code behind the Display Products button on the sample application’s form. The code iterates through the items of the collection, determines the type of each item, and adds the product’s fields to the appropriate ListView control.
Listing 7.7: Iterating through a Collection of Book and Supply Products
Code language: PHP (php)
Private Sub Button2 Click(...) Handles bttnDisplay.Click Dim key As String Dim LI As ListViewItem For Each key In Products.Keys LI = New ListViewItem Dim bookItem As Book, supplyItem As Supply If TypeOf Products.Item(key) Is Book Then bookItem = CType(Products.Item(key), Book) LI.Text = bookItem.ISBN LI.SubItems.Add(bookItem.Description) LI.SubItems.Add("") LI.SubItems.Add( bookItem.ListPrice.ToString("#,##0.00")) ListView1.Items.Add(LI) End If If TypeOf Products.Item(key) Is Supply Then supplyItem = CType(Products.Item(key), Supply) LI.Text = supplyItem.ProductID LI.SubItems.Add(supplyItem.Description) LI.SubItems.Add(supplyItem.LongDescription) LI.SubItems.Add( supplyItem.ListPrice.ToString("#,##0.00")) ListView2.Items.Add(LI) End If Next End Sub
It’s fairly easy to take advantage of inheritance in your projects. The base class encapsulates the functionality that’s necessary for multiple classes. All other classes inherit from the base class and add specific members that don’t apply to other classes (at least, not all of them). The actual data resides in a database, and you’ll have to write code that populates the Products collection from the database, but this is a topic we’ll discuss in Chapter 21, ‘‘Basic Concepts of Relational Databases.”
As I mentioned earlier, for the purposes of selling products, you can use the Product class. You can search for both books and supplier with their ID or bar code and use the product’s description and price to generate an invoice.
The following statements retrieve a product by its ID and print its description and price:
Code language: PHP (php)
Private Sub Button3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button3.Click Dim id As String id = InputBox("ID") Dim selProduct As Product = CType(Products.Item(id.ToUpper), Product) If selProduct Is Nothing Then MsgBox("No such product was found") Else MsgBox("The price of " & selProduct.Description & _ " is " & selProduct.ListPrice) End If End Sub
If executed, the preceding statements will print the following in the Output window. This is all the information you need to prepare invoices and orders, and it comes from the Product class, which is the base class for all products.
Code language: CSS (css)
The price of Supply 2 is 5.99
Before ending this section, I should point out that you can convert the type of an inherited class only to that of the parent class. You can convert instances of the Book and Supply class to objects of the Product type, but not the opposite. The only valid type conversion is a widening conversion (from a narrower to a wider type).
You won’t be hard-pressed to come up with real-world situations that call for inheritance. Employees, customers, and suppliers can all inherit from the Person class. Checking and savings accounts can inherit from the Account class, which stores basic information such as customer info and balances. Later in this chapter, you’ll develop a class that represents shapes and you’ll use it as a basis for classes that implement specific shapes such as circles, rectangles, and so on.