Polymorphism is the use of multiple objects with the same methods that do different things.

Interfaces let you create flexible architecture in your application. Find out how these concepts differ in Visual FoxPro and Visual Studio .NET.

In previous articles, we showed you the fundamental building blocks of the object model in .NET and compared it to the equivalents in Visual FoxPro. So far, you've seen many techniques that were different at the level of details, but similar in overall concept. In this article, we're again going to look at a technique that's available in both environments: polymorphism. However, this time, both the implementation concepts and the details are somewhat different. So, to provide a good overview, we'll have to take a step back and discuss the basic idea of polymorphism first.

Polymorphism is generally described as the "state of having many faces" or the "state of having many forms." What this rather cryptic definition means is there can be multiple objects with the same methods that do different things. Because they appear the same to the outside, however, you can use these objects interchangeably. An example of this might be a save mechanism in an application: Whenever the user clicks on the save button in the toolbar, the currently active form is saved. Of course, different forms require different logic to save their contents. A customer edit form will save its information to the customer table, while an invoice form may save to invoice and line items tables. The code that triggers the save, however, doesn't care about these implementation details. Whenever the user clicks on the button in the toolbar, it will simply call the Save() method on the currently active form. This ability to call the Save() method on the active form object, no matter what it does, is known as polymorphism.

Polymorphism in Visual FoxPro is relatively easy to implement. The above example only requires that every form object has a Save() method. You can add this method to each form (or form class), and the button on the toolbar can issue a method call like this:

_Screen.ActiveForm.Save()

A similar example would be a log object. Envision a scenario in which you want to log certain information to disk, visually on screen, or perhaps the Windows Application Event Log. A simple version of the disk log object could look like this:

DEFINE CLASS DiskLog AS Custom
FUNCTION LogError(cError as String, cUser as String, ;
iLevel as Integer)
STRTOFILE("User" + cUser + ;
" caused the following error:" + cError + ;
" The level of the error is" + TRANSFORM(iLevel),;
"errors.log")
ENDFUNC
    
FUNCTION LogWarning(cWarning as String, ;
cUser as String)
STRTOFILE("User" + cUser + ;
" triggered a warning:" + cWarning,;
"warnings.log")
ENDFUNC
ENDDEFINE

As you can see, this simple object features two methods: one that logs warnings, and another that logs more severe problems. Each method receives a parameter with information to log, as well as a second parameter with the name of the user. In addition, the LogError() method receives a third parameter that describes the severity of the problem. The implementation of this object is somewhat simplistic -- there could also be more methods to log other things -- but for the purpose of this example, this object is sophisticated enough.

You could now use this object throughout your application to log warnings and errors. For example, you could pass this object to a method as a parameter:

DEFINE CLASS SomeClass AS Custom
FUNCTION SomeMethod()
LOCAL loLog
loLog = CreateObject("DiskLog")
THIS.TestMethod(loLog)
ENDFUNC
    
FUNCTION TestMethod(loLog)
TRY
         * Processing happens here
IF NOT File("MyFile.Txt")
loLog.LogWarning("Expected file missing",;
Sys(0))
ENDIF
CATCH
         * Errors are trapped here
loLog.LogError("Something bad happened",;
Sys(0),5) END TRY
ENDFUNC
ENDDEFINE

In many scenarios, the log object could simply exist, rather than be passed along as a parameter. This is similar to the form example earlier in which the form simply exists as an accessible object. You could rewrite this example to work in a similar fashion. One way to accomplish this is to make the log object a member of a global object, such as an application object, which is often the only global object that exists in an application. This depends on the particular architecture you prefer, however. Another option is to make the log object available as a public reference. This isn't considered good form and isn't recommended.

The important thing to realize here is that TestMethod() doesn't particularly care what loLog is or does. Therefore, you could simply create a different implementation of a log object, like this:

DEFINE CLASS ScreenLog AS Custom
FUNCTION LogError(cError as String, ;
cUser as String, iLevel as Integer)
?"User" + cUser + ;
" caused the following error:" + cError + ;
" The level of the error is" + TRANSFORM(iLevel)
ENDFUNC
    
FUNCTION LogWarning(cWarning as String, ;
cUser as String)
?"User" + cUser + ;
" triggered a warning:" + cWarning
ENDFUNC
ENDDEFINE

