|only for RuBoard|
As an abstract base, Payment is pretty limited in its current state. You can only overload methods in the class; that is, you can add new methods that have the same name as existing methods. However, existing behavior cannot be redefined. Note that different payment types need to implement Authorize , Bill , and Credit in different ways. Credit cards and checks are authorized through outside companies. However, while credit-card billing is handled by one of those same outside companies, billing a check means that Racer Jim gets an email and the checks are taken down to the bank. Finally, gift certificates are handled in-house; the information is all stored in a database, so there is quite a bit of variation here.
An existing behavior in a base class can be completely redefined by overriding it in the derived class, but only if the writer of the base class has made it permissible to do so. For a method to be overridden, it must be declared by using the Overridable modifier. Naturally, this suggests some foresight on the part of the class designer. If the method is not marked as overridable, one of two things is happening: the person who coded the class does not want the method to be overridden, or he or she was not thinking ahead of the game. In the latter case, there is a loophole of sorts (see "Shadowing" at the end of this chapter) in the language that allows circumvention of this grievous situation. But for now, assume that the person who wrote the base class had a thinking cap on and defined the Payment class as shown in Example 4-7.
Public MustInherit Class Payment 'Other methods a data here Public Overridable Function Authorize(ByVal amount As Double) As Boolean 'Base implementation End Function Public Overridable Function Bill( ) As Boolean 'Base implementation End Function Public Overridable Function Credit( ) As Boolean 'Base implementation End Function End Class
The person deriving a new class can specify that the method is overridden using the Overrides modifier, as shown here:
Public Class CreditCard : Inherits Payment 'Other methods and data here Public Overrides Function Authorize(ByVal amount As Decimal) _ As Boolean 'Authorize credit card End Function Public Overrides Function Bill( ) As Boolean 'Bill credit card End Function Public Overrides Function Credit( ) As Boolean 'Refund credit card End Function End Class
When a method is overridden, the original definition in the base class is completely hidden. To extend the behavior, rather than merely override it, MyBase can be used to call the base class implementation, as shown in the following code:
Public Overrides Function Bill( ) As Boolean 'Do credit card-specific things here 'then call base class method '(which updates database) MyBase.Bill( ) End Function
Alternatively, the person writing the base class may say, "Hey, there really is no generic behavior for any of this!" It is not uncommon for an abstract base class to require that a behavior be present in a derived class but provide no implementation. As far as flexible design goes, defining an abstract base class with no implementation for its members is the way to go.
Think about the Payment class. No implementation could be provided for Authorize , Bill , or Credit . You have just deluded yourself all along. Yes, you've been in a fantasy world. These operations are handled so differently between one payment type and the next , what would a generic implementation do?
A more realistic way to deal with this issue is to force a derived class to implement a behavior. Derived classes like CreditCard or GiftCertificate can be made to expose a key set of operations by using the MustOverride modifier with the base class' public members, as shown in Example 4-8.
Public MustInherit Class Payment 'No End Function required (Definition only, no implementation) Public MustOverride Function Authorize(ByVal amount As Double) _ As Boolean Public MustOverride Function Bill( ) As Boolean Public MustOverride Function Credit( ) As Boolean End Class
C++ programmers might recognize this example as a pure virtual function . This type of function is used when a default behavior cannot be adequately described. However, you also create a guarantee or a contract, which is enforced by the compiler. By creating this guarantee or contract, it is assured that every class deriving from Payment will support these three operations. MustOverride methods actually play an important role in polymorphism.
When a method is marked Overridable , it is considered that way all the way down the inheritance tree. Suppose the Payment class contains a method that is used internally to verify the validity of an account number. Knowing that every payment type needs a different way to validate this number, the creator of the class has made it overridable:
Public MustInherit Class Payment Protected Overridable Function IsValidNumber( ) As Boolean Return False End Function End Class
Although IsValidNumber was initially defined as overridable, at some point, it might be practical to prevent this override by using the NotOverridable modifier:
Public Class CreditCard : Inherits Payment Protected NotOverridable Function IsValidNumber( ) As Boolean 'Use LUHN Mod 10 algorith to validate 'card number End Function End Class
NotOverridable prohibits further overriding once it has begun. In this case, the base class Payment provides minimal functionality for the IsValidNumber method. It merely returns False . Realizing the simplicity of the provided functionality, the method is marked as Overridable .
The CreditCard class then overrides this behavior to use the Luhn Mod 10 algorithm to validate card numbers . At this point, it is decided that all credit cards that might be derived in the future should use this algorithm. Therefore, NotOverridable is used to prevent further overriding. This way, new credit card classes cannot be derived, which could potentially sidestep this part of the validation process. ( I f you are interested in seeing how this algorithm works, see the Luhn Mod 10 Algorithm sidebar.)
Luhn Mod 10 Algorithm
The Luhn Mod 10 algorithm validates the numbers of most major credit card companies. Given the credit card number 4111111112127777, here's how it works:
The source code for the Luhn function is:
Private Function Luhn(ByVal cc As String) As Boolean Dim i As Integer Dim total As Integer Dim even As String Dim len As Integer = cc.Length Dim digits(len - 1) As Integer For i = 0 To len - 1 digits(i) = CInt(cc.Substring(i, 1)) Next i For i = len - 1 To 1 Step -2 total += digits(i) even = (digits(i - 1) * 2).ToString("00") total += Convert.ToInt32(even.Substring(0, 1)) total += Convert.ToInt32(even.Substring(1, 1)) Next i If len Mod 2 <> 0 Then total += digits(0) End If If total Mod 10 = 0 Then Return True End If Return False End Function
|only for RuBoard|