The entire source code for this article is available in the form of a downloadable zip file. The solution was developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Enterprise Edition. I didn't really test it in any other environment. I request that you post in the discussion area if you have any problems in execution.
Life without the "Abstract" class in Visual Basic 2005
Before understanding the "Abstract" class, let us go through an example with a set of classes, which demonstrates the inconveniences of repeating the code.
The following is a class named "Rectangle" defined with length and breadth as properties along with a method named "GetArea" to calculate the area of a rectangle:
Public Class Rectangle
Private _l As Double
Private _b As Double
Public Sub New(ByVal length As Double, ByVal breadth As Double)
Me.L = length
Me.B = breadth
End Sub
Public Property L() As Double
Get
Return _l
End Get
Set(ByVal value As Double)
_l = value
End Set
End Property
Public Property B() As Double
Get
Return _b
End Get
Set(ByVal value As Double)
_b = value
End Set
End Property
Public Function GetArea() As Double
Return _l * _b
End Function
End Class
There exists nothing special about the previous class apart from properties, methods and a constructor. Let us consider two more classes, "EquiTriangle" and "Square," which have properties that are similar to those of our "Rectangle" class. This will be continued in the next section.
In the previous section, a "Rectangle" class was defined. In this section, two more classes are introduced as follows:
Public Class EquiTriangle
Private _h As Double
Private _b As Double
Public Sub New(ByVal breadth As Double, ByVal height As Double) 'code removed for clarity
Public Property H() As Double 'code removed for clarity
Public Property B() As Double 'code removed for clarity
Public Function GetArea() As Double
Return (1.0 / 2.0) * _b * _h
End Function
End Class
Public Class Square
Private _s As Double
Public Sub New(ByVal side As Double)
Me.S = side
End Sub
Public Property S() As Double
Get
Return _s
End Get
Set(ByVal value As Double)
_s = value
End Set
End Property
Public Function GetArea() As Double
Return _s * _s
End Function
End Class
If you observe the three classes "Rectangle," "EquiTriangle" and "Square," you will see several similarities. First of all, "GetArea" is available in all the classes (and it should be available for any of the shapes). In both "Rectangle" and "EquiTriangle," there exist two properties with almost the same signature along with constructors.
The code to test the above classes would be the following:
Dim r As New Rectangle(10, 20)
Dim s As New Square(5)
Dim t As New EquiTriangle(5, 20)
MessageBox.Show("Area of rectangle: " & r.GetArea)
MessageBox.Show("Area of square: " & s.GetArea)
MessageBox.Show("Area of triangle: " & t.GetArea)
Let us say I want the developers on my team to implement "GetArea" with the same signature as available in "Rectangle" (just to be consistent among all classes). If the developers define their own methods like "GetEquiTriangleArea" or "GetSquareArea," the code would not give any compilation error (or warning) and somehow I lose consistency.
To maintain signature consistency (without implementation) in our code and to enforce such rules, we can implement "abstract" classes, as defined in the next section.
A method which has only a signature (declaration) without any implementation can be called an abstract method. Such methods, when declared, must be defined with "MustOverride." The abstract methods need to be overridden and implemented in child classes with the same signature that was defined in the parent classes.
Any class which has at least one abstract method must be called as an abstract class. The abstract class must be defined with "MustInherit" if it has at least one abstract method. Note that the abstract class may contain several methods; a few of those may already be implemented (not abstract methods) and a few may not (abstract methods). It doesn't mean that abstract classes cannot contain any methods with implementation.
Let us define an abstract class as follows:
Public MustInherit Class Shape
Private _x As Double
Private _y As Double
Public Sub New()
End Sub
Public Sub New(ByVal SingleValue As Double)
Me.X = SingleValue
End Sub
Public Sub New(ByVal FirstValue As Double, ByVal SecondValue As Double)
Me.X = FirstValue
Me.Y = SecondValue
End Sub
Public Property X() As Double
Get
Return _x
End Get
Set(ByVal value As Double)
_x = value
End Set
End Property
Public Property Y() As Double
Get
Return _y
End Get
Set(ByVal value As Double)
_y = value
End Set
End Property
Public MustOverride Function GetArea() As Double
End Class
The above class is named "Shape" and is defined with "MustInherit;" that means it may have one or more abstract methods. Besides normal members such as constructors, properties, fields, and so forth, it has a method declared as follows:
Public MustOverride Function GetArea() As Double
The above method is declared and defined without any code (or implementation). It is simply declared with "MustOverride." Any method declared as "MustOverride" is an abstract method. A class must be defined as "MustInherit" if it has at least one "MustOverride" (abstract) method.
The next section deals with the implementation of abstract methods.
In the previous section, a class named "Shape" is defined with "MustInherit" as it has one method declared with "MustOverride" (an abstract method). In this section, I shall create child classes (sub classes) for the parent class (super class) "Shape."
Let us start with the following:
Public Class Rectangle
Inherits Shape
Public Sub New(ByVal l As Double, ByVal b As Double)
MyBase.New(l, b)
End Sub
Public Overrides Function GetArea() As Double
Return Me.X * Me.Y
End Function
End Class
In the above "Rectangle" class, we have a constructor with the following statement:
MyBase.New(l, b)
The above statement calls the constructor of the super class; in this case, it is constructor of "Shape." Finally, the abstract method is implemented with the same signature defined in the "Shape" class as follows:
Public Overrides Function GetArea() As Double
Return Me.X * Me.Y
End Function
Similarly, the "EquiTriangle" and "Square" can be defined as described in the next section.
Continuing from the previous section, I defined two more sub classes for the super class "Shape" as follows:
Public Class EquiTriangle
Inherits Shape
Public Sub New(ByVal b As Double, ByVal h As Double)
MyBase.New(b, h)
End Sub
Public Overrides Function GetArea() As Double
Return ((1.0 / 2.0) * Me.X * Me.Y)
End Function
End Class
Public Class Square
Inherits Shape
Public Sub New(ByVal s As Double)
MyBase.New(s)
End Sub
Public Overrides Function GetArea() As Double
Return Me.X * Me.X
End Function
End Class
If the developer doesn't implement "GetArea" in any of the classes inherited from the "Shape" class, it would be a compilation error. This is how we can enforce consistency among members of classes belonging to the same group.
Finally, to test the above classes, we can write the code as follows:
Dim r As New Rectangle(10, 20)
Dim s As New Square(20)
Dim t As New EquiTriangle(5, 10)
MessageBox.Show("Area of rectangle: " & r.GetArea)
Since you are now familiar with abstract methods and abstract classes, it is time to learn about polymorphism.
Let us modify the code for testing as follows:
Dim sh As Shape
sh = New Rectangle(10, 20)
MessageBox.Show("Area of rectangle: " & sh.GetArea)
sh = New Square(20)
MessageBox.Show("Area of square: " & sh.GetArea)
sh = New EquiTriangle(5, 10)
MessageBox.Show("Area of triangle: " & sh.GetArea)
In the above code, I am always using the same variable (reference variable, and it is not a concrete object) to hold the instances of all child classes -- "Rectangle," "Square" and "EquiTriangle."
You can also observe that I am using the same variable "sh" and calling the same method "GetArea" with different results. Using the same method with different implementations and different executions at run-time is nothing but polymorphism.
A final note! We can never create any concrete object (an instance with a "new" operator) or instantiate on any abstract class. For example, the following declaration is invalid:
Dim shp As New Shape(10, 20)
This is because any abstract class is never completely implemented (it contains abstract methods without any implementation). If there exists no implementation for a method, we will not be able to access it using an object (or instance). And hence, we will not be able to instantiate any abstract class.
I hope you enjoyed the article and any suggestions, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com