This example is similar to the previous one in that the class has the same methods with the same parameters. However, the class behaves differently. Rather than logging information to files, it simply displays all logging information on the screen. So, in a sense, the object is the same as far as any user of the object is concerned (such as the TestMethod()), but it's different internally. The fact that it has the same "face" as the first object lets you use it in its place without anyone ever noticing:

LOCAL loLog
loLog = CreateObject("ScreenLog")
THIS.TestMethod(loLog)

Everything from this point forward remains the same, except the information is displayed rather than written to a file. But, as we mentioned before, the TestMethod() doesn't care about that. The TestMethod() only cares about the two methods logging objects usually have. In fact, the object you pass along could have a plethora of other methods, as long as the two methods that are expected are available. This is polymorphism at its best.

The downside of all of this is that it's relatively easy to break this code. Consider the following example:

LOCAL loLog
loLog = CreateObject("Custom")
THIS.TestMethod(loLog)

In this example, you're passing an instance of a Custom class. This class doesn't have the LogError() method TestMethod() expects. Therefore, this example would crash at runtime when you attempt to call that method. Because Visual FoxPro is a weakly typed language, the compiler can't catch the problem. In other words, you didn't make any indication of what the first parameter of the TestMethod() function would be. Nor would the compiler have the ability to check whether that parameter fulfills the criteria you require. So, you have to be careful with what type of parameters you pass to avoid hard-to-find bugs.

Visual Studio .NET supports the same concept of polymorphism. Therefore, you could write a simple class like this:

Imports System.IO
    
Public Class DiskLog
Public Sub LogError(ByVal sError As String,_
ByVal sUser As String, ByVal iLevel As Integer)
Me.StrToFile("User" + sUser + _
" caused the following error:" + sError + _
" The level of the error is:" + iLevel.ToString(), _
"errors.log")
End Sub
    
Public Sub LogWarning(ByVal sWarning As String,_
ByVal sUser As String)
Me.StrToFile("User" + sUser + _
" triggered a warning:" + sWarning, _
"warnings.log")
End Sub
Private Sub StrToFile(ByVal sExpression As String,- _
ByVal sFileName As String)
Dim oFs As New FileStream(sFileName,_
FileMode.CreateNew, FileAccess.ReadWrite)
Dim oWriter As New StreamWriter(oFs)
oWriter.Write(sExpression)
oWriter.Flush()
oWriter.Close()
oFs.Close()
End Sub
End Class

This object is the VB.NET version of the DiskLog object you created in Visual FoxPro. The C# version is similar:

using System;
using System.IO;
    
public class DiskLog
{
public void LogError(string sError,
string sUser, int iLevel)
{
this.StrToFile("User" + sUser +
" caused the following error:" + sError +
" The level of the error is:" +
iLevel.ToString(),
"errors.log");
}
    
public void LogWarning(string sWarning, string sUser)
{
this.StrToFile("User" + sUser +
" triggered a warning:" + sWarning,
"warnings.log");
}
    
private void StrToFile(string sExpression,
string sFileName)
{
FileStream oFs = new FileStream(sFileName,
FileMode.CreateNew,FileAccess.ReadWrite);
StreamWriter oWriter = new StreamWriter(oFs);
oWriter.Write(sExpression);
oWriter.Flush();
oWriter.Close();
oFs.Close();
}
}

Note that the .NET Framework doesn't feature a StrToFile() function. Therefore, you have to write this method, which bloats your classes. Other than that, the classes are straightforward. If you need this type of functionality often, you should probably look into the VFP Toolkit for .NET, written by Kamal Patel (www.KamalPatel.net), which provides these types of functions in a way that's easy to use.

Now that you have these classes, you can use them just as you do the Visual FoxPro counterpart. First, here's the VB.NET version:

Public Class SomeClass
Public Sub SomeMethod()
Dim oLog As New DiskLog
Me.TestMethod(oLog)
End Sub
    
Public Sub TestMethod(ByVal oLog As DiskLog)
Try
If Not System.IO.File.Exists("MyFile.txt") Then
oLog.LogWarning("Expected file missing", _
System.Threading.Thread.CurrentPrincipal.Identity.Name)
End If
Catch
oLog.LogError("Something bad happened", _
System.Threading.Thread.CurrentPrincipal.Identity.Name, 5)
End Try
End Sub
End Class

And here's the C# version:

