Sure, you might have heard the term “interface” before in another language and yet not truly know what they mean by “a contract”. Don’t worry, you are not alone. Several books talk about interfaces with the term “contract” and never really explain what they mean by saying a class is bound by that contract. So lets take a closer look at interfaces and specifically how VB.NET “implements” them. We use VB.NET in honor of PsychoCoder because he is not feeling the love of everyone using VB. We have your back buddy, you are not alone. We will hit this topic from a slightly different angle… a changeable bit screwdriver example. Something a bit different, as always, right here on the Programmer’s Underground!
You are probably asking yourself “A changeable bit screwdriver Martyr? WTF!?!” and all I can say is “Yeah, a changeable bit screwdriver.” You might have seen them at the hardware store. They are a screwdriver handle which comes with several bits that are used for different screws. One bit would be for a phillips head (the cross style screwdrivers), a common head (the flat head screwdrivers), an array of square heads for those odd square screws you see on machines, and maybe even a hexagon bit for those screws you get with that fancy table you bought at Ikea (it comes with those Allen wrenches… aka hex keys).
Now these bits come with a screwdriver handle which sometimes even carries the bits inside and you can easily take a bit, put it into the end of a screwdriver and attack that nasty screw. Then when you are screwing in another type, you can easily switch the bit and put that other screw in its place as well.
Where does this all fit into interfaces? Well imagine that each screw you are trying to drill into that piece of lumber is an object. Each screw has a different head, different length, some are flat heads and some are rounded, some are great for metal and some are great for wood. One could be a long silver rounded top screw that takes a common screwdriver to screw in and another could be a short black screw with a flat top and great for wood and takes a phillips head. They are two completely different screws and would need two different screwdrivers!
Then on the other end you have a screwdriver handle which takes a bit with a square end. All the bits have one square end which can then fit into the handle. It expects that any screw this screwdriver encounters will have a bit with one square end on it so that it can do its job. Now the bit is our interface!
Confused? Don’t be, the truth shall be revealed. The bit sits between the screw and the handle. It allows any screw, NO MATTER WHAT TYPE IT IS to appear to the handle as a square ended bit. The handle can’t see the screw itself, it just expects that if it is going to do any work that it has a bit with a square end on it that it can plug into.
This means that if you are screwing in a black wood screw, the handle “interfaces” with a bit that has a square end. If it is screwing a short silver phillips screw, it still sees a bit that has a square end. The handle can rely on the bit to provide a square end. This is what books/tutorials mean by “contract”. The bit “guarantees” that no matter what underlying object is being worked on, that it will provide at least a square end for the handle to use to do the screwing.
Ok, so enough with the giggling every time I say “screw” or “screwing”. Hopefully you get the idea. Below is another example of this interface idea in action. We create two objects. A car and a motorcycle. What do they share in common? They are both machines right? They both have wheels right? Well lets extrapolate out the idea of having wheels and make it an interface.
In the code below we are going to say that we want to create an array of objects. The only requirement for each object in this array is that it must implement a function called “numberOfWheels”. So we will create our interface and call it WheelNumber. Going back to our previous example, this interface is like saying that every bit must implement a function called “squareEnd”.
We create a car and say to the compiler “This object wants to implement the WheelNumber interface and thus will guarantee that it implements the numberOfWheels function”. It then defines a function called numberOfWheels as part of the class. Then we create a motorcycle that also wants to implement the WheelNumber interface. So it too creates a function called numberOfWheels.
So despite the two objects being different (a car versus a motorcycle) they both agree on having the same function named “numberOfWheels”.
With our array of objects, we define it saying that we want an array of objects that implement the “WheelNumber” interface. With this declaration we are saying that we don’t care about the objects type, all we care about is that it at least implements the function numberOfWheels. (We don’t care what screw it is, as long as it has a bit with a square end on it) The object could be a whatchamadohicki and who cares as long as it implements the numberOfWheels function. So all objects belonging to this array will have that interface implemented and a numberOfWheels function defined.
Lets take a look at how this is done in VB.NET….
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ' Declare an array of machines that we know is going to at least implement the WheelNumber interface. Dim machines(3) As WheelNumber ' Lets store a car in the first spot and a motorcycle in the second ' Notice these are different class types going into the same array! It works because both classes ' are classes that implement the "WheelNumber" interface. machines(0) = New Car() machines(1) = New Motorcycle() ' Lets create another motorcycle which is my favorite, is blue and goes 180. Dim myfavorite As New Motorcycle() myfavorite.MotorCycleColor = "blue" myfavorite.TopSpeed = 180 ' Store it in the array in the 3rd spot machines(2) = myfavorite ' My first car was a gray Dodge Aries and I think it topped out at like 90 Dim myfirst As New Car() myfirst.CarColor = "Gray" myfirst.TopSpeed = 90 ' Store it in the 4th spot machines(3) = myfirst ' Loop through the array. All we can deduce from this array is that they are a bunch of objects (possibly ' different types) that have one thing in commmon, they share the interface. For i As Integer = 0 To 3 ' One thing we can count on from this array of machines, that they all implement a numberOfWheels method ' Because they are bound by contract to the interface to at least support this method no matter if the ' machine is a car or a motorcycle TextBox1.Text &= "This machine has " & machines(i).numberOfWheels & " of wheels!" & Constants.vbCrLf Next End Sub End Class ' This is our interface. Any objects that implement this interface are saying ' "I swear to implement all functions and members defined in the interface. Public Interface WheelNumber Function numberOfWheels() As Integer End Interface ' Here is our car saying "I agree to the WheelNumber interface, thus I have numberOfWheels defined" Public Class Car Implements WheelNumber ' Our line stating we are impelementing the interface Private color As String Private numDoors As Integer Private topSpeedMPH As Integer ' Constructor Public Sub New() color = "Red" numDoors = 2 topSpeedMPH = 100 End Sub ' Some properties Public Property CarColor() As String Get Return color End Get Set(ByVal value As String) color = value End Set End Property Public Property NumberOfDoors() As Integer Get Return numDoors End Get Set(ByVal value As Integer) numDoors = value End Set End Property Public Property TopSpeed() As Integer Get Return topSpeedMPH End Get Set(ByVal value As Integer) topSpeedMPH = value End Set End Property ' As for the interface contract, we have fullfilled our obligations by defining its methods ' Notice how we also have an implements clause here tying directly to the interface definition of the function Public Function numberOfWheels() As Integer Implements WheelNumber.numberOfWheels Return 4 End Function End Class ' Our Motorcycle class that also agrees to the WheelNumber interface Public Class Motorcycle Implements WheelNumber Private color As String Private bikeType As String Private topSpeedMPH As Integer Public Sub New() color = "Black" bikeType = "Street" topSpeedMPH = 160 End Sub Public Property MotorCycleColor() As String Get Return color End Get Set(ByVal value As String) color = value End Set End Property Public Property BikesType() As String Get Return bikeType End Get Set(ByVal value As String) bikeType = value End Set End Property Public Property TopSpeed() As Integer Get Return topSpeedMPH End Get Set(ByVal value As Integer) topSpeedMPH = value End Set End Property ' As for the agreement, we implement the interface method numberOfWheels and tie it directly to the interface ' Notice that it returns a different value from that of car. Public Function numberOfWheels() As Integer Implements WheelNumber.numberOfWheels Return 2 End Function End Class
As you can see from the array declaration and the initialization, we can create a new Car and a new Motorcycle and stick them right into the array together. This is only possible because they both implement the same interface. Read the in-code comments to see how I have implemented the functions and tied them to the interface.
I have first created the interface definition called WheelNumber and say that it has one function called numberOfWheels. It has no body, just the function signature. That is all that is required because the body of those functions could be unique for each class and defined in each class. Indeed our car class returns 4 while the motorcycle returns 2.
Second I have told each class that it is to implement that interface by using the “implements” keyword.
Lastly, I have created the function (numberOfWheels) matching the exact signature of the interface except I also specify “implements WheelNumber.numberOfWheels” to let the compiler know that this function is to be used in its bond with the interface definition. The contract between each object and the interface is then complete. We have our bit setup for each screw.
So after loading up the array with two cars and two motorcycles, I can then loop through the array. Since all objects in this array implement the numberOfWheels function, that is the only function guaranteed to be there. So it is the only function I have access to in the loop. Intellisense will only show that one function and nothing else.
Upon execution, it will return the proper number of wheels for each object. If the underlying object is a Car it will return the number 4 and if it is a motorcycle, it will return 2. Each line is then added to a textbox I have setup so that it will show the first object having 4 wheels, the second and third objects having 2 wheels and the last having 4 wheels.
What advantage does this provide? Well imagine that you changed out a Car for a motorcycle in the array… no code would break. Lets say later I created a new object like a tricycle. As long as it implements WheelNumber as an interface I can introduce it into the array without changing anything else.
In a more useful example imagine that the object was a database like SQL Server. We have an interface for allowing our program code to send queries to the database without knowing what kind of database it is dealing with. Then when we change to Oracle, we don’t have to change any application code. It still doesn’t know what database it is dealing with “under the hood”. It makes our code more robust and easier to maintain as well as grants us some flexibility in dealing with several different objects based on their similarities. All this without really knowing what kind of objects we are dealing with underneath the interface… nor do we care.
I hope my example of the screwdriver with different bits helped give you an idea of interfaces and the concept behind them. Always think of the interface itself as the bit, the screws are the underlying objects and the screwdriver handle as the system (be it a class or subsystem) that wants to act on various kinds of screws without knowing what kind of screw it is acting on.
And when they say “contract” in the books, just remember the bit “guaranteeing” the square end of the bit to be there, despite the screw being acted on. The handle can always rely on that square end being there… and take comfort in that *siggggggh*.
Thanks for reading! 🙂