This article is not about "user interfaces" - sometimes called a GUI or Graphical User Interface. This is about the Interface statement that can be used to define a VB.NET program object. It's a key technique in .NET object oriented programming (OOP) and it's often confusing when you first try to understand how it works.
This is an example just the code for a .NET Interface:
Public Interface IThisIsMyInterfaceDefinition Event AnEvent(ByVal Success As Boolean) Function AnInterfaceMethod() As Boolean Function AnotherInterfaceMethod() As Integer Property AProperty As Decimal End Interface
The main thing to notice is that there's no executable code. An Interface never contains any code; just a description of the names and types of code that would be there if it was a normal class.
Microsoft's documentation makes this clear:
"Interfaces define the properties, methods, and events that classes can implement. ... Interfaces cannot contain any implementation code or statements associated with implementation code."
So, if it doesn't do anything, why use one? Why not just define a class - including the code for the properties, methods, and events - rather than just the names and types? Most of what you can read about an interface fails to answer this question. That's what this article is all about.
Traditionally, the name of an interface always starts with a capital I. This isn't required, but everybody does it that way. There are few things in programming that everybody does the same way. Since this is one, don't break the chain! It's bad luck and your programs will never compile again if you do.
One reason for understanding interfaces is that Microsoft uses them a lot in the .NET Framework. For example, there is a complete system that you can use to define custom sorting that is built around the IComparable interface. I wrote a five part article explaining how to use it to sort colors instead of strings at The IComparable Interface: Using a Custom Sort.
But the focus of this article is to explain why you would want to use an interface in your own code. To do that, here's the example I will use:
Suppose you decided that you were going to make your fortune selling a software system to make it easy for someone else to write the code for their own card game invention. The problem with writing all of the code for a card game is the details of things like shuffling cards, dealing the cards, making bets, keeping score and so forth. Your system will handle all those details and so the people who buy your software only have to write the specific code for their unique card game.
An interface will help a lot. Here's a diagram showing how it this specific design will work:
Click Here to display the illustration
Keep in mind that there are a lot of ways to organize your code. I wrote an article a few years ago - Using Interfaces In Separate Files - that discussed the concepts of files, namespaces, the GAC (Global Assembly Cache) and even considered the question of how to describe a "dog" (Yes, the furry animal that digs holes in your lawn.) as an interface.
Starting at the beginning, I created a module named GameInterfaces. That module contains one class, Games, which contains the IDeal interface definition and two other ordinary code elements. Here's the IDeal interface:
Public Interface IDeal Property NumCardsToDeal As Integer Property NumPlayers As Integer End Interface
As the creator of this system, you supply both the interface and any other code that is available to people who will use your system. The other code in this system includes the property Hands, an XML XElement that represents a completed deal of the cards and the method DealTheCards that creates the property Hands. Here's the complete code:
Module GameInterfaces Public Class Games Public Interface IDeal Property NumCardsToDeal As Integer Property NumPlayers As Integer End Interface Private _Hands As XDocument Public Property Hands As XDocument Get Return _Hands End Get Set(ByVal value As XDocument) _Hands = value End Set End Property Public Function DealTheCards( ByVal NumCardsToDeal, ByVal NumPlayers ) As XElement Randomize() Dim theHands As XElement = _
For i As Int16 = 1 To NumPlayers theHands.Add( New XElement("Player" & CStr(i))) Dim aHand As XElement = theHands.Element("Player" & CStr(i)) For j As Int16 = 1 To NumCardsToDeal aHand.Add( New XElement( "Card" & CStr(j), Int(52 * Rnd() + 1))) Next Next Return theHands End Function End Class End Module
As you can see, this class relies on the values in NumCardsToDeal and NumPlayers to generate a random deal of the cards that ends up in the XElement Hands. But there's an obvious flaw here that I'll cover in a few paragraphs. (Can you see what it is?)
By itself, this class can do nothing. To make use of this class, it's inherited (so the inheriting class can make use of the public method and public property) and the interface is implemented. The new class - which is now a complete class because the interface is implemented - is shown below:
Module MyGame Public Class Deal Inherits Games Implements Games.IDeal Dim _NumCardsToDeal As Integer Public Property NumCardsToDeal As Integer _ Implements Games.IDeal.NumCardsToDeal Get NumCardsToDeal = _NumCardsToDeal End Get Set(ByVal value As Integer) _NumCardsToDeal = value End Set End Property Dim _NumPlayers As Integer Public Property NumPlayers As Integer _ Implements Games.IDeal.NumPlayers Get NumPlayers = _NumPlayers End Get Set(ByVal value As Integer) _NumPlayers = value End Set End Property End Class End Module
This code is completely standard and nothing new. All of the unique functionality is in the inherited class. The main thing to see here is the syntax to implement the interface.
The final code is the code that would be written by whoever buys the system to create their own game. (Obviously, a lot more than dealing the cards is required for a card game. This is a tutorial about interfaces.) In this case, I've only written enough code to demonstrate the use of the one class shown in a Windows form:
Dim theDeal As New Deal() theDeal.NumCardsToDeal = 5 theDeal.NumPlayers = 5 Console.WriteLine( theDeal.DealTheCards( theDeal.NumCardsToDeal, theDeal.NumPlayers).ToString)
When this code is run, the Output window shows that a random deal of five cards to five players is generated.
<Root> <Player1> <Card1>29</Card1> <Card2>1</Card2> <Card3>6</Card3> <Card4>45</Card4> <Card5>10</Card5> </Player1> ... <Player5> <Card1>26</Card1> <Card2>8</Card2> <Card3>8</Card3> <Card4>36</Card4> <Card5>1</Card5> </Player5> </Root>
There would have to be an assignment to a card deck. For example, ace of hearts = 1, deuce of hearts = 2, and so forth. But even the shortened sample above demonstrates the obvious flaw I mentioned earlier and leads us right into one of the major advantages of interfaces.
Notice that Player5 has two "8" cards (presumably the eight of hearts). And both Player1 and Player5 have a "1" card. In a game of poker, that might result in guns being drawn.
I covered this problem in a series that eventually grew to four articles starting with the article Create Random Arrays of Unique Integers so I'm not going to repeat all that here. Another problem is that the original code has 52 cards hardcoded into the DealTheCards method. The original question in the link above wanted 144 random unique integers for Mahjong. We can clear this up too.
Suppose you had sold your "IDeal" card game software to a few hundred thousand customers before anyone noticed the flaw. Now, you need to update the software without upsetting the customers. (More than they already are, that is.) A few customers may have implemented your system the way it is and like it that way so you will have to accomodate them too. The solution is to provide customers a new DLL with a version 2.0 of the GameInterfaces module.
The only software that you have to update is the original GameInterfaces module. We can add a new public method - perhaps we could call it DealUniqueCards - that ensured that the no cards were repeated. To solve the second problem, we can add another interface that also includes the total number of cards. We could call this one the IDealVarCards interface.
The key idea of interfaces is a fail safe way to let two sides of a system work together:
- The side that decides what data, methods, events and so forth will be used.
- The side that has to write compatible code to use all this stuff.
Both sides have to agree completely on this or the system doesn't work. Interfaces are the key that creates that agreement.
This illustrates another principal of interfaces. You can choose whether to implement an interface or not. (So, some customers will implement the IDeal interface and others could implement the IDealVarCards interface.) But once you decide to implement an interface, you have to implement everything in that interface.