Section 11.7. Case Study: Creating and Using Interfaces


11.7. Case Study: Creating and Using Interfaces

Our next example reexamines the payroll system of Section 11.5. Suppose that the company involved wishes to perform several accounting operations in an accounts payable application In addition to calculating the earnings that must be paid to each employee, the company also wants to calculate the payment due on each of several invoices (i.e., bills for goods and services purchased). Though applied to unrelated things (i.e., employees and invoices), both operations have to do with obtaining some kind of payment amount. For an employee, the payment refers to the employee's earnings. For an invoice, the payment refers to the total cost of the goods and services listed on the invoice. Can we calculate such different things as the payments due for employees and invoices in a single application polymorphically? Does Visual Basic have a capability that forces possibly unrelated classes to implement a set of common methods (e.g., a method that calculates a payment amount)? Visual Basic interfaces offer exactly this capability.

Interfaces define and standardize the ways in which things such as people and systems can interact with one another. For example, the controls on a radio serve as an interface between radio users and a radio's internal components. The controls allow users to perform only a limited set of operations (e.g., changing the station, adjusting the volume, choosing between AM and FM), and different radios may implement the controls in different ways (e.g., using push buttons, dials, voice commands). The interface specifies what operations a radio permits users to perform but does not specify how the operations are implemented. Similarly, the interface between a driver and a car with a manual transmission includes the steering wheel, the gear shift, and the clutch, gas and brake pedals. This same interface is found in nearly all manual transmission cars, enabling someone who knows how to drive one particular manual transmission car to drive just about any manual transmission car. The components of each individual car may look a bit different, but the components' general purpose is the sameto allow people to drive the car.

Software objects also communicate via interfaces. A Visual Basic interface describes a set of methods that can be called on an object, to tell the object to perform some task or return some piece of information, for example. An interface is often used in place of a Must-Inherit class when there is no default implementation to inheritthat is, no instance variables and no default method and property implementations. The next example introduces an interface named IPayable to describe the functionality of any object that must be capable of being paid and thus must offer a method to determine the proper payment amount due. An interface declaration begins with the keyword Interface and can contain abstract methods and properties, but cannot contain instance variables, concreate methods or concrete properties. Unlike classes, interfaces may not specify any implementation details, such as concrete method declarations and instance variables. All members declared in an interface are implicitly Public, and may not specify an access modifier.

Common Programming Error 11.5

It is a compilation error to explicitly declare interface methods Public.


Common Programming Error 11.6

In Visual Basic, an Interface can be declared only as Public or Friend; the declaration of an Interface as Private or Protected is an error.


To use an interface, a concrete class must specify that it Implements the interface and must implement each method in the interface with the signature and return type specified in the interface declaration. A class that does not implement all the methods of the interface is an abstract class and must be declared MustInherit. Implementing an interface is like signing a contract with the compiler that states, "I will declare all the methods specified by the interface or I will declare my class MustInherit."

Common Programming Error 11.7

Failing to implement any method of an interface in a concrete class that Implements the interface results in a syntax error indicating that the class must be declared MustInherit.


An interface is typically used when unrelated classes need to share common methods and properties. This allows objects of unrelated classes to be processed polymorphically objects of classes that implement the same interface have an is-a relationship with the interface type and can respond to the same method calls. Programmers can create an interface that describes the desired functionality, then implement this interface in any classes that require that functionality. For example, in the accounts payable application that we develop in the next several subsections, we implement interface IPayable in any class that must be able to calculate a payment amount (e.g., Employee, Invoice).

11.7.1. Developing an IPayable Hierarchy

To build an application that can determine payments for employees and invoices alike, we will first create interface IPayable. Interface IPayable contains method GetPayment-Amount, which returns a Decimal amount that must be paid for an IPayable object, and method ToString, which returns the String representation of an IPayable object. Method GetPaymentAmount is a general purpose version of the Employee hierarchy's CalculateEarnings methodmethod CalculateEarnings calculates a payment amount specifically for an Employee, while GetPaymentAmount can be applied more generally to unrelated objects. After declaring interface IPayable, we will introduce class Invoice, which implements interface IPayable. We then modify class Employee so that it, too, implements interface IPayable. Finally, we update Employee derived class SalariedEmployee to "fit" into the IPayable hierarchy (i.e., we rename SalariedEmployee method CalculateEarnings as GetPaymentAmount).