public class SomeClass
{
public void SomeMethod()
{
DiskLog oLog = new DiskLog();
this.TestMethod(oLog);
}
    
public void TestMethod(DiskLog oLog)
{
Try
{
if (!System.IO.File.Exists("MyFile.txt"))
{
oLog.LogWarning("Expected file missing",
System.Threading.Thread.CurrentPrincipal.Identity.Name);
}
}
Catch
{
oLog.LogError("Something bad happened",
System.Threading.Thread.CurrentPrincipal.Identity.Name, 5);
}
}
}

For the most part, these examples are similar to the Visual FoxPro examples. However, there's one detail with far-reaching implications: The first parameter in TestMethod() is strongly typed to be a DiskLog object. As you've learned from our previous articles, it has to be strongly typed as a DiskLog type. Otherwise, you wouldn't be able to call methods on that object. This lets the compiler verify that whatever object you pass along is of the "DiskLog" class and therefore has the methods you're attempting to call. This is great, because it lets you catch errors during compilation, which enhances the quality of your application.

Now let's proceed and implement the ScreenLog class as you do in Visual FoxPro. Here's the VB.NET version first:

Public Class ScreenLog
Public Sub LogError(ByVal sError As String,_
ByVal sUser As String, ByVal iLevel As Integer)
Console.WriteLine("User" + sUser + _
" caused the following error:" + sError + _
" The level of the error is:" + iLevel.ToString())
End Sub
    
Public Sub LogWarning(ByVal sWarning As String,_
ByVal sUser As String)
Console.WriteLine("User" + sUser + _
" triggered a warning:" + sWarning)
End Sub
End Class

And, here's the C# version:

public class ScreenLog
{
public void LogError(string sError,
string sUser, int iLevel)
{
Console.WriteLine("User" + sUser +
" caused the following error:" + sError +
" The level of the error is:" + iLevel.ToString());
}
    
public void LogWarning(string sWarning, string sUser)
{
Console.WriteLine("User" + sUser +
" triggered a warning:" + sWarning);
}
}

There aren't too many surprises here, so you can use this new class when you call your TestMethod() function:

ScreenLog oLog = new ScreenLog();
this.TestMethod(oLog);

This is where things get a bit tricky, because the compiler now indicates an error. What happened? The problem is rather simple. You typed the first parameter of TestMethod() to be a DiskLog object, but you passed a ScreenLog object instead. This may seem fine because both objects happen to have the same methods with the same parameters. However, it isn't fine for the compiler, because there's no assurance things will stay that way. In fact, the compiler has no knowledge at all about the similarities between the two objects. Therefore, this code won't compile. Although the problem is somewhat obvious, the solution isn't. What you need is a common type for both objects, so you can pass either version. One way to accomplish this is to define a common parent class. First, here's the VB.NET version:

Public Class Log
Public Overridable Sub LogError(ByVal sError As_
String, ByVal sUser As String, ByVal iLevel As Integer)
End Sub
    
Public Overridable Sub LogWarning(ByVal sWarning As_
String, ByVal sUser As String)
End Sub
End Class
    
Now, the C# version:
    
public class Log
{
public virtual void LogError(string sError,
string sUser, int iLevel)
{
}
    
    
public virtual void LogWarning(string sWarning,
string sUser)
{
}
}

Note that the Log class doesn't really have any functionality. It's what you generally know as an abstract class. This is a class that's designed for the sole purpose of being subclassed. With these classes in place, you can now change your ("concrete") classes to inherit from them, and override the pre-defined methods:

Public Class DiskLog
Inherits Log
    
Public Overrides Sub LogError(ByVal sError As String,_
ByVal sUser As String, ByVal iLevel As Integer)
' No changes here...
End Sub
    
Public Overrides Sub LogWarning(ByVal sWarning As_
String, ByVal sUser As String)
' No changes here...
End Sub
    
' No changes here...
End Class
Public Class ScreenLog
Inherits Log
    
Public Overrides Sub LogError(ByVal sError As String,_
ByVal sUser As String, ByVal iLevel As Integer)
' No changes here...
End Sub
    
Public Overrides Sub LogWarning(ByVal sWarning As_
String, ByVal sUser As String)
' No changes here
End Sub
End Class

Here's the C# version:

public class DiskLog : Log
{
public override void LogError(string sError, string sUser, int iLevel)
{
// No changes here
}
    
public override void LogWarning(string sWarning, string sUser)
{
// No changes here
}
// No changes here
}
    
