Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
120
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

POLYMORPHISM 373

Polymorphism

This is another powerful aspect of inheritance. Polymorphism is the ability of a base type to adjust itself to accommodate many different derived types. Let’s make it simpler by using some analogies in the English language. Take the word run, for example. This verb can be used to describe athletes, cars, or refrigerators; they all “run.” In different sentences, the same word takes different meanings. When you use it with a person, it means going a distance at a fast pace. When you use it with a refrigerator, it means that it’s working. When you use it with a car, it may take on both meanings. So, in a sense the word run is polymorphic (and so are many other English words): Its exact meaning is differentiated according to the context.

To apply the same analogy to computers, think of a class that describes a basic object, like a Shape. This class would be very complicated if it had to describe and handle all shapes. It would be incomplete too, because the moment you released it to the world, you’d think of a new shape that can’t be described by your class. To design a class that describes all shapes, you build a very simple class to describe shapes at large, and then you build a separate class for each individual shape: a Triangle class, a Square class, a Circle class, and so on. As you can guess, all these classes inherit the Shape class. Let’s also assume that all the classes that describe individual shapes expose an Area method, which calculates the area of the shape they describe. The name of the Area method is the same for all classes, but it calculates a different formula.

The developer, however, doesn’t have to learn a different syntax of the Area method for each shape. They can declare a Square object and calculate its area with the following statements:

Dim shape1 As New Square, area As Double area = shape1.Area

If shape2 represents a circle, the same method will calculate the circle’s area:

Dim shape2 As New Circle, area As Double area = shape2.Area

You can go through a list of objects derived from the Shape class and calculate their areas by calling the Area method. No need to know what shape each object represents—you just call its Area method. Let’s say you’ve created an ArrayList with various shapes. You can go through the collection and calculate the total area with a loop like the following:

Dim shapeEnum As IEnumerator Dim totalArea As Double shapeEnum = aList.GetEnumerator While shapeEnum.MoveNext

totalArea = totalArea + CType(shapeEnum.Current, Shape).area End While

The CType() function converts the current element of the collection to a Shape object; it’s necessary only if the Strict option is on, which prohibits VB from late-binding the expression (Strict is off by default). As a reminder, when the Strict option is off, trivial mistakes will manifest themselves as runtime exceptions. If you mistype the name of the Area method as Arae, the compiler won’t catch this error at design time. If the Strict option is on, however, the error will be caught as you type.

The Area method is polymorphic. Its exact meaning (or formula, in the case of shapes) is adjusted to the context in which it’s used. OK, this is a simple concept. It’s only natural that we’re

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

374 Chapter 8 BUILDING CUSTOM CLASSES

able to call the Area method to calculate the area of any shape, isn’t it? Believe me, it took us many years to get there. VB.NET is the first version of VB that supports true polymorphism. We’re going to look at the implementation of the classes that describe shapes in a moment, but first I would like to discuss some alternatives and show you the drawbacks, so that you won’t think that building classes to calculate the areas of various shapes is wasted time.

The first alternative would be to build a separate function to calculate the area of each shape (SquareArea, CircleArea, and so on). It will work, but why bother with so many function names, not to mention the overhead in your code? You must first figure out the type of shape described by a specific variable, like shape1, and then call the appropriate method. The code will not be as easy to read, and the longer the application gets, the more If and Case statements you’ll be coding.

The second, even less efficient method is a really long Area() function that would be able to calculate the area of all shapes. This function should be a very long Case statement, like the following one:

Public Function Area(ByVal shapeType As String) As Double Select Case shapeType

Case “Square”: { calculate the area of a square } Case “Circle”: { calculate the area of a circle } { . . . more Case statements }

End Select End Function

The real problem with this approach is that every time you want to add a new segment to calculate the area of a new shape to the function, you’d have to edit it. If another developer wanted to add a shape, they’d be out of luck.

In the following section, we’ll build the Shape class, which we’ll extend with individual classes for various shapes. You’ll be able to add your own classes to implement additional shapes, and any code written using the older versions of the Shape class will keep working.

The Shape Class

Let’s start with the Shape class, which will be the base class for all other shapes. This is a really simple class that’s pretty useless on its own. Its real use is that it exposes some members that can be inherited. The base class exposes two methods, Area and Perimeter. Even the two methods don’t do much—actually, they do absolutely nothing. All they really do is provide a naming convention. All classes that will inherit the Shape class will have an Area and a Perimeter method. They must provide the implementation of these methods, so that all object variables that represent shapes will expose an Area method and a Perimeter method.

Start a new project as usual, add a Shape class, and enter the code of Listing 8.37 in it.

