Car Product Line

Introduction

Suppose that you are buying a car and instead of getting a ready-to-use car, you get all the parts necessary to assemble the car your self. However you have to adapt some parts.
A better way is to order the car by describing it in abstract terms, saying only as much as you really care. e.g "mercedes-benz S-class with all the extras", or "a c-class customized for racing, with a high-performance V8 engine". So you get a ready-to-drive car.

Separation Between Problem Space and Solution Space

You don't have to enumerate all the concrete parts, e.g suspension, carburetor, battery etc. but rather specify the class (e.g C- or E-class), the line (e.g Classic- , Elegance-, Sportline) and the options (e.g airbags, trailer coupling) and get a finished car. The features in the problem space may be inherently abstract e.g SportLine. There is no single component that makes a car to be a sports car, but it is rather a particular combination of carefully selected parts that achieve this quality.
Also it is important that you can order a car by specifying only as much as you want.

Domain Analysis

Based on market studies, we decided that the cars that we are going to produce will provide the following features: automatic or manual transmission, electric or gasoline or hybric engine and an optional trailer coupling. This can be documented using feature diagrams which reveal the kinds of variability contained in the design space.

A feature diagram

  • car
    • car body
    • transmission
      • automatic
      • manual
    • engine
      • electric
      • gasoline
    • trailer coupling

The above diagram describes twelve different car variants. Constraints that cannot be expressed in a feature diagram have to be recorded separately.

Domain Design

The GenVoca architecture can be used.

Steps:

  • Identify the main functionalities in the feature diagrams from the Domain Analysis: The main functionalities for the car are car body, transmission, engine and trailer coupling.
  • Enumerate component categories and components per category: The component categories and the components per category are:
    • Car body: CarBody
    • Transmission: AutomaticTransmission, ManualTransmission
    • Engine: GasolineEngine, ElectricEngine, HybricEngine
    • TrailerCoupling: TrailerCoupling
  • Identify "uses" dependencies between component categories: CarBody uses Engine and Transmission, Transmission uses Engine, TrailerCoupling uses CarBody.
  • Sort the categories into a layered architecture: Each layer represents a category and the categories that most other depend on are moved towards the bottom of the hierarchy.
    • TrailerCoupling -> CarBody -> Transmission -> Engine
    • Finally we add two more layers: Car -> TrailerCoupling -> CarBody -> Automatic|Manual -> Gasoline|Electric|Hybrid -> Configuration
  • Write down the GenVoca grammar:

The main idea of the layered architecture is that a component from a given layer takes another component from the layer just below it as a parameter, e.g CarBody may take AutomaticTransmission or ManualTransmission as a parameter. Using this idea, we can represent this layered architecture as a set of grammar rules.

Car: Car[CarBodyWithOptTC]
CarBodyWithOptTC: CarBodyWithTC[CompleteCarBody]| CompletecarBody
CompleteCarBody: CarBody[TransmissionWithEngine]
TransmissionWithEngine: ManualTransmission[Engine] | AutomaticTransmission[Engine]
Engine: GasolineEngine[Config] | ElectricEngine[Config] | HybricEngine[Config]
Config: speeds, Engine, Transmission, CarBody, Car

Implementation Components

We can use C++ class templates for this purpose. For example, GasolineEngine can be implemented as follows:

template<class Config_>
struct GasolineEngine
{
typedef Config_ Config;

GasolineEngine() { cout « "GasolineEngine "; }

};

Manual Assembly

Now we can built different cars by writing down different configuration repositories, in which the appropriate components are assembled together.

Example
The following configuration repository defines a car with a gasoline engine, five-speed manual transmission and without a trailer coupling.

struct Config1
{ enum { speeds = 5; }
typedef GasolineEngine<Config1> Engine;
typedef ManualTransmission<Engine> Transmission;
typedef CarBody<Transmission> CarBody;
typedef Car<CarBody> Car;
};

Writing configuration repositories is a tedious exercise. An alternative would be to include all possible configuration repositories in the library. However, this is usually not practicable since there is normally a large number of configurations.
Solution: Generate the configuration repositories out of more abstract descriptions.

Ordering Cars

When you order a car, you don't have to specify all parts. Instead, you specify the class, the line and the options, which are usually listed in a product information brochure. The brochure specifies features available as standard equipment or as options for each line.
To implement a brochure in C++ we need to provide the vocabulary representing the lines (BasLine, CityLine and EcoLine) and options (transmission and trailer coupling).
The enumeration type Transmission will be used to specify the transmission and Options will be used to state whether a trailer coupling is available or not:

enum Transmission {fiveSpeed, fourSpeedAutomatic};
enum Options {none, trailerCoupling};}}

Now we need the vocabulary representing the three lines. We will model this vocabulary using types.

{{template < int transmission_ = fiveSpeed>
struct BaseLine
{ enum { transmission = transmission_, line=baseline};
};

BaseLine is available with either manual transmission (standard) or automatic transmission (option).

When you design a domain-specific language for "ordering" different systems or components, you may either prevent illegal feature combinations by structuring the language or by having an extra "buildability checking" step in the generator.

The Generator

The generator takes a specification of a system or component and returns the finished system or component. Here there is no buildability checking (since the domain-specific language doesn't give an opportunity to specify illegal feature combinations) and there are no computed defaults.
To implement the generator, we will use the built-in metaprogramming capability of C++, the template metaprogramming.

The generator is implementated as a template, taking the description of a car as its parameter and returning the finished car type. This is called RET.

Example
Create an instance of a BaseLine car with a four-speed automatic transmission and a trailer coupling:

CAR_GENERATOR<BaseLine<FourSpeedAutomatic>,trailerCoupling>::RET car1;

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Share Alike 2.5 License.