only for RuBoard |
Once upon a time, in a dimly lit cube...
After writing CreditCard , GiftCertificate , and Check classes, a self-realization occurs: "Self, I see that I can group all of the common behavior from these classes into a base class called Payment ." This realization usually happens when you start to see that many classes you work with have the same method names (meaning that they exhibit the same behavior).
This general payment class (shown in Example 4-1) might contain methods to authorize, credit, and bill for a specific amount, as well as associate the payment with an account number. The general rules of the class are that all payments are authorized first, then they are billed. A billed payment may also be credited.
Imports System Public Class Payment Private account As String Private amount As Double Private authorized As Boolean Private billed As Boolean Public Sub New(ByVal account As String) Me.account = account authorized = False billed = False amount = 0 End Sub Protected ReadOnly Property AccountNumber( ) As String Get Return Me.account End Get End Property Public Function Authorize(ByVal amount As Double) As Boolean If authorized Then Console.WriteLine("Payment is already authorized") Else Me.amount = amount Console.WriteLine("Authorizing payment for {0:c}", _ amount) authorized = True End If Return authorized End Function Public Function Bill( ) As Boolean If authorized Then Console.WriteLine("Billing payment for {0:c}", _ amount) billed = True Else Console.WriteLine("Payment is not authorized") End If Return billed End Function Public Function Credit( ) As Boolean If billed Then Console.WriteLine("Crediting payment for {0:c}", _ amount) billed = False Return True Else Console.WriteLine("Payment has not been billed") End If Return False End Function End Class
Account information is considered sensitive. This information would probably be stored in a database somewhere in an encrypted state. When the payment information is retrieved, the account number can be unencrypted and used by the object itself. It never needs to be seen by human eyes. It makes sense, then, that AccountNumber should be a protected property, since derived classes would need access to account information as well. The remaining methods of the class are public.
After grouping this behavior in a base class, you can now extend it through inheritance. As shown in Example 4-2, a specialized classperhaps a credit cardmight need to verify the account number. Also, making the account number readily available outside of the class is unwise. However, in terms of credit card numbers , it is often necessary to display a portion of the number on an invoice. To make this display possible, you can add a DisplayNumber property to the class that returns the account number but hides most significant digits (i.e., 4XXXXXXXXXXX1234 ).
Using the Inherits keyword, you can create a CreditCard class that includes all the public methods from Payment . Additional methods can then be added to the derived class, as shown in Example 4-2.
Imports System Imports System.Text Public Class CreditCard Inherits Payment Public Sub New(ByVal account As String) MyBase.New(account) End Sub Public Sub Verify( ) Console.WriteLine("Verifying...") End Sub Public ReadOnly Property DisplayNumber( ) As String Get 'Faster than concatenating normal strings Dim sb As New StringBuilder( ) Dim account As String = Me.AccountNumber Dim len As Integer = account.Length sb.Append(account.SubString(0, 1)) sb.Append(New String("X"c, len - 5)) sb.Append(account.SubString(len - 4, 4)) Return sb.ToString( ) End Get End Property End Class
Alternatively, inheritance can be declared like this:
Public Class CreditCard : Inherits Payment 'Methods here End Class
Here, the colon signifies a new line of code in VB and mimics the inheritance declaration of C#, C++, and Java. Both definitions are equivalent, and using either is a matter of style. The latter will be used throughout the book, but only for readabilitynot to garner the respect of C++ programmers.
However, there is a problem. Constructors are not inherited, so every class must provide one. Otherwise , the compiler will create an empty one. In fact, if the base class has parameterized constructors but does not have a parameterless constructor, a constructor must be defined explicitly. If it is not, the VB.NET compiler will generate an error.
Before an instance of CreditCard is created, a constructor that passes the account number to the base class must be provided. The MyBase keyword allows a base class method to be called from a derived class, so the task is trivial:
Public Class CreditCard : Inherits Payment Public Sub New(ByVal account As String) MyBase.New(account) End Sub Public Sub Verify( ) Console.WriteLine("Verifying {0}", Me.AccountNumber) End Sub 'Other methods here End Class
Now that an appropriate constructor is in place, you can create an instance of CreditCard that can use all the public methods from Payment :
Dim cc As New CreditCard("4111111111111111") cc.Verify( ) If cc.Authorize(3.33) Then If cc.Bill( ) Then cc.Credit( ) End If End If
Remember that everything that is protected in Payment is not available from outside the class:
'Error - internal use only Dim visa As New CreditCard("4111111111111111") Dim accountNumber As String = visa.AccountNumber But it is available within the derived class: Public Class CreditCard : Inherits Payment Public Sub Verify( ) Console.WriteLine("Verifying {0}", Me.AccountNumber) End Sub End Class
Everything that is private in the Payment class is just that. You can do nothing about it. Private data is not directly accessible outside of the scope where it was declared, regardless of whether it is a derived class or not. And to reiterate (and probably not for the last time), all member data for any class should always be private. [1]
[1] It should be privateat least initially. Relaxing restrictions is easier than enforcing them. Working from a totally public class interface would be more difficult than working from a more restricted one because of interdependencies that could occur during the development cycle.
|
Strings Are ImmutableBe careful and considerate when using strings. Strings in .NET are immutable, meaning they cannot be changed. There really is no such thing as appending one string to another, although the following code fragment mistakenly suggests otherwise: Dim msg As String = "Hello" msg += ", World!" Actually, when two strings are concatenated like this, a third string is created. The string "Hello" is then marked for finalization . You will often need to compose a string within a loop by appending a value to a master string on each iteration. This is the case with Example 4-2. Do not use String for this purpose. Instead, use the StringBuilder class in System.Text . |
The Payment class implicitly derives from System.Object , as does CreditCard . This is done automatically; nothing special has to be done because the compiler handles it all.
Inheritance from System.Object provides a common set of functionality for all objects running under .NET. Therefore, each object will have the following standard set of behaviors associated with it:
This behavior compares objects. You can override this method and make your own determinations about when one of your objects is considered equal to another.
This behavior determines whether two objects share the same reference.
This method is called before the garbage collector frees the memory associated with the object. It is not called when the object goes out of scope; rather, it is called whenever the garbage collector deems it necessary. Therefore, you should not rely on this method to free any limited resources that might be associated with your object.
This method generates a number that represents the object's value. It allows all objects to participate in the hash table implementations provided by .NET.
This behavior returns an instance of System.Type , which is used heavily in reflection (see Chapter 7). A Type object can be used to find class- related information about an object, such as the methods it has, whether the class is abstract, and the events it publishes.
This method describes an object by returning a meaningful text string. By default, it returns the name of the class, but it can be overridden. When an object is used in a place that requires a string, this method is called automatically. For example, Console.WriteLine(myObject) results in a call to myObject.ToString .
VB does not support multiple inheritance; a class cannot be derived from more than one class at the same time . A single inheritance hierarchy can be sustained indefinitely, though, as the following code suggests:
Public Class Visa : Inherits CreditCard End Class
Here, we've defined a Visa class, which inherits from the CreditCard class, which in turn inherits from the Payment class. The Payment class in turn implicitly inherits from the framework's System.Object class.
Eventually, your hierarchies will become very specific, and at some point you may want to prevent further derivation. You can use the NotInheritable modifier to accomplish this goal. This modifier is mutually exclusive with MustInherit , the Visual Basic keyword discussed in the next section:
Public Class NotInheritable DebitCard : Inherits Visa 'Can no longer inherit End Class
Considering that there is no generic way to authorize, bill, and credit the various payment types, you should probably make Payment an abstract base class (ABC). An ABC usually contains minimal functionality that can be used by all derivatives, but its main purpose is to serve as a template of sorts. It defines the methods a subclass needs to support to be considered a kind of the base class.
To make Payment an abstract base class, add the MustInherit keyword to the class declaration from Example 4-1. The class declaration then appears as follows :
Public MustInherit Class Payment
The derived CreditCard class still functions as it did before, but the ABC cannot be instantiated :
Dim myPayment As New Payment( ) 'Error
Preventing instances of a base class is usually preferable because it represents an idea more than a specific "thing." The specifics are left to the concrete classes that are derived from the base class, such as CreditCard , GiftCertificate , and Check .
only for RuBoard |