Interface Aggregation

| | Comments (7)

A new language feature popped up in 2008r2 with very little fanfare, but is actually a really awesome new capability. Interface aggregation is the ability to aggregate several interfaces together into a "super interface." For instance:


Interface Test
Sub Foo()
End Interface

Interface Awesome
Sub Bar()
End Interface

Interface Sweet
Aggregates Test, Awesome
Sub Blah()
End Interface


In this example, we have three interfaces that contain a single method each. If a class were to implement the Test interface, then it would be required to implement just the method Foo. However, if the class were to implement the Sweet interface, then it would have to implement Foo, Bar and Blah. That's because Sweet aggregates the Test and Awesome interfaces.

Basically, when a class implements an interface, it must implement all of the methods from the interface, as well as methods from any aggregated interfaces. When a class implements an interface, then calling IsA on the class will return true for that interface as well as any aggregated interfaces. From our example above, a class implementing Sweet, then IsA Sweet, IsA Test and IsA Awesome are all true (since it implements all three of the interfaces).

This allows you the ability to combine interfaces in creative ways! This is important because interfaces don't necessarily relate to one another, but there are times when you need something that IsA multiple different interfaces. Or, when you want to merge functionality together for interfaces.

For instance, let's say that you want to have an item which can be iterated over. A common interface might be:


Interface Iterator
Function Current() as Variant
Function MoveNext() as Boolean
Sub Reset()
End Interface

This allows you to iterate over the object in a generic fashion. However, it could very well be that creating this iterator will cause circular references, or some sort of memory management issues. In that case, we might want to use a disposable interface, like this:

Interface Disposable
Sub Dispose()
End Interface

In our example, we could have the Iterator interface aggregate the Disposable interface. This would guarantee that anything which can be iterated over an also be disposed of afterwards.

You might be wondering "so why not just put the Dispose method right onto the Iterator interface?" That's a perfectly legitimate way to accomplish the same goal -- however, Iterator and Disposable aren't the same conceptually. So it doesn't make sense to force the two interfaces together in such a fashion. It doesn't hurt, but it just doesn't help either. Let's pick an example where it would hurt!

Let's say you have a method that is going to generically take a parameter which represents something that can read and write to/from a source. In this case, you want something that's both Readable and Writeable. You could just declare the method like this:


Sub DoSomethingAwesome( foo as Readable )
if foo IsA Writeable then
// Do reading and writing
end if
End Sub

This code will compile and will work, but it's also not safe. The user could pass in something which is Readable, but not Writeable and it will compile. Instead, the user could easily do this instead:

Interface ReadableAndWriteable
Aggregates Readable, Writeable
End Interface

Sub DoSomethingAwesome( foo as ReadableAndWriteable )
End Sub


Now there will be a compile error if the item doesn't implement both interfaces. That's a much better solution to the problem, wouldn't you agree?

The last thing I want to cover is the naming of the feature. Aggregates might seem like a very strange term to use, but in actuality, it really describes the feature better than anything else. The term "aggregate" means "to sum up" (essentially), which is what happens here. You are taking all of the interfaces and summing them up into a single uber-interface. The term "Inherits" would be a possible candidate, but it doesn't fit very well. Inherits assumes that the interfaces relate to one another, which isn't correct (look at the Readable and Writeable, for example). What's more, REALbasic only allows single inheritance and you can aggregate multiple interfaces together.

7 Comments

Now that is really useful. Thanks for pointing it out.

Apparently this idea comes from COM -- I'll have to think about it a bit.

@Charles -- actually, this idea also comes from C++, VB.NET, C# and other languages that support interfaces/pure abstract classes.

I totally missed this.

While in some cases it would be convenient if a certain group of interfaces was used together often , it seems to me that by far most of the time just assigning all the individual interfaces to a class would get the job done just fine.

Am I missing something? Would it be used more often by people than I think ?

- Karen

@Karen -- having a class implement multiple interfaces would achieve the same goal, yes. However, there are cases where this isn't feasible. For instance, what would a method signature look like for a function that requires something to be both Readable and Writeable? Since you can only pick one or the other, you're stuck doing IsA checks to ensure the caller is meeting your needs (which puts bugs off until runtime, instead of at compile time). Aggregation allows you to design a more clear API. (Of course, this is a rather contrived example.)

I'm delighted to see this feature. While working on a framework that's intended to be re-usable, I found I wanted to have interfaces that extend other interfaces.

My re-usable framework consists of several classes arranged in a hierarchy. For maintainability, I want code that uses the framework to use interfaces rather than the concrete classes; the classes are inside a module and set to "private" to enforce this. Up till now a completely separate interface was needed for each class. Each concrete classes in the framework implement several interfaces, making the code that uses the framework messy.

With 2008r2 onwards I'll be able to have a hierarchy of interfaces that 'shadows' the hierarchy of concrete classes, and inherit parents' capabilities. This will be clearer, and make both the framework and code that uses it easier to maintain.

Thanks Aaron and team!

The one thing still on my OOP wish list is a "package" / "friend" / "module" access level (in addition to the existing public, protected and private) that makes a member of "Class1" accessible only to code in other classes in the same module as "Class1" :)

I agree - package scope would be great. You can simulate it by creating a private interface in the module and having the class implement it using private methods. Other classes in the same module can then cast the object to the private interface and gain access to the methods, but code outside the module cannot see the interface. This would be even nicer if interfaces could contain properties; but in the end a built in package scope is the better solution.

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.