Good Programming Practice 11.1

When declaring a method in an interface, choose a method name that describes the method's purpose in a general manner, because the method may be implemented by many unrelated classes.


Classes Invoice and Employee both represent things for which the company must be able to calculate a payment amount. Both classes implement IPayable, so a program can invoke method GetPaymentAmount on Invoice objects and Employee objects alike. As you will soon see, this enables the polymorphic processing of Invoices and Employees required for our accounts payable application.

The UML class diagram in Fig. 11.10 shows the class and interface hierarchy used in our accounts payable application. The hierarchy begins with interface IPayable. The UML distinguishes an interface from other classes by placing the word "interface" in guillemets (« ») above the interface name. The UML expresses the relationship between a class and an interface through an association known as a realization. A class is said to "realize," or implement, an interface. A class diagram models a realization as a dashed arrow with a hollow arrowhead pointing from the implementing classes to the interface. The diagram in Fig. 11.10 indicates that class Invoice realizes (i.e., implements) interface IPayable. Class Employee also realizes interface IPayable, but it is not required to provide method implementations, because it is an abstract class (note that it appears in italics). Concrete class SalariedEmployee inherits from Employee and inherits its base class's realization relationship with interface IPayable, so SalariedEmployee must implement the method(s) of IPayable.

Figure 11.10. IPayable interface hierarchy UML class diagram.


11.7.2. Declaring Interface IPayable

The declaration of interface IPayable begins in Fig. 11.11 at line 3. Interface IPayable contains Public methods GetPaymentAmount (line 4) and ToString (line 5). Note that the methods are not explicitly declared Public. Interface methods are implicitly Public. Also note that we explicitly declare the ToString method. Unlike a class, an interface does not inherit from Object, so it does not implicitly have a ToString method. You must explicitly declare ToString in the interface if a program must call this method through an IPayable variable. Interfaces can have any number of methods. Although methods GetPaymentAmount and ToString have no parameters, interface methods can have parameters.

Figure 11.11. IPayable interface declaration.

1  ' Fig. 11.11: IPayable.vb 2  ' IPayable interface declaration. 3  Public Interface IPayable                                     4     Function GetPaymentAmount() As Decimal ' calculate payment 5     Function ToString() As String ' display Payable object     6  End Interface ' IPayable                                     

11.7.3. Creating Class Invoice

We now create class Invoice (Fig. 11.12) to represent a simple invoice that contains billing information for only one kind of part. The class declares Private instance variables partNumberValue, partDescriptionValue, quantityValue and pricePerItemValue (in lines 69) that indicate the part number, a description of the part, the quantity of the part ordered and the price per item, respectively. Class Invoice also contains a constructor (lines 1218), and properties PartNumber (lines 2129), PartDescription(lines 3240), Quantity (lines 4355) and PricePerItem (lines 5870) that manipulate the class's instance variables. Note that the Set accessors of properties Quantity and PricePerItem ensure that quantityValue and pricePerItemValue are assigned only non-negative values.