Listing 8.37: The Shape Class

Class Shape

Overridable Function Area() As Double

End Function

Overridable Function Perimeter() As Double

End Function

End Class

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

POLYMORPHISM 375

If there are properties common to all shapes, you place the appropriate Property procedures in the Shape class. If you want to assign a color to your shapes, place a Color property in this class. The Overridable keyword means that a class that inherits from the Shape class can override the default implementation of the corresponding methods or properties. As you will see shortly, it is possible for the base class to provide a few members that can’t be overridden in the derived class.

Then you can implement the classes for the individual shapes. Add another Class to the project, name it Shapes, and enter Listing 8.38’s code in it.

Listing 8.38: The Square, Triangle, and Circle Classes

Public Class Square

Inherits Shape

Private sSide As Double

Public Property Side() As Double Get

Side = sSide End Get

Set

sSide = Value End Set

End Property

Public Overrides Function Area() As Double Area = sSide * sSide

End Function

Public Overrides Function Perimeter() As Double Return (4 * sSide)

End Function End Class

Public Class Triangle

Inherits Shape

Private side1, side2, side3 As Double Property SideA() As Double

Get

SideA = side1 End Get

Set

side1 = Value End Set

End Property

Property SideB() As Double Get

SideB = side2 End Get

Set

side2 = Value End Set

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

376 Chapter 8 BUILDING CUSTOM CLASSES

End Property

Public Property SideC() As Double

Get

SideC = side3

End Get

Set

side3 = Value

End Set

End Property

Public Overrides Function Area() As Double

Dim perim As Double

perim = Perimeter()

Return (Math.Sqrt(perim * (perim - side1) * (perim - side2) * _

(perim - side3)))

End Function

Public Overrides Function Perimeter() As Double

Return (side1 + side2 + side3)

End Function

End Class

Public Class Circle

Inherits Shape

Private cRadius As Double

Public Property Radius() As Double

Get

Radius = cRadius

End Get

Set

cRadius = Value

End Set

End Property

Public Overrides Function Area() As Double

Return (Math.Pi * cRadius ^ 2)

End Function

Public Overrides Function Perimeter() As Double

Return (2 * Math.Pi * cRadius)

End Function

End Class

The Shapes.vb file contains three classes: Square, Triangle, and Circle. All three expose their basic geometric characteristics as properties. The Triangle class, for example, exposes the properties SideA, SideB, and SideC, which allow you to set the three sides of the triangle. In addition, all three classes expose the Area and Perimeter methods. These methods are implemented differently for each class, but they do the same thing: they return the area and the perimeter of the corresponding shape. The Area method of the Triangle class is a bit involved, but it’s just a formula.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

POLYMORPHISM 377

Testing the Shape Class

To test the Shapes class, all you have to do is create three variables—one of each type of shape—and call their methods. Or, you can store all three variables into an array and iterate through them. If the collection contains Shape variables only, the current item is always a shape, and as such it exposes the Area and Perimeter methods. The code in Listing 8.39 does exactly that. First, it declares three variables of the Triangle, Circle, and Square types. Then it sets their properties and calls their Area method to print their areas.

Listing 8.39: Testing the Shape Class

Protected Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs)

Dim shape1 As New Triangle()

Dim shape2 As New Circle()

Dim shape3 As New Square()

Set up a triangle shape1.SideA = 3 shape1.SideB = 3.2 shape1.SideC = 0.94

Console.WriteLine(“The triangle’s area is “ & shape1.Area.ToString)

Set up a circle

shape2.Radius = 4

Console.WriteLine(“The circle’s area is “ & shape2.Area.ToString)

Set up a square shape3.Side = 10.01

Console.WriteLine(“The square’s area is “ & shape3.Area.ToString) Dim shapes() As Shape

shapes(0) = shape1 shapes(1) = shape2 shapes(2) = shape3

Dim shapeEnum As IEnumerator Dim totalArea As Double shapeEnum = shapes.GetEnumerator

While shapeEnum.MoveNext

totalArea = totalArea + CType(shapeEnum.Current, shape).Area End While

Console.WriteLine(“The total area of your shapes is “ & totalArea.ToString) End Sub

In the last section, the test code stores all three variables into an array and iterates through its elements. At each iteration, it casts the current item to the Shape type and calls its Area method. The expression that calculates areas is CType(shapeEnum.Current, shape).Area, and the same expression calculates the area of any shape.

The Shape base class is quite trivial—it doesn’t expose any functionality of its own. Depending on how you will use the individual shapes in your application, you can add properties and methods

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com