An Example of Inheritance and Polymorphism


Let's use the Building class from Chapter 17 as a basis for the example presented here. The following sections concentrate on the changes you need to make to the code.

A Base Class Constructor

The first change you need to implement is a base class constructor for the Building class. The new code for the Building class is shown in Listing 18.1.

Listing 18.1 The Base Class Constructor for the Building Class
 ' ================== Constructor ================== Public Sub New()  mAddress = "Empty building"  ' The remaining data = 0 anyway End Sub 

When you create the Building class constructor, you need no arguments. At first blush, the constructor code shown in Listing 18.1 probably seems obvious. Then it hits you: "Wait a minute! The Building class is defined as a virtual class, using the MustInherit keyword. How can I have a constructor for an object I cannot create?" Good question.

Remember that you can treat a derived class object as if it were a base class object, but not vice versa. Therefore, if you create a derived object of the Building class with an empty initializer list, the base class constructor shown in Listing 18.1 is used. Under such circumstances, all the member data values are initialized to (by default), except the mAddress member, which is initialized to "Empty building" .

If you wanted to initialize the other members of the base class to some nonzero value, you could. However, leaving those members with the value might be a good idea because it reinforces the idea that the object does not yet contain valid data values.

Constructors with Initializer Lists

If you rely on the default constructor for the derived objects of the Building class, you have to use the property methods of the object to fill in the appropriate values. As an alternative, however, you can overload the constructor to initialize the base class members. Listing 18.2 shows the alternative constructor, using an initializer list for the Apartment class; the code is virtually identical for the other derived classes, too.

Listing 18.2 Constructors for the Derived Building Classes, Using an Initializer List
 Public Sub New()  ' The Apartment constructor with no arguments End Sub ' Constructor with initializer list of arguments Public Sub New(ByVal Addr As String, ByVal Price As Double, ByVal Payment _         As Double, ByVal Taxes As Double, ByVal Bldg As Integer)  MyBase.Address = Addr  MyBase.PurchasePrice = Price  MyBase.MonthlyPayment = Payment  MyBase.Taxes = Taxes  MyBase.BuildingType = Bldg End Sub 

The first thing to notice in Listing 18.2 is that each derived class has two constructors. Why? You made the design decision that you will allow the user of the class to create an object with or without an initializer list for the members of the base class. You must therefore provide constructors for both types of object instantiation. This means that the following statements to create an Apartment object are valid:

 Dim MyApartment As New Apartment("6 North Ben Street", _               550000, 6000, 12000, APARTMENT) Dim YourApt as New Apartment() 

If you did not provide the empty Apartment constructor, the second statement line would force Visual Basic .NET to tell you that you are missing the five arguments required in the constructor's initializer list. If you supplied only the first constructor in Listing 18.2, this statement:

 Dim MyApartment As New Apartment("6 North Ben Street", _              550000, 6000, 12000, APARTMENT) 

would cause Visual Basic .NET to issue a "too many arguments" error message. Therefore, if you want to allow both types of definition statements, you need both constructors in the derived classes.

Note that the code in Listing 18.2 for the second constructor type has an initializer list that only initializes the data members in the base class. You do not require the initializer list to include the members of the derived class. You could do this if you wanted, but I chose not to in this case, in order to keep the code a little bit simpler. If you want to allow a complete initializer list to include those members of the derived class, you should add the class members' parameters to the initializer list in Listing 18.2 for each derived class.

Programmer's Tip

graphics/tip_icon.gif

In the strictest sense, it is not necessary to use MyBase in Listing 18.2 because the property accessor methods in the base class are defined by using the Public keyword. However, using the MyBase keyword in the derived classes helps document what the constructor is doing.


Keep in mind that each of the derived classes needs to implement two constructor methods, similar to those shown in Listing 18.2. Only by using two constructors can you allow each derived class to have the option of either an empty or a nonempty initializer list. Given that fact, why don't you just move the constructor with the initializer list into the Building base class and omit it from the derived classes? That would be a good idea, but it won't work because you have declared all the member data in the Building class to be Private . Therefore, you are forced to use the property accessor methods of the base class to affect the values of the members in the base class.