Figure 11.12. Invoice class that implements interface IPayable.

 1  ' Fig. 11.12: Invoice.vb  2  ' Invoice class implements IPayable.  3  Public Class Invoice    4     Implements IPayable  5  6     Private partNumberValue As String  7     Private partDescriptionValue As String  8     Private quantityValue As Integer  9     Private pricePerItemValue As Decimal 10 11     ' four-argument constructor 12     Public Sub New(ByVal part As String, ByVal description As String, _ 13        ByVal count As Integer, ByVal price As Decimal) 14        PartNumber = part 15        PartDescription = description 16        Quantity = count ' validate quantity 17        PricePerItem = price ' validate price per item 18     End Sub ' New 19 20     ' property PartNumber 21     Public Property PartNumber() As String 22        Get 23           Return partNumberValue 24        End Get 25 26        Set(ByVal part As String) 27           partNumberValue = part 28        End Set 29     End Property ' PartNumber 30 31     ' property PartDescription 32     Public Property PartDescription() As String 33        Get 34           Return partDescriptionValue 35        End Get 36 37        Set(ByVal description As String) 38           partDescriptionValue = description 39        End Set 40     End Property ' PartDescription 41 42     ' property Quantity 43     Public Property Quantity() As Integer 44        Get 45           Return quantityValue 46        End Get 47 48        Set(ByVal count As Integer) 49           If count < 0 Then ' validate quantity 50              quantityValue = 0 51           Else 52              quantityValue = count 53           End If 54        End Set 55     End Property ' Quantity 56 57     ' property PricePerItem 58     Public Property PricePerItem() As Decimal 59        Get 60           Return pricePerItemValue 61        End Get 62 63        Set(ByVal price As Decimal) 64           If price < 0 Then ' validate price 65              pricePerItemValue = 0 66           Else 67              pricePerItemValue = price 68           End If 69        End Set 70     End Property ' PricePerItem 71 72     ' function required to carry out contract with interface IPayable 73     Public Function GetPaymentAmount() As Decimal _                   74        Implements IPayable.GetPaymentAmount                           75        Return Quantity * PricePerItem ' calculate total cost          76     End Function ' GetPaymentAmount                                   77 78     ' return String representation of Invoice object                  79     Public Overrides Function ToString() As String _                  80        Implements IPayable.ToString                                   81        Return ("invoice:" & vbCrLf & "part number: " & PartNumber & _ 82           "(" & PartDescription & ")" & vbCrLf & "quantity: " & _     83           Quantity & vbCrLf & _                                       84           String.Format("price per item: {0:C}", PricePerItem))       85     End Function ' ToString                                           86  End Class ' Invoice

Line 4 of Fig. 11.12 indicates that class Invoice implements interface IPayable. Class Invoice also implicitly inherits from Object. A derived class cannot inherit from more than one base class, but can inherit from a base class and implement zero or more interfaces. To implement more than one interface, use a comma-separated list of interface names after keyword Implements in the class declaration, as in:

Public Class ClassName    Inherits BaseClassName    Implements IFirstInterface, ISecondInterface, ...


Inherits BaseClassName is optional if the class implicitly inherits from class Object. All objects of a class that implements multiple interfaces have the is-a relationship with each implemented interface type.

Class Invoice implements the methods in interface IPayable. Method GetPaymentAmount is declared in lines 7376. Line 74 contains keyword Implements followed by IPayable.GetPaymentAmount to indicate that the method in lines 7376 is the implementation of the interface's GetPaymentAmount method. Line 75 calculates the total payment required to pay the invoice by multiplying the values of quantityValue and pricePerItemValue (obtained through the appropriate properties) and returning the result.

Method ToString (lines 7985) returns the String representation of an Invoice object. Recall that all classes directly or indirectly inherit from class Object. Therefore, class Invoice implicitly inherits Object's ToString method. However, we want to declare a customized ToString method in Invoice that returns a String containing the values of an Invoice's instance variables. Line 79 indicates that Invoice's ToString method overrides the one defined in base class Object. We also must satisfy the requirements of the IPayable interface, which Invoice implements. Line 80 contains the keyword Implements followed by IPayable.ToString to indicate that this version of ToString also serves as the required implementation of IPayable's ToString method. Note that it is possible for the same method to both override a method of a base class and implement a method of an interface. Implementing methods GetPaymentAmount and ToString in class Invoice satisfies the implementation requirement for the methods of interface IPayableconcrete class Invoice fulfills the interface contract with the compiler.

11.7.4. Modifying Class Employee to Implement Interface IPayable

We now modify class Employee to implement interface IPayable. Figure 11.13 contains the modified Employee class. This class declaration is identical to that of Fig. 11.4 with only three exceptions. First, line 4 of Fig. 11.13 indicates that class Employee now implements interface IPayable. Second, we must implement method ToString (lines 5256) declared in the IPayable interface, which also overrides the version declared in base class Object. Third, since Employee now implements interface IPayable, we rename CalculateEarnings to GetPaymentAmount throughout the Employee hierarchy. As with method CalculateEarnings in the Employee class of Fig. 11.4, however, it does not make sense to implement method GetPaymentAmount in abstract class Employee because we cannot calculate the earnings payment owed to a general Employeefirst we must know the specific type of Employee. In Fig. 11.4, we declared method CalculateEarnings as MustOverride for this reason, so class Employee had to be declared MustInherit. This forced each concrete Employee derived class to override CalculateEarnings with an implementation.