public class ScreenLog : Log
{
public override void LogError(string sError, string sUser, int iLevel)
{
// No changes here
}
    
public override void LogWarning(string sWarning, string sUser)
{
// No changes here
}
}
This now also lets us redefine TestMethod() to use your abstract class:
    
Public Sub TestMethod(ByVal oLog As Log)
' Method continues as before
    
Here's the code in C#:
    
public void TestMethod(Log oLog)
// Method continues as before

Now you can call TestMethod() with any of the two log objects, because both inherit from the Log class, which makes them Log classes -- although specialized ones. You have to jump through a few hoops to make this happen, but the result is superior to the Visual FoxPro version. Because it's strongly typed, it lets the compiler check the validity of the code. Although the Visual FoxPro version has a high potential for hiding bugs that are difficult to find, there's almost no chance for a problem to creep into the .NET version.

What about interfaces?

So far, this solution works nicely. However, it still isn't optimal. For one, you had to create the Log class, without getting much benefit out of it other than being able to specify a common type. This isn't a huge problem, but it isn't ideal either. One of the downsides to this approach is you have to make all methods overridable/virtual, and as you've learned from our previous articles, this causes a bit of a performance hit.

Also, relying on the inheritance mechanism makes the construct somewhat inflexible. After all, each class can only have one parent class. But what if you wanted to make your class much more powerful? Perhaps you want the DiskLog class to also send e-mails when an error occurs. Or, maybe you use the same class to log things other than errors and warnings, such as user logins and logouts. In that case, you'd probably face the same problem you had before: You'd have to create some type of shared parent class for different objects capable of logging these types of events. However, this would put you in a pickle. Would you want the DiskLog class to inherit from Log, or from another class such as UserLog? The answer is you couldn't do this with inheritance. The architecture you've chosen is simply too inflexible. But there's a better way: interfaces!

An interface is the definition of all public methods and properties of a class/type. In fact, the Log class you created above is close to being an interface, because it only defines public methods and properties and doesn't have any code. So now you can convert that class to an interface. This is a straightforward task in VB.NET:

Public Interface ILog
 Sub LogError(ByVal sError As String,
 ByVal sUser As String, ByVal iLevel As Integer)
 Sub LogWarning(ByVal sWarning As String,
 ByVal sUser As String)
 End Interface

The C# version is also simple:

 public interface ILog
 {
 void LogError(string sError, string sUser, int iLevel);
 void LogWarning(string sWarning, string sUser);
 }

All you have to do is to use the "interface" rather than the "class" keyword. Subsequently, you define the methods you want. Note that you don't need the "public" keyword. Interfaces, by definition, define all the methods and properties that are visible to the outside world. Therefore, they couldn't be anything but public. Also, you don't have to mark these methods as virtual or overridable. That would be an inheritance mechanic that has nothing to do with the fact that the methods exist. The final difference is there are no method bodies, meaning there are no End Sub statements and no curly brackets.

It's important to realize that interfaces aren't classes. You can't instantiate an interface. However, interfaces are types you can use whenever you use references. Therefore, you can redefine TestMethod() like this in VB.NET:

 Public Sub TestMethod(ByVal oLog As ILog)
 
 And like this in C#:
 
 public void TestMethod(ILog oLog)

Now you can pass any object that "implements the ILog interface." This means you can pass any object that conforms to the definition of the ILog interface. However, it isn't enough just to add the same methods to an object. Instead, you have to formally declare that you want to fulfill the contract defined by that interface. At this point, you don't have such a class, but you can easily create a new ScreenLog class that fulfills that requirement:

 Public Class ScreenLog2
 Implements ILog
 
 Public Sub LogError(ByVal sError As String, ByVal_
 sUser As String, ByVal iLevel As Integer) Implements_
 ILog.LogError
 Console.WriteLine("User" + sUser + _
 " caused the following error:" + sError + _
 " The level of the error is:" + iLevel.ToString())
 End Sub
 
 Public Sub LogWarning(ByVal sWarning As String, ByVal_
 sUser As String) Implements ILog.LogWarning
 Console.WriteLine("User" + sUser + _
 " triggered a warning:" + sWarning)
 End Sub
 End Class

