Software Product Lines
Framework Home
Introduction
Product Line Essential Activities
Product Line Practice Areas
Software Engineering
Practice Areas
Architecture Definition
Architecture Evaluation
Component Development
Mining Existing Assets
Requirements Engineering
Software System Integration
Testing
Understanding Relevant Domains
Using Externally Available Software
Technical Management
Practice Areas
Organizational Management
Practice Areas
Frequently Asked Questions
Glossary
Bibliography

A Framework for Software Product Line Practice, Version 5.0

Next Section Table of Contents Previous Section

Component Development

One of the tasks of the software architect is to produce the list of components that will populate the architecture. This list gives the development, mining, and acquisition teams their marching orders for supplying the parts that the software system will comprise. The term component is about as generic as the term object; definitions for each term abound. Simply stated, components are the units of software that go together to form whole systems (products), as dictated by the software architecture of the products. Szyperski offers a more precise definition that applies well [Szyperski 2002a]:

A software component is a unit of composition with contractually specified interfaces and explicit context dependencies only. A software component can be deployed independently and is subject to composition by third parties.

By component development, we mean the production of components that implement specific functionality within the context of a software architecture. The functionality is encapsulated and packaged and then integrated with other components using an interconnection method.

Software components trace their heritage back to the subroutine, which was the first unit of software reuse. Programmers discovered they could invoke a previously written segment of code and have access to its functionality while being blissfully unconcerned with its implementation, development history, storage management, and so forth. Soon, few people had to worry about how to code, say, a numerically stable double-precision cosine algorithm. Besides saving time, this practice elevated our thinking: we could think "cosine" but not have to think about storage registers and overflowing multiplications. It also elevated our languages: sophisticated subroutines were indistinguishable from primitive, atomic statements in the programming language.

What we now call component-based software development flows in an unbroken line from these early beginnings. Modern components, the latest instantiation of which are Web-based services that populate service-oriented architectures, are much larger and much more sophisticated, carry us much higher into domain-specific application realms, and have more complex interaction mechanisms than subroutine invocation (mechanisms that are standardized). However, the concepts and the reasons why we embrace them remain the same. In the same way that early subroutines liberated the programmer from thinking about details, component-based (or service-oriented) software development shifts the emphasis from programming software to composing software systems. Implementation has given way to integration as the focus. At its foundation is the assumption that sufficient commonality exists in many large software systems to justify developing reusable components to exploit and satisfy that commonality. Today, we look for components that provide large collections of related functionality all at once (instead of a cosine routine, think of Mathematica; instead of a relational database, think of an all-encompassing business platform; instead of an address book, think of Customer Resource Management systems) and whose interconnections with each other are loose and flexible. If we have control over the decomposition into components and the interfaces of each, the granularity and interconnection is determined by our system's software architecture. If the components are built externally, their granularity and interfaces are imposed on us, and they affect our software architecture.

The "Component Development" practice area is concerned with in-house development and how to build the components so that the instructions given to us in the architecture are carried out. New development should occur only after carrying out the activities in the "Make/Buy/Mine/Commission Analysis" practice area. Components obtained from sources other than new, in-house development are covered under the "Using Externally Available Software," "Mining Existing Assets," and "Developing an Acquisition Strategy" practice areas.

Aspects Peculiar to Product Lines

For the purposes of product lines, components are the units of software that go together to form whole systems (products), as dictated by the product line architecture for the products and the product line as a whole. If we appeal to the Szyperski definition of components given above, "deployed independently" may simply mean installed into a product line's core asset base where they are made available for use in one or more products. "Third parties" are the product developers who assemble the component with others to create systems. The contractually specified interfaces are paramount, as they are in any software development paradigm with software architecture at its foundation.

The component development portion of a product line development effort focuses on providing the operational software that is needed by the products and that is to be developed in-house. Either the resultant components are included in the core asset base and hence used in multiple products in the product line or they are product-specific components. Components that are included in the core asset base must support the flexibility needed to satisfy the variation points specified in the product line architecture and/or the product line requirements. The needed functionality is defined in the context of the product line architecture. The architecture also defines where variation is needed.

The singular aspect of component development that is peculiar to product lines is providing the required variability in the developed components via the variation mechanisms described in the example practices for this practice area. These variation mechanisms must be chosen to adequately support the production strategy and production constraints (see Core Asset Development).

Application to Core Asset Development

If a developed component is to be a core component, it must have an associated attached process that explains how any built-in component-level variation can be exercised in order to produce an instantiated version for a particular product. Developed components and their related artifacts (interface specifications, attached processes for instantiating built-in variability, test support, and so on) constitute a major portion of the product line's core asset base. Hand in hand with the software architecture that mandated them into existence, the core components form the conceptual basis for building products. Consequently, component development, as described above, is a large portion of the activity on the core asset development side of product line operations.

Application to Product Development