Figure 11.13. Employee class that implements interface IPayable.

 1  ' Fig. 11.13: Employee.vb  2  ' Employee abstract base class implements IPayable.  3  Public MustInherit Class Employee  4     Implements IPayable             5  6     Private firstNameValue As String  7     Private lastNameValue As String  8     Private socialSecurityNumberValue As String  9 10     ' three-argument constructor 11     Public Sub New(ByVal first As String, ByVal last As String, _ 12        ByVal ssn As String) 13        FirstName = first 14        LastName = last 15        SocialSecurityNumber = ssn 16     End Sub ' New 17 18     ' property FirstName 19     Public Property FirstName() As String 20        Get 21           Return firstNameValue 22        End Get 23 24        Set(ByVal first As String) 25           firstNameValue = first 26        End Set 27     End Property ' FirstName 28 29     ' property LastName 30     Public Property LastName() As String 31        Get 32           Return lastNameValue 33        End Get 34 35        Set(ByVal last As String) 36           lastNameValue = last 37        End Set 38     End Property ' LastName 39 40     ' property SocialSecurityNumber 41     Public Property SocialSecurityNumber() As String 42        Get 43           Return socialSecurityNumberValue 44        End Get 45 46        Set(ByVal ssn As String) 47           socialSecurityNumberValue = ssn 48        End Set 49     End Property ' SocialSecurityNumber 50 51     ' return String representation of Employee object                     52     Public Overrides Function ToString() As String _                      53        Implements IPayable.ToString                                       54        Return (String.Format("{0} {1}", FirstName, LastName) & vbCrLf & _ 55            "social security number: " & SocialSecurityNumber)             56     End Function ' ToString                                               57 58     ' Note: We do not implement IPayable function GetPaymentAmount here 59     Public MustOverride Function GetPaymentAmount() As Decimal _        60       Implements IPayable.GetPaymentAmount                             61     End Class ' Employee                                               

In Fig. 11.13, we handle this situation similarly. Recall that when a class implements an interface, the class makes a contract with the compiler stating either that the class will implement each of the methods in the interface or that the class will be declared MustInherit. If you choose the latter option, you must declare the interface methods as Must-Override in the abstract class. Any concrete derived class of the abstract class must implement the interface methods to fulfill the base class's contract with the compiler. If the derived class does not do so, it too must be declared MustInherit. As indicated in lines 5960, class Employee of Fig. 11.13 does not implement method GetPaymentAmount, so the class is declared MustInherit (line 3). Each direct Employee derived class inherits the base class's contract to implement method GetPaymentAmount and thus must implement this method to become a concrete class from which objects can be instantiated. A class that inherits from one of Employee's concrete derived classes will inherit an implementation of GetPaymentAmount and thus will also be a concrete class.

11.7.5. Modifying Class SalariedEmployee for Use in the IPayable Hierarchy

Figure 11.14 contains a modified version of concrete class SalariedEmployee that inherits abstract class Employee and fulfills base class Employee's contract to implement interface IPayable's GetPaymentAmount method. This SalariedEmployee class is identical to that of Fig. 11.5, but this version implements method GetPaymentAmount (lines 3234) instead of method CalculateEarnings. These methods contain the same functionality but have different names. Recall that IPayable's GetPaymentAmount method has a more general name to be applicable to possibly disparate classes. The remaining Employee derived classes (e.g., HourlyEmployee, CommissionEmployee and BasePlusCommissionEmployee) also must be modified to contain GetPaymentAmount methods in place of CalculateEarnings methods to reflect the fact that Employee now implements IPayable. We use only SalariedEmployee in our test program in this section.

