Journal Articles

Overload Journal #86 - August 2008 + Programming Topics + Design of applications and programs
Browse in : All > Journals > Overload > 86 (7)
All > Topics > Programming (877)
All > Topics > Design (236)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Globals, Singletons and Parameters

Author: webeditor

Date: 17 August 2008 09:57:00 +01:00 or Sun, 17 August 2008 09:57:00 +01:00

Summary: One size rarely fits all. Bill Clare considers different approaches to parameterization.

Body: 

The purpose of this article is to outline a design approach for allowing data to be shared where needed and to not be visible elsewhere. The issue becomes particularly interesting when not all users who have visibility of a shared capability are actually provided with the same implementation of that capability.

Background

Kevlin Henney [Henney07] has recently written a series of articles ('The PfA Papers: Context Matters' et al.) about the history and advantages of a pattern termed PfA or Parameterize from Above. The pattern involves passing environment variables to applications as parameters rather than through use of globals or singletons.

Allan Kelly [Kelly04] addressed many of the same issues by developing a Pattern for a shared context 'The Encapsulated Context Pattern'. A spirited set of responses was later given (Overload 65, February 2005).

Many have written about the use and misuse of singletons.

Problem to be addressed

Before addressing approaches to this, it is well to state carefully the issues involved.

OO methodology provides strong support for encapsulation of the behaviour of a single or a related set of concepts. By itself, though, it provides little guidance about how these concepts interact and communicate. Various language features, patterns and approaches address this issue.

Here we consider approaches where:

This provides a separation of capabilities, that are dependent on their environment, from the base functionality of the clients, which are environment independent. The approach is to allow clients to be adapted to a rich set of environment based capabilities without code change to the client. However, it is worth noting that this notion of an 'environment' boundary can be somewhat flexible for many applications.

This notion also supports some of the concepts of Aspect Oriented programming, where common capabilities are 'woven' into client users. The emphasis there is on compile-time binding, while here it is on runtime binding.

Objectives

The basic objectives here are to suggest a framework where:

General approach

An approach to this can be based on considerations of an overall environment with particular implementations of shared capabilities to be used within certain scopes. Also there are related considerations for capabilities that have interdependencies, for resource control, for establishing concurrent processing, for data sharing, for establishing controls externally and for testing.

Environment

An environment is specified through a set of function or function object pointers that provide client access to capabilities.

Capability implementations

Capability implementations can be maintained and specified:

Roles

Ordinarily the external capabilities are orthogonal not only to their clients but also to each other. Where there are interdependencies among the capabilities, it is useful to view them as satisfying a common role. Examples of role based capabilities include:

Scopes

Values of a capability access pointer are stacked by scopes within the execution hierarchy. This can occur in two phases:

In both cases, resources may be obtained and setup, and appropriate configurations initialized when the scope is entered. When the scope is exited, finalization routines release resources, and restore the previous external state.

Access to other capabilities, that do not need to be adapted to the scope, are left untouched and thus are directly inherited.

Resource allocation and management

Data and other resources for shared capabilities needs to be actually created at some time and within some scope. The scope concept suggests a framework for management of resource instances. In many cases, this can provide more structured support for data sharing and control than through use of so called smart pointers.

Alternatives here include:

Implementation instances can be specified globally through maps indexed by a scope ID to appropriate functions or objects. Alternatively, implementations can be specified through local scopes where they are needed. This is based on design trade-offs between a co-ordinated global environment and configuration management on the one hand, and independent support for lower level functions on the other.

Concurrent processing

For concurrently executing threads or processes and for remote executions, the current environment is copied to initialize the new thread, process or remote execution environment.

For applications that queue requests to a separate thread or process, the queue manager can propagate access pointer references to the execution environment or environments of the request processing.

Data sharing

Where data needs to be shared for both read and update, the usual issues of data sharing remain, independently of scope management. These issues can be addressed with the usual techniques of:

External environment parameters

Parameters for adaptation can be supplied in environment variables and files that are accessed by initialization routines. With this, adaptation and tailoring of services for particular environments can be accomplished without code modification.

Test requirements

Testing occurs at several levels.

Accessing capabilities

Actual access to capabilities needs to be consider from two perspectives:

Scope setup

Scope managers set up client access to particular capability implementations. They have visibility to implementations only to create and locate them, and then to set pointers. There are several mechanisms possible for this access.

None of these techniques is exclusive of the others, and they are independent of the requirements of scope management. Thus, they can be combined as needed, especially for code obtained from different sources.

Client access

Actual use of a capability within client code can be much simpler. Here, the actual base application code can look like:


      pi( );         // retrieve value with
                     // consistent precision for
                     // this scope

or perhaps:


      math( ).pi( ); // retrieve role and property

The implementation mechanism at the client level can determine if these routines that access pi or math are:

In particular, this is independent of whether the actual values are derived from globals, singletons or passed parameters.

Logging example

Turning back to the logging example above, particular scopes may need specific logging capabilities, and so may substitute their own or just add to a global capability. Logging within a particular scope can in turn have specific functions for filtering, formatting, or routing. For test environments, or even as deployed, logging needs may vary considerably for individual scopes and circumstances.

Applications can be instrumented with a considerable amount of internal trace calls. Such code usually has considerable overhead, so it is desirable to be able to dynamically adjust the amount of detailed logging within separate scopes. Each trace call can provide parameters that specify a level of detail at which output should be recorded. The amount of output for specific tests, or to debug specific problems, can then be adjusted through external parameters that specify trace levels for different scopes. With appropriate templates, some of this tailoring can occur at compile time, with calls being completely eliminated through redefinition of the call templates.

Summary

A program's environment consists of a set of nested scopes which can be more or less global. Scopes have internal and external capabilities with many of the external capabilities shared to differing degrees with other components. A capability may have different implementations for use in different scopes under different conditions.

Judicious use of scope concepts allows common capabilities to:

Within this framework, applications can, without code impact, use capabilities derived from globals, singletons or parameters or, where necessary, combinations of such techniques.

References

[Henney07] Henney, Kevlin (2007) 'The PfA Papers: Context Matters' in Overload 82, December 2007 (and other articles in the same series in subsequent volumes).

[Kelly04] Kelly, Allan (2004) 'The Encapsulated Context Pattern' in Overload 63, October 2004.

Notes: 

More fields may be available via dynamicdata ..