You've probably heard of them, and you may have never used one because you're not quite certain why you'd want to. What are these "interface" thingers, and why should you care about?
In its simplest form, an interface is a contract which says "anyone who implements me will for sure have these methods."
Let's dig deeper into why this is a very important thing for OO programming. Let's say you'd like to make a method which takes a generic object of varying types and be able to have it perform some common operation. Let's also say that these varying types are probably not going to be subclasses of one another, because they're widely varying -- apples and oranges instead of oranges and tangerines, so to speak. As a programmer, how would you be able to guarantee that you're able to call this method when you aren't even sure of what type you're being passed? Sure, you could be passed a generic (if your language supports the concept) and then use introspection (again, langauge dependent) to see if the object has such a method -- but you lose all type-safety when you do this sort of hackery, and it's a *lot* of work.
Wouldn't it just be better if you were able to specify in the parameters that you're only accepting objects which have this method on them? That way you get type-safety and you know that the method exists on the object, so you can just call it -- regardless of what's passed to you. Neato!
That's why interfaces are awesome, because they do exactly that.
An interface is an object type which declares a set of methods on it but does not define what those methods do. So when you create an interface, there's no code behind it to actually do anything -- you're just saying "I want a method with this name and these parameters and this return type."
Interfaces alone aren't objects you can instansiate. They've got no code behind them, just a set of declarations. So how do you use one of these? How do you get one so that you can pass it to your special method?
Similar to how a class can inherit methods, etc from its superclass, a class can implement an interface. When a class implements an interface, it tells the compiler "hey, anything that's looking for a type called 'MyInterfaceName' can take an instance of me!" The class must then adhere to the interface's contract -- that is, it must implement all of the methods that the interface declares. In this fashion, you can pass this class type around as either itelf, or as an interface which it implements, and the compiler will be happy. When a method receives something of the type "MyInterfaceName", it's able to access any of those interface methods without worrying, since it knows that the object passed to it defines the methods it wants to call. If it didn't, the compiler wouldn't let the object be passed in the first place.
So that's the theory behind interfaces. Now let's take a look at why you might want to use one from some real world examples.
In REALbasic, the framework defines a pair of interfaces which you've probably used without even knowing it: Readable and Writeable. These interfaces define a set of methods which allows you to pass around various objects which all have a common set of functionality. Let's take a look at Writeable as our example.
The Writeable interfaces declares that all implementors must have the following three methods defined:
[rbcode]
Interface Writeable
Sub Write( text as String )
End Sub
Sub Flush()
End Sub
Function WriteError() as Boolean
End Function
End Interface[/rbcode]
There are a number of classes which implement the Writeable interface within the framework itself: Serial, TCPSocket, BinaryStream, TextOutputStream, StandardOutputStream and IPCSocket. User-defined classes can also implement the Writeable interface as well.
So how is this particular interface handy? Why should you care about the power of interfaces? Simple -- the flexibility they offer you as a programmer is amazing. Let's write a quick and dirty debug logging function which utilizes this interface.
[rbcode]
Class Logger
mLogs( -1 ) as Writeable
Sub Constructor( originalLog as Writeable )
// Make sure we have at least one thing to log to
AddLogTarget( originalLog )
End Sub
Sub AddLogTarget( w as Writeable )
// If the user passed us something invalid, throw an exception
if w = nil then raise new RuntimeException
// Add the log target to our list
mLogs.Append( w )
End Sub
Sub Log( msg as String )
// Loop over all of our targets (we should have at
// least one from the Constructor) and log to them
for each logTarget as Writeable in mLogs
// Write to the log
logTarget.Write( msg )
// Make sure the write happens now
log.Flush
next logTarget
End Sub
End Class[/rbcode]
The point to this code is that you can make a global logger and add a bunch of targets to it. Why is this handy? Simple -- you can log to anything that's a Writeable. Let's see how!
[rbcode]
Module Globals
Private Dim mLogger as Logger
Public Sub AddLogTarget( w as Writeable )
if mLogger = nil then
mLogger = new Logger( w )
else
mLogger.AddLogTarget( w )
end if
End Sub
Public Sub Log( msg as String )
if mLogger <> nil then mLogger.Log( msg )
End Sub
End Module
// Let's log to a file
AddLogTarget( someFile.CreateTextFile )
// Now let's log to a remote computer via TCP
AddLogTarget( TCPSocket1 )
// Let's log to another process on the same machine
AddLogTarget( MySpiffyIPCSocket )
// Since we're running on a headless target machine with no
// ethernet, let's log to the serial device connected to us
AddLogTarget( SerialConnection )
// Now let's log some data
Log( "This is my message" )[/rbcode]
As you can see, it doesn't matter what we log to so long as it implements the Writeable interface. All the various class types that we passed to AddLogTarget will write out "This is my message" for us without us having to write specific code to handle those class types. And you'll notice that all these classes are different; none of them inherit from one another. The only similarity is that they all implement the same interface.
Now that you know the theory behind interfaces, and you've seen how powerful they can be, let's wrap up by describing how you can make interfaces in your REALbasic project.
- Open a new project
- When in the project item editor, go to Project->Add->Class Interface
- Change the name of the new class interface to be MySpiffyInterface
- Double click on MySpiffyInterface to open up the code editor for it
- Add a new method, and call it MySpiffyMethod. You'll notice that you have no way to enter code for this new method -- that's because interfaces don't contain code, they only contain declarations.
- Close the class editor for the interface. Back in the project item editor, click on the Add Class toolbar button
- Right click on the newly added class and select the "Implement Interface" contextual menu. In this dialog, drop the list down and select MySpiffyInterface
- Ta da! You've now created and implemented an interface.
Now you can go forth with your newfound knowledge and implement all sorts of great object-oriented concepts like the Chain of Responsibility pattern, the Bridge pattern, the Command pattern, and the Observer pattern.
Ask and ye shall deliver :-)
Thanks for the quick intro to interfaces.
Now to complicate matters further how does binding work. :-) ?
What I am specifically interested in is "runtime-binding", ever since I saw "addActionNotificationReceiver" I have been intrigued with it and want to know how exactly this can be used ?
I'd explain what bindings are, but Charles Yeomans has already done a fine job of it:
http://www.declaresub.com/Articles/ControlBinding/index.html
Hi Aaron,
Interfaces are really handy. Any chance that interfaces will be available on window classes? It'd be so nice to have them, and it seems odd not to have them, and so... so... unorthogonal! Or maybe there are technical reasons that make it difficult?
Thanks.
Dave
They're already available on window classes, but the ease of use depends on the RB version
If you're using 5.5 or less, you need to make a Class, set its super to Window and then implement interfaces on it. Then, make a window, set its super to the class name and BAM, interface implementing window.
In RB2005, the window itself lets you implement interfaces directly. No more subclassery.
Whoa! I didn't realize that. That's great!
Dave