Case Study: Creating and Using Interfaces

Case Study Creating and Using Interfaces

Our next example (Figs. 11.1111.15) reexamines the payroll system of Section 11.5. Suppose that the company involved wishes to perform several accounting operations in a single accounts-payable applicationin addition to calculating the payroll earnings that must be paid to each employee, the company must also calculate the payment due on each of several invoices (i.e., bills for goods purchased). Though applied to unrelated things (i.e., employees and invoices), both operations have to do with calculating 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 listed on the invoice. Can we calculate such different things as the payments due for employees and invoices polymorphically in a single application? Does C# offer a capability that requires that unrelated classes implement a set of common methods (e.g., a method that calculates a payment amount)? C# interfaces offer exactly this capability.

Interfaces define and standardize the ways in which people and systems can interact with one another. For example, the controls on a radio serve as an interface between a radio's users and its internal components. The controls allow users to perform 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 must permit users to perform but does not specify how the operations are performed. Similarly, the interface between a driver and a car with a manual transmission includes the steering wheel, the gear shift, the clutch pedal, the gas pedal and the brake pedal. 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 general purpose is the sameto allow people to drive the car.

Software objects also communicate via interfaces. A C# 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. The next example introduces an interface named IPayable that describes 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 only abstract methods, properties, indexers and events (events are discussed in Chapter 13, Graphical User Interface Concepts: Part 1.) All interface members are implicitly declared both public and abstract. In addition, each interface can extend one or more other interfaces to create a more elaborate interface that other classes can implement.

Common Programming Error 11 6

It is a compilation error to declare an interface member public or abstract explicitly, because they are redundant in interface member declarations. It is also a compilation error to specify any implementation details, such as concrete method declarations, in an interface.

To use an interface, a class must specify that it implements the interface by listing the interface after the colon (:) in the class declaration. Note that this is the same syntax used to indicate inheritance from a base class. A concrete class implementing the interface must declare each member of the interface with the signature specified in the interface declaration. A class that implements an interface but does not implement all the interface's members is an abstract classit must be declared abstract and must contain an abstract declaration for each unimplemented member of the interface. Implementing an interface is like signing a contract with the compiler that states, "I will provide an implementation for all the members specified by the interface, or I will declare them abstract."

Common Programming Error 11 7

Failing to declare any member of an interface in a class that implements the interface results in a compilation error.

An interface is typically used when disparate (i.e., unrelated) classes need to share common methods. This allows objects of unrelated classes to be processed polymorphicallyobjects of classes that implement the same interface can respond to the same method calls. Programmers can create an interface that describes the desired functionality, then implement this interface in any classes requiring that functionality. For example, in the accounts-payable application developed in this section, we implement interface IPayable in any class that must be able to calculate a payment amount (e.g., Employee, Invoice).

An interface often is used in place of an abstract class when there is no default implementation to inheritthat is, no fields and no default method implementations. Like public abstract classes, interfaces are typically public types, so they are normally declared in files by themselves with the same name as the interface and the .cs filename extension.

11.7.1. Developing an IPayable Hierarchy

To build an application that can determine payments for employees and invoices alike, we first create an interface named IPayable. Interface IPayable contains method GetPaymentAmount that returns a decimal amount that must be paid for an object of any class that implements the interface. Method GetPaymentAmount is a general purpose version of method Earnings of the Employee hierarchymethod Earnings calculates a payment amount specifically for an Employee, while GetPaymentAmount can be applied to a broad range of unrelated objects. After declaring interface IPayable, we introduce class Invoice, which implements interface IPayable. We then modify class Employee such that it also implements interface IPayable. Finally, we update Employee derived class SalariedEmployee to "fit" into the IPayable hierarchy (i.e., rename SalariedEmployee method Earnings as GetPaymentAmount).

Good Programming Practice 11 1

By convention, the name of an interface begins with "I". This helps distinguish interfaces from classes, improving code readability.

Good Programming Practice 11 2

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 a broad range of 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 an application can invoke method GetPaymentAmount on Invoice objects and Employee objects alike. This enables the polymorphic processing of Invoices and Employees required for our company's accounts-payable application.

