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 ConstructorThe 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 ListsIf 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 ListPublic 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
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 ClassesOption 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. |