In VB.NET, the Implements keyword indicates you intend to write a class that has all the members defined by the interface. As soon as you type "Implements ILog" and press Enter, the Visual Studio .NET IDE automatically adds the two methods defined in the interface. Each method also has the Implements keyword that defines which method from the interface you want to implement. Note that the names of the methods are the same as in the interface, although that isn't a requirement. However, we don't recommend creating methods of different names because it's extremely confusing.

The concept is the same in C#, although the syntax is a bit different:

 public class ScreenLog2 : ILog
 {
 public void LogError(string sError,
 string sUser, int iLevel)
 {
 Console.WriteLine("User" + sUser +
 " caused the following error:" + sError +
 " The level of the error is:" + iLevel.ToString());
 }
 
 public void LogWarning(string sWarning, string sUser)
 {
 Console.WriteLine("User" + sUser +
 " triggered a warning:" + sWarning);
 }
 }

As you can see, C# doesn't make much fuss about ILog being an interface rather than a class. Therefore, C# still uses the ":" keyword to indicate you intend to conform to the ILog contract. Similar to VB.NET, C# assists you in implementing the appropriate methods. When you type "ILog", a tooltip tells you to hit the Tab key to create all the methods required by the interface. A difference between VB.NET and C# is that in C#, the method names have to be the same as in the interface, because that establishes the relationship.

You're now ready to use the new Log classes whenever you call TestMethod():

 Dim oLog As ILog = New ScreenLog2
 Me.TestMethod(oLog)

Here's the C# code:

 ILog oLog = new ScreenLog2();
 this.TestMethod(oLog);

In both versions you define a local variable of type "ILog", yet instantiate the ScreenLog2 class. This is perfectly acceptable, because ScreenLog2 is an ILog-compliant class.

When to use interfaces

The advantage of interfaces over inheritance is that each class can implement an unlimited number of interfaces, but each class can only have one parent class. This makes interfaces much more flexible than inheritance. On the other hand, interfaces are also less powerful. You can't attach any code or default behavior to interfaces. So the question is: When should you use interfaces, and when should you use inheritance? You generally want to use both. Interfaces and inheritance can co-exist perfectly. In the example, you defined the ILog interface, which creates a fundamental contract that describes what log objects should be like. Of course, it doesn't describe any behavior or provide default functionality. For example, your DiskLog object needs the StrToFile() method to write content to a file. This is a method that could be of interest to other classes as well, so you might want to put it into a high-level class. This isn't possible with an interface.

In general, it's a good idea to first define an interface, then create a default implementation in the form of a class. Based on that guideline, you can now create a DefaultLog class that implements the ILog interface:

 Public Class DefaultLog
 Implements ILog
 
 Public Overridable Sub LogError(ByVal sError As_
 String, ByVal sUser As String, ByVal iLevel As Integer)_
 Implements ILog.LogError
 End Sub
 
 Public Overridable Sub LogWarning(ByVal sWarning As_
 String, ByVal sUser As String) Implements ILog.LogWarning
 End Sub
 
 Protected Overridable Sub StrToFile(ByVal sExpression_
 As String, ByVal sFileName As String)
 Dim oFs As New FileStream(sFileName, FileMode.CreateNew, FileAccess.ReadWrite)
 Dim oWriter As New StreamWriter(oFs)
 oWriter.Write(sExpression)
 oWriter.Flush()
 oWriter.Close()
 oFs.Close()
 End Sub
 End Class

The C# version is again very similar:

 public class DefaultLog : ILog
 {
 public virtual void LogError(string sError,
 string sUser, int iLevel)
 {
 }
 
 public virtual void LogWarning(string sWarning,
 string sUser)
 {
 }
 
 protected virtual void StrToFile(string sExpression,
 string sFileName)
 {
 FileStream oFs = new FileStream(sFileName,
 FileMode.CreateNew,FileAccess.ReadWrite);
 StreamWriter oWriter = new StreamWriter(oFs);
 oWriter.Write(sExpression);
 oWriter.Flush();
 oWriter.Close();
 oFs.Close();
 }
 }

