Exported Interface

Intent

To allow an independently deployable component written in a statically-typed language (such as C++ or Java) to reverse an outgoing dependency so that the component can be compiled.

Motivation

Say we wish to write a GUI builder that simplifies the task of laying out a complex user interface, and we wish to sell this as a commercial tool to developers. Naturally, we want to be able to generate as much of the GUI code as possible to add value to our product. Therefore we want to be able to generate code to handle button clicks such as this:

MyGUI::Button1_Clicked()
{
   // call the relevant function in the developer's code
}

But how do we know what function to call? We can't know any of the names of the developer's functions as our product is a general product that can be bought off the shelf (we hope!). So how can we compile our code? Do we just leave the button handler as a stub function with a "fill in this bit yourself later" comment and no indication of how to fill it in, or do we try to write some code? If we write some code, how can we compile it if we're calling something whose name we don't (and cannot) know?

What we can do is to export an abstract interface from our component and get the developer to implement that interface and write the button handler like this:

MyGUI::Button1_Clicked()
{
   buttonHandler.Execute();    // an example of Command pattern
}

where buttonHandler is a member of MyGUI of type ButtonHandler. This allows us to generate and compile the required code without having to have an outgoing dependency on an as-yet unwritten piece of code. The dependency has been reversed and means that the application-specific client code now depends on the general GUI builder instead.

Applicability

Use the Exported Interface pattern whenever a component needs to call code outside of itself that cannot be known at compile time.

Structure

UML structure diagram

Participants

  • CoreClass
    • the algorithmic code that wishes to call external code

  • ExportedInterface
    • an abstract interface that defines the external methods to be called

  • ConcreteImplClass
    • the external code to be called that implements the exported interface

Collaborations

  • The CoreClass can call external code only via the exported interface

  • The user of the component must implement the exported interface, and pass a reference to that implementation into the component

Consequences

The Exported Interface pattern has the following consequences:

  1. The application-specific external code now depends upon the general component code. Without this dependency, it would be very difficult to write pieces of code that could be independently deployed.

  2. The exported interface becomes the home for documentation of the interface contract. Users of the component can therefore be made aware of the responsibilities of the contract in terms of pre- and postconditions, invariants, order of calls, etc.

  3. Blackbox reuse. This is an example of a blackbox framework. Users of the component needn't understand the internals of the component; all they need do is understand and implement the interface.

  4. Use of Adapters. If we are trying to compose two components, then it is quite likely that there will be an Adapter between the two that implements the exported interface of one and calls the inbound interface of another.

Implementation

The important part of this pattern is that the interface must be in the same component as the core class.

In C++, which has weak package management, this might be implemented using namespaces and delivering a source header file for the interface to the user along with the binary code for the component.

In Java the package construct would be used; the exported interface would be declared public and the internals of the component would be accessible only within the package itself.

Sample Code

Returning to the GUI builder example, we might have GUI builder code like this:

MyGUI::Button1_Clicked()
{
   buttonHandler.Execute();    // an example of Command pattern
}

the exported interface would be:

class MyGUICommandIF
{
public:
   virtual void Execute() = 0;
};

and the concrete implementation class might be:

class PayrollSuite : public MyGUICommandIF
{
public:
   virtual void Execute();
};

void PayrollSuite::Execute()
{
   Payroll payroll;
   payroll.StartPayrollRun();
}

Known Uses

COM uses this principle for all "outbound" interfaces. Java uses it as part of the JavaBeans framework when defining event listeners. Java servlets are another example.

Related Patterns

This pattern is strongly related to a group of patterns called Abstract Server, Abstract Client and Abstract Partner, the purpose of which is to decouple the caller and the callee. In the case of these abstract pattern the interface is often optional, whereas for this pattern the exported interface is essential as the class that is to implement that interface cannot be known in a composable component-based system.

The external implementing class (or classes) may well be external Strategy objects as a way of customising the components using external code.