Share via


Visual Basic Concepts

Implementing and Using Standard Interfaces

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

Using Implemented Interfaces

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.

Setting References to Type Libraries

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."

Summary

The following list provides an outline for implementing multiple interfaces:

  1. Define a set of interfaces, each containing a small group of related properties and methods that describe a service or feature your system requires. This factoring process is discussed in "Providing Polymorphism by Implementing Interfaces."

  2. Create a type library containing abstract interfaces — abstract classes, if you create the type library by compiling a Visual Basic project — that specify the arguments and return types of the properties and methods. Use the MkTypLib utility or Visual Basic to generate the type library, as described in "Creating Standard Interfaces with Visual Basic."

  3. Develop a component that uses the interfaces, by adding a reference to the type library and then using the Implements statement to give classes secondary interfaces as appropriate.

  4. For every interface you’ve added to a class, select each property or method in turn, and add code to implement the functionality in a manner appropriate for that class. See "Polymorphism" in "Programming with Objects" in the Visual Basic Programmer’s Guide.

  5. Compile the component and create a Setup program, making sure you include the type library that describes the abstract interfaces.

  6. Develop an application that uses the component by adding references to the component and to the type library that describes the abstract interfaces.

  7. Compile the application and create a Setup program, including the component (and the abstract type library, if the component runs out of process or — with the Enterprise Edition — on a remote computer).

The next section discusses how the process outlined here can be used to gradually enhance a system.

Systems that Evolve Over Time

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:

  • Once an interface is defined and in use, it must never change. This concept of interface invariance is discussed in "Providing Polymorphism by Implementing Interfaces," in this chapter, and in "Polymorphism" in "Programming with Objects," in the Visual Basic Programmer’s Guide.

  • If an interface needs to be expanded, create a new interface. This is discussed in "Polymorphism, Interfaces, Type Libraries, and GUIDs," earlier in this chapter.

  • New versions of components can provide new features by implementing new and expanded interfaces.

  • New versions of components can support legacy code by continuing to provide old interfaces.

  • New versions of applications can take advantage of new features (that is, new and expanded interfaces), and if necessary can be written so as to degrade gracefully when only older interfaces are available. (See "Polymorphism, Interfaces, Type Libraries, and GUIDs.")

Implements and Code Reuse

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.