This class implements ILog and can therefore be used wherever an ILog type is expected. However, the class doesn't have any real code other than the ability to write to a file. Instead, all methods on the class are declared as virtual and overridable (which doesn't interfere with what's defined in the interface). This gives you a convenient starting place for most implementations of a log object. All you have to do is derive from the default object, like this:

 Public Class DiskLog2
 Inherits DefaultLog
 
 Public Overrides Sub LogError(ByVal sError As String,_
 ByVal sUser As String, ByVal iLevel As Integer)
 Me.StrToFile("User" + sUser + _
 " caused the following error:" + sError + _
 " The level of the error is:" + iLevel.ToString(), _
 "errors.log")
 End Sub
 
 Public Overrides Sub LogWarning(ByVal sWarning As_
 String, ByVal sUser As String)
 Me.StrToFile("User" + sUser + _
 " triggered a warning:" + sWarning, _
 "warnings.log")
 End Sub
 End Class
 
 Here's the C# code:
 
 public class DiskLog2 : DefaultLog
 {
 public override void LogError(string sError, string
 sUser, int iLevel)
 {
 this.StrToFile("User" + sUser +
 " caused the following error:" + sError +
 " The level of the error is:" + iLevel.ToString(),
 "errors.log");
 }
 
 public override void LogWarning(string sWarning,
 string sUser)
 {
 this.StrToFile("User" + sUser +
 " triggered a warning:" + sWarning,
 "warnings.log");
 }
 }

In this class, you don't worry about the interface. Because the parent class already implements the ILog interface, the subclass will do so as well.

Defining an interface first, then creating a default implementation of the interface, provides the best of both worlds. The hope is that you can use DefaultLog as the parent class for most of your logging objects, just as in other cases you'd hope abstract classes would serve that purpose. However, this assumption isn't always true. If you rely only on inheritance, this would be a problem. But, because you can still fall back to the basic ILog contract when you have to, you can implement logging objects that work completely differently. For example, you could have a form object that already has logging capabilities. If you want to reuse that object for logging purposes, you'd be at a dead-end if you'd relied only on inheritance (all forms have to inherit from Form, not your Log object). Instead, you can simply add an implementation of your ILog interface to the form:

 Public Class Form1
 Inherits System.Windows.Forms.Form
 Implements ILog, ISave
 // Rest of the form continues here...

Here's the C# code:

 public class Form1 : System.Windows.Forms.Form, ILog, ISave

The important thing to remember is that each class can only inherit from one other class, but it can implement any number of interfaces. The example above defines that the form implements a second interface called ISave that could be responsible for saving the form's contents.

A feature that's hard to do without

Interfaces are a powerful mechanism when it comes to creating flexible architecture. Inheritance is a powerful mechanism when it comes to creating default behavior that's fine-tuned or reused down the inheritance chain. There are numerous examples that define the usefulness of interfaces. This article presents the case for a simple scenario. However, it isn't hard to find other scenarios. For example, we've created our own .NET Framework and solution platform called Milos. One of the fundamental design guidelines in Milos is that no matter what we create, we first always create an interface that establishes a contract of what we need to develop. There's an IName interface that defines what methods and properties a typical name object has. Based on this interface, we then create specialized name objects, such as Customer. All our user interface objects are based on the IName interface. This way, we can create all kinds of different name objects, yet always use the same user interface. Of course, we don't always want to start from scratch when we create a new name object. Therefore, we also have a default implementation of a name object we can inherit from in most scenarios. But when we encounter scenarios that don't match our default implementation, we have other options. For example, when we encounter scenarios in which our clients already have existing code that describes customer information, we simply add the implementation of our IName interface to the existing code (no matter what those objects inherit from), and voilĂ ! We can use all our user interface objects with an entity that has never been designed to work that way!

There are also technical examples for the use of interfaces. One of the first examples presented in this article uses the Thread.CurrentPrincipal object to retrieve the name of the current user. This is a rough equivalent of the Sys(0) function in Visual FoxPro, although Thread.CurrentPrincipal only returns useful information if a user is logged into the application using Windows Authentication. But this might not be what you want. Maybe you want to create a simple data table with user name and password information, and use a custom mechanism to authenticate users. You could easily do this by implementing the IPrincipal interface in a custom class, which lets you create your own security mechanism. But, at the same time, everything else in the .NET Framework that relies on that class (such as security-related calls you might write) would still function unchanged.

Interfaces are intuitive and logical, yet many VFP developers have a hard time understanding the need for them. This isn't surprising considering there's no need for interface definitions in Visual FoxPro, mainly due to VFP's weakly typed nature. However, after you get used to the nature of interfaces, and start to appreciate their architectural advantages and quality control implications, they're one of those features that's hard to live without.

By Claudio Lassala and Markus Egger