Dealing with software diversity in kinds
Frank van der Linden
There are different kinds of variation within our products. Additional there are several different ways to deal with variation within products. To be effective we need to be able to select for each kind of variation the best way to deal with it. In order to do so we have investigated different kinds of variation and selected an initial set of mechanisms to deal with them.
Within our consumer products we find (mainly) three source of variation:
1 user interface diversity, partially originating from cultural differences,
2 differences in transmission standards,
3 differences between low-end and high end.
Each kind of variation may lead to many variants. Combining the variants we come to a huge number of possible variation within our products.
These sources each give rise to different kinds of variation:
– Each product has to deal with only one variant.
– Within one product several of variants the same functionality is available.
– A mixed form: Diverse products have different numbers and variants of the same piece of software.
For the second and third kind of variance compiler flags and option bits may not be sufficient. Dealing with the flags may become overly complex.
Additionally there are different time-scales associated to different kinds of variants:
– variation is between fixed limits and is known before product family design, e.g. transmission standards.
– not all variants are known before product family design, e.g. “evolving standards”.
– variation is only known before product construction, e.g. user-interfaces.
– variation that may be provided after delivery, e.g. user customisation.
Present approaches for dealing with variation
Presently diversity is modeled with option bits and compiler switches. The diversity gives rise to more than 500 parameters. In an extreme case it was reported that the collection of parameters consists of 1/4 part of the complete code, consisting of more than 1M lines. In these cases the option bits and compiler flags do not work because of the following reasons:
– The number of parameters is too large to be comprehended. Structuring or tool support is presently used to be able to deal with such an amount of variation.
– Complier flags and option bits are mainly used globally, and have their effect upon large parts of the system.
– Compiler flags and option bits are integrated at a low level within the code. Often they are used within if- or case-statements, sometimes they are used within arithmetic expressions or as parameters. Therefore the effect of an option bit or a compiler flag is difficult to predict.
Other possibilities for dealing with variation are not used:
– Inheritance mechanism in object-oriented languages
– Generic components, like in ADA
– Template mechanism, like in C++
– Specific components encapsulating the variance
– Variable configurations of components
– Dynamic link libraries, encapsulating the variance
– Other techniques providing binding of components at run-time.
The main reasons why these mechanisms are presently not used are:
– No support within the used programming language (presently C)
– No support by the development environment (compilers, linkers, configuration management, …)
– Not enough resources (memory, processing time) available upon the target
Encapsulation of variation
Because of the large amount of variability, the means to deal with variation should be considered to be part of the code. Variation should be considered in the design and taken into account in the design of components. Moreover we want to be able to concentrate the variance to small scale components. Many kinds of variance should affect only one, or a few, components. As with other software features variance should be encapsulated, and hidden from the remainder of the design. Of course, compiler flags and option bits may be an implementation strategy to deal with the matters of variation. However, we need to use them in a restricted way, in order to be able to keep variance local.
In order to encapsulate variation we should assign variation towards components. For several components there may be more than one version. Encapsulation can be performed through the availability of a fixed interface that all variants comply to. However, in certain case this is not enough. Then it may be better to have a separate component that deals as an intermediate component between the variant components and the remainder of the system. Especially in the cases that variation is well understood this is a good option. In the case that variation is not well understood, and cannot be completely predicted other means may be necessary.
Dealing with different kinds of variation
Consider the three sources of variation in the consumer products. The first kind of diversity (user interface diversity) is dynamic and short term. Preferably, the exact feature of the user interface are determined just before production time. Additionally variants may become available only after the system is initially constructed. Of course, several features of the variants can be predicted, and in particular the interface may be stable for a longer period. This kind of variation resides upon a set of “standard” user interface functions. The variation expresses itself in the different combinations the primitive user interface functions. In particular how input signals (e.g button presses) are translated into series of actions. Notice that future variation, both in input devices as in the course of actions, cannot exactly be predicted. Therefore, we should also be able to deal with the case that the interface is extended.
In order to deal effectively with this kind of variation we need to be able to develop the variant components separately. Of course they should conform to the requirements of the interface. A `late’ binding method should be used to add the variant(s) to the existing piece of software. The precise parameters of the variance can only be available for the variant developers. These parameters can and may not be set by the developers of the remainder of the system. Therefore, the interface between the variants and the remainder of the system should not involve any of these parameters. Variant information that is needed at other parts of the system should only provide by the component itself. This may, for example be done by specific calls that the component performs during initialisation. Notice, that for this kind of variation we should be able to deal with a recursive structure of variants within variants. For dealing with recursive variants similar measures can be taken as with the highest level of variance, viz. certain variants have specific interfaces or interface components encapsulation lower level of variance.
The second kind of variation (transmission standards) is static and long term. With this we mean that the collection of transmission standards is fixed already for many years. Notice that the digital standards are still arising, giving rise to a larger set of standards in the future. Most types of TV-set have to be provided with all kinds of standard, but usually only one standard for each set. Sometimes one set needs to be able to deal with more than one standard. The variation determined by the standards are mainly low-level functions, e.g. how to get information out of the transmitted signal. Certain kinds of information can only be selected for certain formats. For instance the teletext functionality is only available for the standards used within Europe. This means that a large part of high level functionality is senseless when we do not have such a standard. Preferably this functionality is also not present for these variants.
For each variant a component may be present in the system. Only one such component is present on each product. The variance may be encapsulated into components that are developed long before the remainder of the system is available. Parameters dealing with the variance should be provided by the component itself, e.g. upon initialisation, in order to make the remainder of the system independent of the variant. Dependent variance may be encapsulated into separate components that can only be bound if the right variant is available. Tool support, may be used to deal with the dependencies.
The third kind of variation (low-end vs. high-end) is longer term than for user-interface variation, but shorter term than for standards. This kind of variation is determined before product development starts. Variation expresses itself in the amount of available hardware. This means in the first place that for the middle- and low-end sets certain functions are not available. In the second place it means that less memory is available in the middle- and low-end sets. This reflects itself in different (lower) values for certain system constants dealing with sizes of tables etc. It also reflects itself in less memory to store user-functionality. Thus also for this reason certain functionality is not available. In certain cases a degenerated variant of a high-end functionality is available, e.g. less flexible.
This kind of variation affect the whole system. Therefore it cannot (completely) be encapsulated into components. However, we may have low-end and high-end variants of several components. Moreover, certain components may only be present in high-end sets. Finally, global system constants, and certain algorithms, may be combined into a collection of global components, of which there are several variants.
It may be the case that for different variants different binding techniques may be used. Late binding techniques have their effect upon performance and memory usage. this may not be available upon low end products. Additionally, variants should be available that consume less resources than other variants. However, also in this case the first technique may still be to separate and localise the different variants into components. Parts of the variant information may directly refer to hardware constraints. Specific tool support (compilers) may be able to combine the smaller variants into code that does not need too much resources. For each kind of variation resource management should be available keeping track of which variants can be combined.
For reasons of a.o. complexity management software variation should be encapsulated into dedicated components. Classical means, like compiler switches and option bits are not immediately applicable, without guidelines, supporting encapsulation. For different kinds of variation encapsulation is more or less easy to do. This is partly dependent in the time-scale in which changes occur, but it has also to do with persavive aspects, like the available hardware.