Visual Basic Concepts
Once you’ve defined a standard interface, either by creating a type library with the MkTypLib utility or by compiling a Visual Basic project containing abstract classes (that is, class modules with properties and methods that don’t contain any code), you can implement that interface in classes your components provide.
Suppose you had a LateCretaceous system that included a number of components, each of which provided objects representing flora, fauna, and business rules of that era. For example, one component might provide a Velociraptor class, while another provided a Tyrannosaur class.
You might create a standard interface named IPredator, which included Hunt and Attack methods:
' Code for the abstract IPredator class module.
Public Sub Hunt()
End Sub
Public Sub Attack(ByVal Victim As IDinosaur)
End Sub
Notice that the argument of the Attack method uses another interface, IDinosaur. One would expect this interface to contain methods describing general dinosaur behavior, such as laying eggs, and that it would be implemented by many classes — Velociraptor, Tyrannosaur, Brontosaur, Triceratops, and so on.
Notice also that there’s no code in these methods. IPredator is an abstract class that simply defines the interface (referred to as an abstract interface). Implementation details will vary according to the object that implements the interface.
For example, the Tyrannosaur class might implement IPredator as follows:
Implements IPredator
Private Sub IPredator_Hunt()
' Code to stalk around the landscape roaring, until
' you encounter a dinosaur large enough to
' qualify as a meal.
End Sub
Private Sub IPredator_Attack(ByVal Victim As IDinosaur)
' Code to charge, roaring and taking huge bites.
End Sub
Important As noted in "Providing Polymorphism by Implementing Interfaces," an interface is a contract. You must implement all of the properties and methods in the interface.
By contrast, the Velociraptor class might implement IPredator as shown here:
Implements IPredator
Private Sub IPredator_Hunt()
' Fan out and hunt with a pack, running down
' small dinosaurs or surrounding large ones.
End Sub
Private Sub IPredator_Attack(ByVal Victim As IDinosaur)
' Code to dart in from all sides, slashing the
' victim and wearing it down.
End Sub
Once you have classes that implement IPredator, you can upgrade your existing applications one by one to use the new, more competitive interface. You can access the Hunt and Attack methods by assigning a Velociraptor or Tyrannosaur object to a variable of type IPredator, as shown here:
Dim tyr As New Tyrannosaur
Dim prd As IPredator
Set prd = tyr
prd.Hunt
You can also declare procedure arguments As IPredator
, and pass the procedure any object that implements the IPredator interface, as here:
Public Sub DevourTheCompetition(ByVal Agent As _
IPredator, ByVal Target As IDinosaur)
Agent.Hunt
Agent.Attack Target
End Sub
The Sub procedure shown above could be called with any predatory dinosaur as the first argument, and any dinosaur at all as the second. The caller of the procedure can use whatever predatory dinosaur is most appropriate for the occasion. This kind of flexibility is important in maintaining a business advantage.
A type library containing abstract interfaces provides a reference point for both implementing and using interfaces.
In order to implement an interface, you must use the References dialog box to set a reference to the type library. This is because the type library contains the information required to specify the arguments and return types of the interface’s members.
In similar fashion, any application that uses objects which have implemented an abstract interface must also have a reference to the type library that describes the interface. Information about implemented interfaces cannot be included in the type libraries of components, because there is no way to resolve naming conflicts.
Important In order to marshal data between processes or between remote computers, out-of-process components must include in their Setup programs any type libraries that describe abstract interfaces. In-process components should also include these type libraries, because a developer may want to pass its objects to other applications, either on the local computer or on a remote computer. See "Deploying Components," in "Debugging, Testing, and Deploying Components."
The following list provides an outline for implementing multiple interfaces:
The next section discusses how the process outlined here can be used to gradually enhance a system.
The observant reader will no doubt have noticed a bug in the code given earlier in this topic. If predatory dinosaurs only ate other dinosaurs, how did they keep the Mammals down? A more general IPredator interface might accept as a victim any object that implemented IAnimal.
This illustrates a key advantage of component software development using multiple interfaces: As the LateCretaceous system evolves into, say, the Pleistocene system, components that provide predatory dinosaur objects can be replaced by components that provide SaberTooth and DireWolf objects.
A legacy application compiled to use dinosaurs may be still be able to function quite nicely using the new predator classes, as long as it doesn’t include code specific to dinosaurs.
The key points to remember when using multiple interfaces in this fashion are:
The Implements statement also allows you to reuse code in existing objects. In this form of code reuse, the new object (referred to as an outer object) creates an instance of the existing object (or inner object) during its Initialize event.
In addition to any abstract interfaces it implements, the outer object implements the default interface of the inner object. (To do this, use the References dialog box to add a reference to the component that provides the inner object.)
When adding code to the outer object’s implementations of the properties and methods of the inner object, you can delegate to the inner object whenever the functionality it provides meets the needs of the outer object.
For example, the Tyrannosaur class might implement the interface of a Dinosaur object (instead of an abstract IDinosaur interface). The Dinosaur object might have a LayEggs method, which the Tyrannosaur class could implement by simple delegation:
Private dnoInner As Dinosaur
Private Sub Class_Initialize()
Set dnoInner = New Dinosaur
End Sub
Private Sub Dinosaur_LayEggs()
' Delegate to the inner object.
dnoInner.LayEggs
End Sub
This is an extremely powerful and flexible way to reuse code, because the outer object can choose to execute its own code before, after, or instead of delegating to the inner object.
For More Information Code reuse with the Implements statement is discussed in more detail in "Polymorphism," in "Programming with Objects," in the Visual Basic Programmer’s Guide.