The UML class diagram in Fig. 11.10 shows the interface and class hierarchy used in our accounts-payable application. The hierarchy begins with interface IPayable. The UML distinguishes an interface from a class by placing the word "interface" in guillemets (« and ») above the interface name. The UML expresses the relationship between a class and an interface through 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 class to the interface. The diagram in Fig. 11.10 indicates that classes Invoice and Employee each realize (i.e., implement) interface IPayable. Note that as in the class diagram of Fig. 11.2, class Employee appears in italics, indicating that it is an abstract class. Concrete class SalariedEmployee extends Employee and inherits its base class's realization relationship with interface IPayable.

Figure 11.10. IPayable interface and class hierarchy UML class diagram.

(This item is displayed on page 536 in the print version)

 

11.7.2. Declaring Interface IPayable

The declaration of interface IPayable begins in Fig. 11.11 at line 3. Interface IPayable contains publicabstract method GetPaymentAmount (line 5). Note that the method cannot be explicitly declared public or abstract. Interface IPayable has only one method, but interfaces can have any number of members. In addition, method GetPaymentAmount has no parameters, but interface methods can have parameters.

Figure 11.11. IPayable interface declaration.

1 // Fig. 11.11: IPayable.cs
2 // IPayable interface declaration.
3 public interface IPayable
4 {
5 decimal GetPaymentAmount(); // calculate payment; no implementation
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 one kind of part. The class declares private instance variables partNumber, partDescription, quantity and pricePerItem (lines 58) that indicate the part number, the description of the part, the quantity of the part ordered and the price per item. Class Invoice also contains a constructor (lines 1118), properties (lines 2170) that manipulate the class's instance variables and a ToString method (lines 7379) that returns a string representation of an Invoice object. Note that the set accessors of properties Quantity (lines 5356) and PricePerItem (lines 6669) ensure that quantity and pricePerItem are assigned only non-negative values.

Figure 11.12. Invoice class implements IPayable.

(This item is displayed on pages 537 - 538 in the print version)

 1 // Fig. 11.12: Invoice.cs
 2 // Invoice class implements IPayable.
 3 public class Invoice : IPayable
 4 {
 5 private string partNumber;
 6 private string partDescription;
 7 private int quantity;
 8 private decimal pricePerItem;
 9
10 // four-parameter constructor
11 public Invoice( string part, string description, int count,
12 decimal price )
13 {
14 PartNumber = part;
15 PartDescription = description;
16 Quantity = count; // validate quantity via property
17 PricePerItem = price; // validate price per item via property
18 } // end four-parameter Invoice constructor
19
20 // property that gets and sets the part number on the invoice
21 public string PartNumber
22 {
23 get
24 {
25 return partNumber;
26 } // end get
27 set
28 {
29 partNumber = value; // should validate
30 } // end set
31 } // end property PartNumber
32
33 // property that gets and sets the part description on the invoice
34 public string PartDescription
35 {
36 get
37 {
38 return partDescription;
39 } // end get
40 set
41 {
42 partDescription = value; // should validate
43 } // end set
44 } // end property PartDescription
45
46 // property that gets and sets the quantity on the invoice 47 public int Quantity 48 { 49 get 50 { 51 return quantity; 52 } // end get 53 set 54 { 55 quantity = ( value < 0 ) ? 0 : value; // validate quantity 56 } // end set 57 } // end property Quantity 58 59 // property that gets and sets the price per item 60 public decimal PricePerItem 61 { 62 get 63 { 64 return pricePerItem; 65 } // end get 66 set 67 { 68 pricePerItem = ( value < 0 ) ? 0 : value; // validate price 69 } // end set 70 } // end property PricePerItem 71 72 // return string representation of Invoice object 73 public override string ToString() 74 { 75 return string.Format( 76 "{0}: {1}: {2} ({3}) {4}: {5} {6}: {7:C}", 77 "invoice", "part number", PartNumber, PartDescription, 78 "quantity", Quantity, "price per item", PricePerItem ); 79 } // end method ToString 80 81 // method required to carry out contract with interface IPayable 82 public decimal GetPaymentAmount() 83 { 84 return Quantity * PricePerItem; // calculate total cost 85 } // end method GetPaymentAmount 86 } // end class Invoice

Line 3 of Fig. 11.12 indicates that class Invoice implements interface IPayable. Like all classes, class Invoice also implicitly extends object. C# does not allow derived classes to inherit from more than one base class, but it does allow a class to inherit from a base class and implement any number of interfaces. All objects of a class that implement multiple interfaces have the is-a relationship with each implemented interface type. To implement more than one interface, use a comma-separated list of interface names after the colon (:) in the class declaration, as in:

public class ClassName : BaseClassName, FirstInterface, SecondInterface, ...

When a class inherits from a base class and implements one or more interfaces, the class declaration must list the base class name before any interface names.

Class Invoice implements the one method in interface IPayablemethod GetPaymentAmount is declared in lines 8285. The method calculates the amount required to pay the invoice. The method multiplies the values of quantity and pricePerItem (obtained through the appropriate properties) and returns the result (line 84). This method satisfies the implementation requirement for the method in interface IPayablewe have fulfilled 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 two exceptions. First, line 3 of Fig. 11.13 indicates that class Employee now implements interface IPayable. Second, since Employee now implements interface IPayable, we must rename Earnings to GetPaymentAmount throughout the Employee hierarchy. As with method Earnings in the version of class Employee in Fig. 11.4, however, it does not make sense to implement method GetPaymentAmount in 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 Earnings as abstract for this reason, and as a result, class Employee had to be declared abstract. This forced each Employee derived class to override Earnings with a concrete implementation.

Figure 11.13. Employee abstract base class.

 1 // Fig. 11.13: Employee.cs
 2 // Employee abstract base class.
 3 public abstract class Employee : IPayable
 4 {
 5 private string firstName;
 6 private string lastName;
 7 private string socialSecurityNumber;
 8
 9 // three-parameter constructor
10 public Employee( string first, string last, string ssn )
11 {
12 firstName = first;
13 lastName = last;
14 socialSecurityNumber = ssn;
15 } // end three-parameter Employee constructor
16
17 // read-only property that gets employee's first name
18 public string FirstName
19 {
20 get
21 {
22 return firstName;
23 } // end get
24 } // end property FirstName
25
26 // read-only property that gets employee's last name
27 public string LastName
28 {
29 get
30 {
31 return lastName;
32 } // end get
33 } // end property LastName
34
35 // read-only property that gets employee's social security number
36 public string SocialSecurityNumber
37 {
38 get
39 {
40 return socialSecurityNumber;
41 } // end get
42 } // end property SocialSecurityNumber
43 44 // return string representation of Employee object 45 public override string ToString() 46 { 47 return string.Format( "{0} {1} social security number: {2}", 48 FirstName, LastName, SocialSecurityNumber ); 49 } // end method ToString 50 51 // Note: We do not implement IPayable method GetPaymentAmount here so 52 // this class must be declared abstract to avoid a compilation error. 53 public abstract decimal GetPaymentAmount(); 54 } // end abstract class Employee

In Fig. 11.13, we handle this situation the same way. Recall that when a class implements an interface, the class makes a contract with the compiler stating that the class either will implement each of the methods in the interface or will declare them abstract. If the latter option is chosen, we must also declare the class abstract. As we discussed in Section 11.4, any concrete derived class of the abstract class must implement the abstract methods of the base class. If the derived class does not do so, it too must be declared abstract. As indicated by the comments in lines 5152, class Employee of Fig. 11.13 does not implement method GetPaymentAmount, so the class is declared abstract.

11.7.5. Modifying Class SalariedEmployee for Use in the IPayable Hierarchy

Figure 11.14 contains a modified version of class SalariedEmployee that extends Employee and implements method GetPaymentAmount. This version of SalariedEmployee is identical to that of Fig. 11.5 with the exception that the version here implements method GetPaymentAmount (lines 2932) instead of method Earnings. The two methods contain the same functionality but have different names. Recall that the IPayable version of the 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 method GetPaymentAmount in place of Earnings to reflect the fact that Employee now implements IPayable. We leave these modifications as an exercise and use only SalariedEmployee in our test application in this section.

Figure 11.14. SalariedEmployee class that extends Employee.

(This item is displayed on pages 540 - 541 in the print version)

 1 // Fig. 11.14: SalariedEmployee.cs
 2 // SalariedEmployee class that extends Employee.
 3 public class SalariedEmployee : Employee
 4 {
 5 private decimal weeklySalary;
 6
 7 // four-parameter constructor
 8 public SalariedEmployee( string first, string last, string ssn,
 9 decimal salary ) : base( first, last, ssn )
10 {
11 WeeklySalary = salary; // validate salary via property
12 } // end four-parameter SalariedEmployee constructor
13
14 // property that gets and sets salaried employee's salary
15 public decimal WeeklySalary
16 {
17 get
18 {
19 return weeklySalary;
20 } // end get
21 set
22 {
23 weeklySalary = value < 0 ? 0 : value; // validation
24 } // end set
25 } // end property WeeklySalary
26 27 // calculate earnings; implement interface IPayable method 28 // that was abstract in base class Employee 29 public override decimal GetPaymentAmount() 30 { 31 return WeeklySalary; 32 } // end method GetPaymentAmount 33 34 // return string representation of SalariedEmployee object 35 public override string ToString() 36 { 37 return string.Format( "salaried employee: {0} {1}: {2:C}", 38 base.ToString(), "weekly salary", WeeklySalary ); 39 } // end method ToString 40 } // end class SalariedEmployee

When a class implements an interface, the same is-a relationship provided by inheritance applies. For example, class Employee implements IPayable, so we can say that an Employee is an IPayable, as are any classes that extend Employee. 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 classes derived from 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 6

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. An object of any derived classes of a class that implements an interface also can be thought of as an object of the interface type.

Software Engineering Observation 11 7

The is-a relationship that exists between base classes and derived classes, and between interfaces and the classes that implement them, holds when passing an object to a method. When a method parameter receives a variable of a base class or interface type, the method polymorphically processes the object received as an argument.

11.7.6. Using Interface IPayable to Process Invoices and Employees Polymorphically

PayableInterfaceTest (Fig. 11.15) illustrates that interface IPayable can be used to process a set of Invoices and Employees polymorphically in a single application. Line 10 declares payableObjects and assigns it an array of four IPayable variables. Lines 1314 assign the references of Invoice objects to the first two elements of payableObjects. Lines 1518 assign the references of SalariedEmployee objects to the remaining two elements of payableObjects. These assignments are allowed because an Invoice is an IPayable, a SalariedEmployee is an Employee and an Employee is an IPayable. Lines 2429 use a foreach statement to process each IPayable object in payableObjects polymorphically, printing the object as a string, along with the payment due. Note that line 27 implicitly invokes method ToString off an IPayable interface reference, even though ToString is not declared in interface IPayableall references (including those of interface types) refer to objects that extend object and therefore have a ToString method. Line 28 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 2728 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 foreach loop, class Invoice's ToString and GetPaymentAmount methods execute.

Software Engineering Observation 11 8

All methods of class object can be called by using a reference of an interface typethe reference refers to an object, and all objects inherit the methods of class object.

 

Figure 11.15. Tests interface IPayable with disparate classes.

 1 // Fig. 11.15: PayableInterfaceTest.cs
 2 // Tests interface IPayable with disparate classes.
 3 using System;
 4
 5 public class PayableInterfaceTest
 6 {
 7 public static void Main( string[] args )
 8 {
 9 // create four-element IPayable array
 10 IPayable[] payableObjects = new IPayable[ 4 ];
11
12 // populate array with objects that implement IPayable
13 payableObjects[ 0 ] = new Invoice( "01234", "seat", 2, 375.00M );
14 payableObjects[ 1 ] = new Invoice( "56789", "tire", 4, 79.95M );
15 payableObjects[ 2 ] = new SalariedEmployee( "John", "Smith",
16 "111-11-1111", 800.00M );
17 payableObjects[ 3 ] = new SalariedEmployee( "Lisa", "Barnes",
18 "888-88-8888", 1200.00M );
19
20 Console.WriteLine(
21 "Invoices and Employees processed polymorphically:
" );
22
23 // generically process each element in array payableObjects 24 foreach ( IPayable currentPayable in payableObjects ) 25 { 26 // output currentPayable and its appropriate payment amount 27 Console.WriteLine( "{0} {1}: {2:C} ", currentPayable, 28 "payment due", currentPayable.GetPaymentAmount() ); 29 } // end foreach 30 } // end Main 31 } // end class PayableInterfaceTest
Invoices and Employees processed polymorphically:

invoice:
part number: 01234 (seat)
quantity: 2
price per item: $375.00
payment due: $750.00

invoice:
part number: 56789 (tire)
quantity: 4
price per item: $79.95
payment due: $319.80

salaried employee: John Smith
social security number: 111-11-1111
weekly salary: $800.00
payment due: $800.00

salaried employee: Lisa Barnes
social security number: 888-88-8888
weekly salary: $1,200.00
payment due: $1,200.00

11.7.7. Common Interfaces of the .NET Framework Class Library

In this section, we overview several common interfaces in the .NET Framework Class Library. These interfaces are implemented and used in the same manner as those you create (e.g., interface IPayable in Section 11.7.2). The FCL's interfaces enable you to extend many important aspects of C# with your own classes. Figure 11.16 overviews several commonly used FCL interfaces.

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

(This item is displayed on page 544 in the print version)

Interface

Description

IComparable

As you learned in Chapter 3, C# contains several comparison operators (e.g., <, <=, >, >=, ==, !=) that allow you to compare simple-type values. In Section 11.8 you will see that these operators can be defined to compare two objects. Interface IComparable can also be 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 to return 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 26, Generics, and Chapter 27, 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.

IDisposable

Implemented by classes that must provide an explicit mechanism for releasing resources. Some resources can be used by only one program at a time. In addition, some resources, such as files on disk, are unmanaged resources that, unlike memory, cannot be released by the garbage collector. Classes that implement interface IDisposable provide a Dispose method that can be called to explicitly release resources. We discuss IDisposable briefly in Chapter 12, Exception Handling. You can learn more about this interface at msdn2.microsoft.com/en-us/library/aax125c9. The MSDN article Implementing a Dispose Method at msdn2.microsoft.com/en-us/library/fs2xkftw discusses the proper implementation of this interface in your classes.

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 27, Collections.


Preface

Index

    Introduction to Computers, the Internet and Visual C#

    Introduction to the Visual C# 2005 Express Edition IDE

    Introduction to C# Applications

    Introduction to Classes and Objects

    Control Statements: Part 1

    Control Statements: Part 2

    Methods: A Deeper Look

    Arrays

    Classes and Objects: A Deeper Look

    Object-Oriented Programming: Inheritance

    Polymorphism, Interfaces & Operator Overloading

    Exception Handling

    Graphical User Interface Concepts: Part 1

    Graphical User Interface Concepts: Part 2

    Multithreading

    Strings, Characters and Regular Expressions

    Graphics and Multimedia

    Files and Streams

    Extensible Markup Language (XML)

    Database, SQL and ADO.NET

    ASP.NET 2.0, Web Forms and Web Controls

    Web Services

    Networking: Streams-Based Sockets and Datagrams

    Searching and Sorting

    Data Structures

    Generics

    Collections

    Appendix A. Operator Precedence Chart

    Appendix B. Number Systems

    Appendix C. Using the Visual Studio 2005 Debugger

    Appendix D. ASCII Character Set

    Appendix E. Unicode®

    Appendix F. Introduction to XHTML: Part 1

    Appendix G. Introduction to XHTML: Part 2

    Appendix H. HTML/XHTML Special Characters

    Appendix I. HTML/XHTML Colors

    Appendix J. ATM Case Study Code

    Appendix K. UML 2: Additional Diagram Types

    Appendix L. Simple Types

    Index



    Visual C# How to Program
    Visual C# 2005 How to Program (2nd Edition)
    ISBN: 0131525239
    EAN: 2147483647
    Year: 2004
    Pages: 600

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