Let's add one more method to your derived classes but force each class to supply the method. In the Building class code, you should add the following line just before the DisplayBaseInfo() method:

 Public MustOverride Function CallSnowRemoval() As String 

This statement says that each derived class must provide its own version of the CallSnowRemoval() method. Why would you do this? First, remember that when the base class is an virtual class as the Building class is, its purpose is to serve as a collection point for commonalities shared in the derived classes. Therefore, the properties that distinguish each of the derived classes are properly found in the derived class code.

In other words, MustOverride means you are allowing different actions to be taken for each derived class when snow needs to be removed from the property. For example, the Apartment and Commercial classes can use the CallSnowRemoval() method to call a large commercial snow removal company. The Home class, however, might just call the person renting the home and remind him that his lease requires him to shovel the walk.

Because the base class declares the method, the derived classes must define (that is, implement) the method. Therefore, in each derived class there must be code similar to the following (which uses the Apartment class as an example):

 Public Overrides Function CallSnowRemoval() As String   ' Purpose: This routine is called to remove snow for the apartment.  '  Return "Apartment snow removal: " & MyBase.SnowRemoval End Function 

The use of the keyword Overrides in the first statement line tells Visual Basic .NET to override a method that is declared in the base class. Notice that you use the new SnowRemoval() property accessor method to read a new member named mSnowPhone that you added to the base Building class. (The revised code is presented in Listing 18.3.)

The important point to remember is that MustOverride requires each derived class to implement its own version of the CallSnowRemoval() method. It also follows that the base class does not implement a version of the CallSnowRemoval() method. The base class declares the CallSnowRemoval() method, and the derived classes define the method.

The complete listing for the Building class and the derived classes is presented in Listing 18.3.