Figure 11.14. SalariedEmployee class that implements interface IPayable methods GetPaymentAmount and ToString.

 1  ' Fig. 11.14: SalariedEmployee.vb  2  ' SalariedEmployee class inherits Employee, which implements IPayable.  3  Public Class SalariedEmployee  4     Inherits Employee  5  6     Private weeklySalaryValue As Decimal  7  8     ' four-argument constructor  9     Public Sub New(ByVal first As String, ByVal last As String, _ 10        ByVal ssn As String, ByVal salary As Decimal) 11        MyBase.New(first, last, ssn) ' pass to Employee constructor 12        WeeklySalary = salary ' validate and store salary 13     End Sub ' New 14 15     ' property WeeklySalary 16     Public Property WeeklySalary() As Decimal 17        Get 18           Return weeklySalaryValue 19        End Get 20 21        Set(ByVal salary As Decimal) 22           If salary < 0 Then ' validate salary 23              weeklySalaryValue = 0 24           Else 25              weeklySalaryValue = salary 26           End If 27        End Set 28     End Property ' WeeklySalary 29 30     ' calculate earnings; implements interface IPayable method      31     ' GetPaymentAmount that was MustOverride in base class Employee 32     Public Overrides Function GetPaymentAmount() As Decimal         33        Return WeeklySalary                                          34     End Function ' GetPaymentAmount                                 35 36     ' return String representation of SalariedEmployee object         37     Public Overrides Function ToString() As String                    38        Return ("salaried employee: " & MyBase.ToString() & vbCrLf & _ 39           String.Format("weekly salary {0:C}", WeeklySalary))         40     End Function ' ToString                                           41  End Class ' SalariedEmployee

When a class implements an interface, the same is-a relationship as inheritance applies. For example, class Employee implements IPayable, so we can say that an Employee is an IPayable. Objects of any classes that inherit from Employee are also IPayable objects. SalariedEmployee objects, for instance, are IPayable objects. As with inheritance relationships, an object of a class that implements an interface may be thought of as an object of the interface type. Objects of any derived classes of the class that implements the interface can also be thought of as objects of the interface type. Thus, just as we can assign the reference of a SalariedEmployee object to a base class Employee variable, we can assign the reference of a SalariedEmployee object to an interface IPayable variable. Invoice implements IPayable, so an Invoice object also is an IPayable object, and we can assign the reference of an Invoice object to an IPayable variable.

Software Engineering Observation 11.3

Inheritance and interfaces are similar in their implementation of the is-a relationship. An object of a class that implements an interface may be thought of as an object of that interface type. Objects of any derived classes of a class that implements an interface can also be thought of as objects of the interface type.


Software Engineering Observation 11.4

The is-a relationship that exists between derived classes and base classes, and between classes that implement interfaces and the interfaces themselves, holds when passing an object to a method. If a parameter has a base class type, it can receive either a base class or derived class reference as an argument. If a parameter has an interface type, it can receive a reference to an object of any class that implements the interface.


11.7.6. Using Interface IPayable to Process Invoices and Employees Polymorphically

Module PayableInterfaceTest (Fig. 11.15) illustrates that interface IPayable can be used to process a set of Invoices and Employees polymorphically in a single application, even though these classes represent such different kinds of things.

Figure 11.15. IPayable interface test program processing Invoices and Employees polymorphically.

 1  ' Fig. 11.15: PayableInterfaceTest.vb  2  ' Testing interface IPayable.  3  Module PayableInterfaceTest  4     Sub Main()  5        ' create four-element IPayable array                   6        Dim payableObjects() As IPayable = New IPayable(3) {}  7  8        ' populate array with objects that implement IPayable  9        payableObjects(0) = New Invoice("01234", "seat", 2, 375) 10        payableObjects(1) = New SalariedEmployee( _ 11           "John", "Smith", "111-11-1111", 800) 12        payableObjects(2) = New Invoice("56789", "tire", 4, 79.95D) 13        payableObjects(3) = New SalariedEmployee( _ 14           "Lisa", "Barnes", "888-88-8888", 1200) 15 16        Console.WriteLine( _ 17           "Invoices and Employees processed polymorphically:" & vbCrLf) 18 19        ' generically process each element in array payableObjects 20        For Each currentPayable As IPayable In payableObjects 21           ' output currentPayable and its appropriate payment amount 22           Console.WriteLine(currentPayable.ToString() & vbCrLf & _ 23              String.Format("payment due: {0:C}", _ 24              currentPayable.GetPaymentAmount()) & vbCrLf) 25        Next 26     End Sub ' Main 27  End Module ' PayableInterfaceTest

