Temporal coupling refers to a variety of dependences between program or system components that involve time. These dependences have created difficulties for programmers since the early days of computing. Many of the program design methodologies and techniques in use today - data structures and design patterns for frameworks, toolkits, and asynchronous programs - is aimed at addressing these difficulties.

Data structures in general solve temporal coupling problems with regard to data. Without a data structure, when you discover, uncover, or produce data you must know what you need to do with it and have all of the prerequisite data that you need to deal with it.

Random access data structures (tables and, their big brothers, data bases) allow different program components to deal with discovery of data and its use. A data producing component can store the data in a random access structure, giving the data a key that data consuming components can use to access the data. They access the data according to their own schedule. The only coordination required between data producers and consumers is agreement on the association between keys and data. This is non-temporal information so the temporal coupling is dissolved.

Dispensers (stacks, queues, and priority queues) deal with ordering requirements in the processing of data items. A data producing component puts data items into a dispenser in some order and the dispenser hands out the data to a data consuming component in a possibly different order determined by its ordering policy.

The need for dispensers often arises when the data consuming component discovers new data items as part of its processing of earlier data items. Then the data producing component and the data consuming component are the same component.

When new data items must be processed completely before the processing of an older data item can be completed then a stack (last-in-first-out dispenser) is needed. A stack gives the component the capability to interrupt what it is currently doing, do something else, then resume where it left off. All but the earliest programming languages have supported this kind of behavior for procedures and functions using a built-in runtime stack.

When data items must be processed in the order that they are produced, but new data items are discovered before the processing of the data item that led to their discovery has completed, then a queue (first-in-first-out dispenser) is needed. New data items can be placed in the queue for later processing, allowing the processing component to complete its processing of the current data item.

Sometimes the order of processing of data items is dictated by some characteristic of the data item itself. For example, in discrete event simulations events cause or imply the occurrence of later events, and it is desireable to handle the events in time order. A priority queue, prioritized by time, lies at the heart of such simulations. The event processing component is just a loop that takes events off the priority queue and processes them. While processing one event, it may discover caused events. It only needs to place them in the priority queue for later handling, allowing the component to complete the handling of the current event.

Add text.

Data structures deal with one kind of temporal coupling - the potential coupling between when data is produced and when it is processed. Higher-level design patterns often deal with a different kind of temporal coupling - the potential coupling between what to do and when to do it. This kind of problem arises frequently in the design of toolkits and frameworks and the design of asynchronous programs.

The "what to do - when to do it" problem arises frequently in the design of toolkits and frameworks. A toolkit or framework designer knows when something should be done (that is, where it belongs in the code) but has no idea what should be done - that is left up to the toolkit or framework user. Many of the behavioral and creational design patterns in [GHJV1995] deal with this problem.

Intent: Make the behavior of an object dynamic.

Motivation

Dynamic behavior is a common objective in behavioral design patterns. The Observer, Chain of Responsibility, Strategy, and State design patterns are all examples. The structures of these design patterns all involve a delegation substructure that we can treat as a Dynamic Behavior design super-pattern.

Dynamic Behavior involves a Delegator participant and a Delegatee participant, with the Delegator delegating part of its responsibility to the Delegatee. Dynamic Behavior enhances flexility and easy reuse in several contexts, including

Another kind of situation where the "what to do - when to do" it problem arises is asynchronous programs. In an asynchronous program there are events occuring whose timing is independent of of the execution of the program. The application programmer knows what to do but has no control over when it is done. The most familiar example is programs with a graphical user interface. The program has no control over when the program user will click or move the mouse or press or release a keyboard key.

The traditional solution is conceptually simple: after setting up components in the graphical user interface, the program is driven by an event handler loop. User actions are entered into a queue as events. The event handler loop just removes events from the queue and handles them with callback code (listeners) provided by the application programmer. There is some synchronization code involved in access to the event queue but that is all hidden from the application programmer.

There is a potential problem that arises with this solution: what if a user event hander runs amuck? For example, it may contain an infinite loop or it may generate new events which directly or indirectly generate the same kind of event. For a single application this kind of problem should get fixed before the application is distributed. However, window managers and browsers cannot anticipate the applications that will be run on them.

Many early window managers provided a suite of window components, such as buttons and lists, that could be used by application programs. Typically all of these components were operated off a single event handler loop, along with window controls for shutting down windows and bringing up system menus.

With all of the system and application window controls operated from a single event handler loop, one errant application brings the entire system to its knees. An event handler in the errant application never completes execution so later events never get handled. On some older computers, the user may even be unable to shut down the computer without pulling the plug.

After the fact, the solution is obvious: set up the window manager so that it has its own event handler loop running in one thread. Each application has its own thread and its own event handler loop. The window manager event handler loop forwards events that occur in a window to the event handler thread of the process that owns the window.

Early browsers had a single event handler loop. When browsers began supporting multiple tabs the result was that an errant JavaScript program in one thread could lock up the handler thread. Consequently, a user could not even shut down the tab - they could only terminate the entire program (if the window manager was design right).

Modern browsers are fixing this problem by setting up a main event handler for the top-level controls of the browser. Each tab is running in its own thread with its own event handler loop.