Using model-driven development for agile embedded apps: Part 3 - Action models & model translation
A design pattern has a number of important aspects. The problem is a statement of the goals of the design pattern, specifically: What design problem does the pattern address? The applicability defines the environmental or operational pattern because ultimately these decide whether the pattern is an appropriate choice.
The consequences are a set of benefits and costs of using the pattern. Because design (and design patterns) is all about optimality, whenever you optimize one aspect, you de-optimize some other. The consequences enable the developer to make good pattern selections.
The application of design patterns can be automated by tools. For example, the Rhapsody tool (from IBM Rational) applies a number of design patterns automatically (although this can be configured). Some of the design patterns Rhapsody automatically implements include the following:
- Container-Iterator Pattern
- Event Queue Pattern
- Guarded Call Pattern
- State Pattern
- CORBA Broker Pattern (supports CORBA, COM [Component Object Model], DCOM [Distributed Component Object Model])
- Data Bus Pattern (supports DDS)
For example, for the classes in Figure 2.11 below, code can be generated that uses built-in or Standard Template Library (STL) containers and iterators to manage the collection. This is done automatically by Rhapsody and serves as a useful, but simple, example of design pattern automation.

Figure 2.11 Container-Iterator Pattern example
The header file for the Waveform class is shown in Listing 2.1, below, and the implementation file is shown in Listing 2.2 below. You can see that OMCollection<DataElement*> and OMIterator<DataElement*> are added to manage the collection of DataElements.Listing 2.1: Waveform.h
#ifndef Waveform_H #define Waveform_H
//## auto_generated #include <oxf\oxf.h> //## auto_generated #include <oxf\omcollec.h> //## link itsDataElement class DataElement;
//## package pattern
//## class Waveform class Waveform { //// Constructors and destructors ////
public :
//##
auto_generated
Waveform();
//##
auto_generated
~Waveform();
//// Additional operations ////
//## auto_generated OMIterator<DataElement*> getItsDataElement() const;
//## auto_generated void addItsDataElement(DataElement* p_DataElement);
//## auto_generated void removeItsDataElement(DataElement* p_DataElement);
//##
auto_generated
void clearItsDataElement();
protected :
//##
auto_generated
void cleanUpRelations();
//// Relations and components ////
OMCollection<DataElement*> itsDataElement; //## link itsDataElement
//// Framework operations ////
public :
//##
auto_generated
void _addItsDataElement(DataElement* p_DataElement);
//##
auto_generated
void _removeItsDataElement(DataElement* p_DataElement);
//##
auto_generated
void _clearItsDataElement();
}; #endif
Listing 2: Waveform.cpp
//## auto_generated #include "Waveform.h" //## link itsDataElement #include "DataElement.h" //## package pattern
//## class Waveform Waveform::Waveform() { }
Waveform::~Waveform() { cleanUpRelations(); }
OMIterator<DataElement*> Waveform::getItsDataElement() const { OMIterator<DataElement*> iter(itsDataElement); return iter;
}
void Waveform::addItsDataElement(DataElement* p_DataElement) { if(p_DataElement != NULL) { p_DataElement->_setItsWaveform(this); } _addItsDataElement(p_DataElement); }
void Waveform::removeItsDataElement(DataElement* p_DataElement) { if(p_DataElement != NULL) { p_DataElement->__setItsWaveform(NULL); } _removeItsDataElement(p_DataElement); }
void Waveform::clearItsDataElement() { OMIterator<DataElement*> iter(itsDataElement); while (*iter){
(*iter)->_clearItsWaveform();
iter++;
}
_clearItsDataElement();
}
void Waveform::cleanUpRelations() {
{
OMIterator<DataElement*> iter(itsDataElement);
while (*iter){
Waveform* p_Waveform = (*iter)->getItsWaveform(); if(p_Waveform != NULL) { (*iter)->__setItsWaveform(NULL); }
iter++;
}
itsDataElement.removeAll();
} }
void Waveform::_addItsDataElement(DataElement* p_DataElement) { itsDataElement.add(p_DataElement); }
void Waveform::_removeItsDataElement(DataElement* p_DataElement) { itsDataElement.remove(p_DataElement); }
void Waveform::_clearItsDataElement() { itsDataElement.removeAll(); }
It should be noted that sometimes some models may be only implicitly created. For example, Rhapsody can generate PSI directly from a PIM through the application of design patterns. During this transformation (called “code generation”), Rhapsody internally generates the PSM (which it calls the “simplified model”) and then generates code from the PSM. Normally, this PSM is not exposed to the modeler, but it can be, if the developer wants to see, store, or manipulate it. For example, in the previous code samples, the PSM is not explicitly exposed.
It is also common to manually elaborate the PIM into the PSM by adding design patterns by hand. This can be done because some patterns are difficult to automate, for example. In general, I recommend a combination of both automated and manual transformations to create the PSM.


Loading comments... Write a comment