If a developed component is not to be part of the core asset base, it is most likely specific to a particular product and therefore probably does not have much variability built into it. While the development task must obey the architecture as strictly as it must for core components, non-core development is likely to be simpler. Nevertheless, developers of non-core components would be wise to look for places where variability could be installed in the future, should the component in question ever turn out to be useful in a group of products. Components for a product are either (1) used directly from the core asset base, (2) used directly after binding the built-in variations, (3) used after modification or adaptation, or (4) developed anew. Since the first two cases are pro forma, we will discuss the last two.

Adapting components: Components that are being used in a context other than the one for which they were originally developed seldom fit their assigned roles exactly. There are techniques that can accommodate these differences. The Adapter Design pattern [Gamma 1995a], imposes an intermediary between two components. The adapter can compensate for mismatches in the number or types of parameters within a service signature, provide synchronization in a multithreaded interaction, and adjust for many other types of incompatibilities. Scripting languages can often be used to implement the adapter.

Another technique is to modify the component to fit its new environment. Doing so may be impossible if the source code is not available. And, even if it is possible, it is usually a bad idea. Cloning an existing component creates a new asset that must be managed and creates a dependency that cannot be expressed explicitly. It can vary independently of its parent component, making maintaining both pieces a difficult task. Object-oriented notations provide a semantic device to express this type of relationship by defining the dependent class in terms of an extension of the original class. Although similar devices do not exist at the component level, a new component may be implemented by deriving objects from those that implement the original component.

Developing new components: New development should occur only after a thorough search has been made of existing core assets. In some organizations, the product team may have to "contract" with a component development organization to build the needed component. If it is built by the product organization, product line standards should exist for creating the core assets that support the component.

Whether a product component is adapted or built from scratch, it should be reviewed ultimately for "promotion" to the core asset base (and, in fact, should be developed with that in mind). To help with that review, robustness analysis [Jacobson 1997a] can be applied to determine how flexible the product is with respect to future changes in requirements. By examining change cases (use cases that are not yet requirements), the team identifies system changes that are needed in order to support the new requirements and thereby provides a feedback loop to the component developers. Specifications for new components and modifications to existing ones are the outputs of this analysis.

Example Practices

The example practices in this practice area all deal with component-level variation mechanisms. Architectural variation mechanisms are addressed in the "Architecture Definition" practice area.

Variation mechanisms (1): Jacobson, Griss, and Jonsson discuss the mechanisms for supporting variability in components (see the following table) [Jacobson 1997a]. Each mechanism provides a different type of variability. The variation of functionality happens at different times depending on the type. Some of these variation types are included in the specification implicitly. For example, when a parameter is used, the specification includes the specific type of component mentioned in the contract or any component that is a specialization of that component. In the template instantiation example below, the parameter to the template is Container, which permits variation implicitly via the Inheritance pattern. The Container parameter can be replaced by any of its subclasses, such as Set or Bag.

One aspect of variability that is important in a product line effort is whether the variants must be identified at the time of product line architecture definition or can be discovered during the individual product's architectural phase. Inheritance allows for a variant to be created without the existing component having knowledge of the new variant. Likewise, template instantiation allows for the discovery of new parameter values after the template is designed; however, the new parameter must satisfy the assumptions of the template, which may not be stated explicitly in the interface of the formal parameter. In most cases, configuration further constrains the variation to a fixed set of attributes and a fixed set of values for each attribute.

Types of Variation [Jacobson 1997a]

Mechanism

Time of Specialization

Type of Variability

Inheritance

At class definition time

Specialization is done by modifying or adding to existing definitions.

Example: LongDistanceCall inherits from PhoneCall.

Extension

At requirements time

One use of a system can be defined by adding to the definition of another use.

Example: WithdrawalTransaction extends BasicTransaction.

Uses

At requirements time

One use of a system can be defined by including the functionality of another use.

Example: WithdrawalTransaction uses the Authentication use.

Configuration

Previous to runtime

A separate resource, such as file, is used to specialize the component.

Example: JavaBeans properties file

Parameters

At component implementation time

A functional definition is written in terms of unbound elements that are supplied when actual use is made of the definition.

Example: calculatePriority(Rule)

Template instantiation

At component implementation time

A type specification is written in terms of unbound elements that are supplied when actual use is made of the specification.

Example: ExceptionHandler<Container>

Generation

Before or during runtime

A tool that produces definitions from user input.

Example: Configuration wizard

Variation mechanisms (2): Anastasopoulos and Gacek expound on a somewhat different set of variation options that includes [Anastasopoulos 2000a]

Practice Risks

The overriding risk in component development is building unsuitable components for the software product line applications. Doing so results in poor product quality, the inability to field products quickly, low customer satisfaction, and low organizational morale. Unsuitable components can come about by

Further Reading

[Jacobson 1997a] & [Anastasopoulos 2000a]
Together, these two works provide a superb compendium of component-level variation mechanisms that are available to a product line component developer.

[Szyperski 2002a]
Szyperski provides a comprehensive presentation on components that gives a survey of component models and covers supporting topics such as domain analysis and component frameworks.

Next Section Table of Contents Previous Section