[Back to Main Page]

Placeholder
Structural

Intent

Enables you to easily change which implementation of a component is used across an entire application. This can prevent inadvertent inclusion of multiple, incompatible implementations.

Also Known As

Pass-through, wrapper

Example TinyOS Components

...

Motivation

Often, a given component has several variant implementations. For example, a locking interface may have semantic requirements on its operations, such as requiring that only the component that holds a lock can unlock it. One implementation could assume that all calls are made properly, while a second one could add additional run-time checks.

The decision of which implementation to use has to be a system-wide one. Otherwise, different parts of the application may acquire locks on separate implementations, which would defeat the purpose of mutual exclusion. However, every configuration that wires the component names it; changing implementation could require changing many files, spread out over many subsystems.

One option is to implement the two versions with the same component name, and put them in separate directories. Manipulating the nesC search order could allow you to select which version to use.

While this approach may work for one or two options, it doesn't scale well to many: each implementation of each component needs a separate directory. Streamlining this structure by bundling several implementations (e.g., the "safe" version) in a single directory requires all-or-nothing inclusion.

The Placeholder pattern offers a solution. The desired component is represented by a placeholder configuration: all components that need to use the component wire to the placeholder. The placeholder itself is just a simple-pass through wiring to a concrete implementation. Changing which implementation is used requires changing only which one the placeholder represents. The additional level of indirection is merely at the language level, and the compiler optimizes it away.

Applicability

Use the Placeholder pattern when:

  • A component or service has multiple, mutually exclusive implementations.
  • Many subsystems and parts of your application need to use this component/service.
  • You need to be able to easily switch between implementations.

Structure

Participants

  • Placeholder: the component that all other components wire to. It encapsulates the implementation and exports its interfaces with pass-through wiring. It has the same signature as the Implementation component.
  • Implementation: the specific version of the component.
  • Users: components that want to use the functionality the abstraction provides.
  • Service: components providing functionality that the that abstraction depends on.

Collaborations

Consequences

Using the Placeholder pattern generally requires that every component in an application wire to the Placeholder instead of a concrete instance. Otherwise, there could be multiple implementations used concurently. Incorporating a Placeholder into an existing application can therefore require modifying many components.

Althlough a Placeholder adds another level of wiring indirection, the nesC compiler optimizes this away into a direct function call.

Implementation

Sample Code

Imagine an application that uses ad-hoc collection routing to collect and aggregate sensor readings. Several parts of the application -- sensing, time synchronization, power management -- may all need to interact with the routing protocol. However, the application is designed to be independent of the routing implementation, so that improvements or new algorithms can be easily incorporated.

In this case, the routing subsystem can be represented by a Placeholder, which provides a unified name for the underlying implementation:

configuration CollectionRouter {
  provides {
    interface StdControl;
    interface RouteControl;
    interface Send[uint8_t id];
    interface Receive[uint8_t id];
    interface Intercept[uint8_t id];

}
implementation {
  component MultiHopRouter as Router;

  StdControl = Router;
  RouteControl = Router;
  Send = Router;
  Receive = Router;
  Intercept = Router;
}

Every component that uses multihop routing wires to CollectionRouter:

configuration Sensing {
  ...
}
implementation {
  components SensingM, CollectionRouter;

  ...

  SensingM.Send = CollectionRouter.Send[AM_AGGDATAMSG];
  SensingM.Receive = CollectionRouter.Receive[AM_AGGDATAMSG];
  SensingM.Intercept = CollectionRouter.Intercept[AM_AGGDATAMSG];

  ...
}
		  

Changing the underlying routing implementation for all components that use it requires changing a single line in CollectionRouter, which then reconfigures the entire application.

Known Uses

Related Patterns

The Facade pattern also encapsulates a set of functionality inside a configuration, but it does so to provide several services using a unified interface. In contrast, the Placeholder pattern represents a single service for which there are multiple, incompatible implementations. The Facade deals with the granularity at which programmers deal with services, while the Placeholder deals with how those services are chosen and named.

[Back to Main Page]

Last modified: Fri Jul 30 17:09:59 PDT 2004