Listing 18.3 Code for the Building Class and the Derived Classes
 Option Strict On Public MustInherit Class Building  ' This is the base class, from which we derive the  ' apartment, commercial, and home classes.  ' =============== Symbolic Constants ================  Private Const APARTMENT As Integer = 0  Private Const COMMERCIAL As Integer = 1  Private Const HOME As Integer = 2  ' Default snow removal phone numbers  Public Const APTPHONE As String = "555-1000"  Public Const COMPHONE As String = "555-2000"  Public Const HOMPHONE As String = "555-3000"  ' ================== Data Members ==================  Private mAddress As String     ' Street address  Private mPrice As Double      ' Purchase price  Private mMonthlyPayment As Double  ' Monthly payment  Private mTaxes As Double      ' Annual taxes  Private mType As Integer      ' Building type  Private mSnowPhone As String    ' Who to call for snow removal  ' ================== Constructor ==================  Public Sub New()   Address = "Empty building"  End Sub  ' ==================== Properties ======================  Public Property Address() As String      ' Address Property   Get    Return mAddress   End Get   Set(ByVal Value As String)    mAddress = Value   End Set  End Property  Public Property PurchasePrice() As Double  ' Purchase Price Property   Get    Return mPrice   End Get   Set(ByVal Value As Double)    mPrice = Value   End Set  End Property  Public Property MonthlyPayment() As Double  ' Monthly Payment Property   Get    Return mMonthlyPayment   End Get   Set(ByVal Value As Double)    mMonthlyPayment = Value   End Set  End Property  Public Property Taxes() As Double      ' Property Taxes Property   Get    Return mTaxes   End Get   Set(ByVal Value As Double)    mTaxes = Value   End Set  End Property  Public Property BuildingType() As Integer   ' Building Type Property   Get    Return mType   End Get   Set(ByVal Value As Integer)    If Value < APARTMENT Or Value > HOME Then     mType = -1  ' an error    Else     mType = Value    End If   End Set  End Property  Public Property SnowRemoval() As String    ' Call for snow removal   Get    Return mSnowPhone   End Get   Set(ByVal Value As String)    mSnowPhone = Value   End Set  End Property  ' ========================== Methods ========================  ' Force the derived classes to figure out who to call.  Public MustOverride Function CallSnowRemoval() As String  Protected Sub DisplayBaseInfo()   Dim BldType As String   ' Purpose: To display the base member data   Select Case mType       ' Which type of building?    Case APARTMENT     BldType = "Apartment"    Case COMMERCIAL     BldType = "Commercial"    Case HOME     BldType = "Home"   End Select   Console.WriteLine()   Console.WriteLine("---------- " & BldType & " -----------")   Console.WriteLine("Address: " & mAddress)   Console.WriteLine("Purchase Price: " & FormatMoney(mPrice))   Console.WriteLine("Monthly Payment: " & FormatMoney(mMonthlyPayment))   Console.WriteLine("Property Taxes: " & FormatMoney(mTaxes))   Console.WriteLine("Building Type: " & BldType)   Console.WriteLine("Snow removal Phone: " & mSnowPhone)   Console.WriteLine()  End Sub  ' =========================== Helpers =========================  Protected Function FormatMoney(ByVal num As Double) As String   ' Purpose: To format a dollar value   '   ' Argument list:   '  num     A double that is the dollar value to format   '   ' Return value:   '  string   A format dollar value string   Return Format(num, "$#,###,###,###.00")  End Function End Class ' ++++++++++++++++++++++++++++ Apartment Class ++++++++++++++++++++++++ Public Class Apartment  Inherits Building  Private mUnits As Integer   ' The number of apartments  Private mRent As Double    ' Rent per unit  Private mOccupRate As Double ' Occupancy rate for building  Public Sub New()  End Sub  ' Constructor with initializer list of arguments  Public Sub New(ByVal Addr As String, ByVal Price As Double, _          ByVal Payment As Double, ByVal Taxes As Double, _          ByVal Bldg As Integer, ByVal SnowPhone As String)   MyBase.Address = Addr   MyBase.PurchasePrice = Price   MyBase.MonthlyPayment = Payment   MyBase.Taxes = Taxes   MyBase.BuildingType = Bldg   If SnowPhone = "" Then    MyBase.SnowRemoval = APTPHONE   Else    MyBase.SnowRemoval = SnowPhone   End If  End Sub  ' ==================== Properties ======================  Public Property Units() As Integer    ' Units   Get    Return mUnits   End Get   Set(ByVal Value As Integer)    mUnits = Value   End Set  End Property  Public Property Rents() As Double    ' Rents   Get    Return mRent   End Get   Set(ByVal Value As Double)    mRent = Value   End Set  End Property  Public Property OccupancyRate() As Double ' Occupancy rate   Get    Return mOccupRate   End Get   Set(ByVal Value As Double)    mOccupRate = Value   End Set  End Property  ' ============== Methods ==================  Public Overrides Function CallSnowRemoval() As String   ' Purpose: This routine is called to remove snow for an Apartment.   '   Return "Apartment snow removal: " & MyBase.SnowRemoval  End Function  Public Sub DisplayBuilding()   DisplayBaseInfo()   Console.WriteLine("      Apartment members:")   Console.WriteLine("Number of Units: " & mUnits)   Console.WriteLine("Rent per Unit: " & FormatMoney(mRent))   Console.WriteLine("Occupancy Rate: " & mOccupRate)  End Sub End Class ' ++++++++++++++++++++++++++++ Commercial Class ++++++++++++++++++++++++++++ Public Class Commercial  Inherits Building  Private mSquareFeet As Integer  ' Rentable square feet  Private mRentPerSF As Double   ' Rent per square foot  Private mParking As Integer    ' Parking spots  Public Sub New()  ' Constructor with no arguments  End Sub  Public Sub New(ByVal Addr As String, ByVal Price As Double, _         ByVal Payment As Double, ByVal Taxes As Double, _         ByVal Bldg As Integer, ByVal SnowPhone As String)   MyBase.Address = Addr   MyBase.PurchasePrice = Price   MyBase.MonthlyPayment = Payment   MyBase.Taxes = Taxes   MyBase.BuildingType = Bldg   If SnowPhone = "" Then    MyBase.SnowRemoval = COMPHONE   Else    MyBase.SnowRemoval = SnowPhone   End If  End Sub  ' ==================== Properties ======================  Public Property SquareFeet() As Integer   ' Square feet   Get    Return mSquareFeet   End Get   Set(ByVal Value As Integer)    mSquareFeet = Value   End Set  End Property  Public Property RentPerSF() As Double    ' Rent per square foot   Get    Return mRentPerSF   End Get   Set(ByVal Value As Double)    mRentPerSF = Value   End Set  End Property  Public Property ParkingSpots() As Integer  ' Parking spots   Get    Return mParking   End Get   Set(ByVal Value As Integer)    mParking = Value   End Set  End Property  ' ============== Methods ==================  Public Overrides Function CallSnowRemoval() As String   ' Purpose: This routine is called to remove snow for a Commercial building.   '   Return "Commercial snow removal: " & MyBase.SnowRemoval  End Function  Public Sub DisplayBuilding()   DisplayBaseInfo()        ' Call base class   Console.WriteLine("      Commercial members:")   Console.WriteLine("Square Feet: " & mSquareFeet)   Console.WriteLine("Rent per SF: " & FormatMoney(mRentPerSF))   Console.WriteLine("Parking Spots: " & mParking)  End Sub End Class ' ++++++++++++++++++++++++++++ Home Class ++++++++++++++++++++++++++++ Public NotInheritable Class Home  Inherits Building  Private mSquareFeet As Integer   ' Home's square feet  Private mRentPerMonth As Double   ' Rent per month  Private mBedrooms As Integer    ' Number of bedrooms  Private mBaths As Integer      ' Number of bathrooms  ' ==================== Properties ======================  Public Sub New()  ' Constructor with no arguments  End Sub  Public Sub New(ByVal Addr As String, ByVal Price As Double, _         ByVal Payment As Double, ByVal Taxes As Double, _         ByVal Bldg As Integer, ByVal SnowPhone As String)   MyBase.Address = Addr   MyBase.PurchasePrice = Price   MyBase.MonthlyPayment = Payment   MyBase.Taxes = Taxes   MyBase.BuildingType = Bldg   If SnowPhone = "" Then    MyBase.SnowRemoval = HOMPHONE   Else    MyBase.SnowRemoval = SnowPhone   End If  End Sub  Public Property SquareFeet() As Integer    ' Square feet   Get    Return mSquareFeet   End Get   Set(ByVal Value As Integer)    mSquareFeet = Value   End Set  End Property  Public Property Rent() As Double       ' Rent   Get    Return mRentPerMonth   End Get   Set(ByVal Value As Double)    mRentPerMonth = Value   End Set  End Property  Public Property Bedrooms() As Integer     ' Bedrooms   Get    Return mBedrooms   End Get   Set(ByVal Value As Integer)    mBedrooms = Value   End Set  End Property  Public Property Baths() As Integer       ' Baths   Get    Return mBaths   End Get   Set(ByVal Value As Integer)    mBaths = Value   End Set  End Property  ' ============== Methods ==================  Public Overrides Function CallSnowRemoval() As String   ' Purpose: This routine is called to remove snow for an apartment.   '   Return "Home snow removal: " & MyBase.SnowRemoval  End Function  Public Sub DisplayBuilding()   MyBase.DisplayBaseInfo()        ' Call base class   Console.WriteLine("      Home members:")   Console.WriteLine("Square Feet: " & mSquareFeet)   Console.WriteLine("Rent per Month: " & FormatMoney(mRentPerMonth))   Console.WriteLine("Bedrooms: " & mBedrooms)   Console.WriteLine("Baths: " & mBaths)  End Sub End Class 

The code in Listing 18.3 is very similar to the code in Listing 17.2 from Chapter 17. Listing 18.3 includes added constructors for the base and derived classes. In each derived class constructor, an If statement block is used to allow for an "empty" phone number to be passed to the constructor. Consider the following code fragment for the Apartment class:

 If SnowPhone = "" Then   MyBase.SnowRemoval = APTPHONE ' Use the default snow phone Else  MyBase.SnowRemoval = SnowPhone End If 

If the argument SnowPhone is an empty string, the default snow removal phone number is supplied. (In real life, for example, a newly rented home that is being added to the list of real estate investments might be added before the new tenant has received a new home phone number.)

You should examine the constructors for the derived classes in Listing 18.3 because that code is not present in Listing 17.2. The code itself is fairly straightforward, and you should be comfortable reading it.



Visual Basic .NET. Primer Plus
Visual Basic .NET Primer Plus
ISBN: 0672324857
EAN: 2147483647
Year: 2003
Pages: 238
Authors: Jack Purdum

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net