Invoices and Employees processed polymorphically: invoice: part number: 01234(seat) quantity: 2 price per item: $375.00 payment due: $750.00 salaried employee: John Smith social security number: 111-11-1111 weekly salary $800.00 payment due: $800.00 invoice: part number: 56789(tire) quantity: 4 price per item: $79.95 payment due: $319.80 salaried employee: Lisa Barnes social security number: 888-88-8888 weekly salary $1,200.00 payment due: $1,200.00



Line 6 declares payableObjects and assigns to it an array of four IPayable variables. Lines 9 and 12 assign the references of Invoice objects to payableObjects(0) and payableObjects(2), respectively. Lines 1011 and 1314 assign the references of SalariedEmployee objects to payableObjects(1) and payableObjects(3), respectively. These assignments are allowed because an Invoice is an IPayable, and a SalariedEmployee is an Employee and an Employee is an IPayable. Lines 2025 use a For Each...Next statement to polymorphically process each IPayable object in payableObjects, displaying the object as a String, along with the payment amount due. Line 22 invokes method ToString via an IPayable variable to get the String representation of each object. Line 24 invokes IPayable method GetPaymentAmount to obtain the payment amount for each object in payableObjects, regardless of the actual type of the object. The output reveals that the method calls in lines 2224 invoke the appropriate class's implementation of methods ToString and GetPaymentAmount. For instance, when currentEmployee refers to an Invoice during the first iteration of the For Each loop, class Invoice's ToString and GetPaymentAmount methods execute.

Software Engineering Observation 11.5

Using a base class reference, you can polymorphically invoke any method specified in the base class declaration (and in class Object). Using an interface reference, you can polymorphically invoke any method specified in the interface declaration.


11.7.7. Common Interfaces of the .NET Framework Class Library (FCL)

In this section, we overview several common interfaces found in the .NET class library. These interfaces are implemented and used in the same manner as the interfaces you create. Figure 11.16 overviews a few of the more popular interfaces of the .NET class library that we use in this book.

Figure 11.16. Common interfaces of the .NET Class Library.

Interface

Description

IComparable

As you learned in Chapter 3, Visual Basic contains several comparison operators (e.g., <, <=, >, >=, =, <>) that allow you to compare primitive values. However, these operators cannot be used to compare objects. Interface IComparable is used to allow objects of a class that implements the interface to be compared to one another. The interface contains one method, CompareTo, that compares the object that calls the method to the object passed as an argument to the method. Classes must implement CompareTo such that it returns a value indicating whether the object on which it is invoked is less than (negative integer return value), equal to (0 return value) or greater than (positive integer return value) the object passed as an argument, using any criteria specified by the programmer. For example, if class Employee implements IComparable, its CompareTo method could compare Employee objects by their earnings amounts. Interface IComparable is commonly used for ordering objects in a collection such as an array. We use IComparable in Chapter 25, Generics and Chapter 26, Collections.

IComponent

Implemented by any class that represents a component, including Graphical User Interface (GUI) controls (such as buttons or labels). Interface IComponent defines the behaviors that components must implement. We discuss IComponent and many GUI controls that implement this interface in Chapter 13, Graphical User Interface Concepts: Part 1 and Chapter 14, Graphical User Interface Concepts: Part 2.

IEnumerator

Used for iterating through the elements of a collection (such as an array) one element at a time. Interface IEnumerator contains method MoveNext to move to the next element in a collection, method Reset to move to the position before the first element, and property Current to return the object at the current location. We use IEnumerator in Chapter 26, Collections.




Visual BasicR 2005 for Programmers. DeitelR Developer Series
Visual Basic 2005 for Programmers (2nd Edition)
ISBN: 013225140X
EAN: 2147483647
Year: 2004
Pages: 435

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