Executable and translatable UML separates models from design. It enables you to test the model before you have a target, then generate an optimal target-specific design. Here's how to put it into practice.
Executable and Translatable UML (XTUML) is a subset of the Unified Modeling Language endowed with rules for execution. With an executable model, you can formally test the model before making decisions about implementation technologies, and with a translatable model, you can retarget your model to new implementation technologies. This article describes the fundamental ideas behind XTUML and how they work in practice.
Figure 1: Separation of application models and software architecture
Separate but equal
XTUML separates application models from software architecture design, weaving them together through a translator at deployment time, as shown in Figure 1. There are three components of an XTUML design:
- Application models capture what the application does. The models are executable, which enables you to validate that your application meets requirements early on. Application models are independent of design and implementation technologies.
- Software architecture designs, defined as design patterns, design rules, and implementation technologies, are incorporated by a translator that generates code for the target system. The software architecture designs are independent of the applications they support.
- The translator applies the design patterns to the application models according to the appropriate design rules.
Figure 2: Concurrent design and modeling
The separation of the software architecture design from the application models supports concurrent design and application analysis modeling, as illustrated in Figure 2. Using XTUML, you can iteratively and incrementally construct both the application and the software architecture design.
UML in execution
The UML specification defines the “abstract syntax” of UML diagrams but provides few rules on how the various elements interact dynamically.
XTUML incorporates well-defined execution semantics. Objects execute concurrently, and every object is in exactly one state at a time. An object synchronizes its behavior with another object by sending a signal interpreted by the receiver's state machine as an event. When the event causes a transition in the receiver, the procedure in the destination state of the receiver executes after the action that sent the signal, thus capturing the desired “cause and effect” in the system's behavior.
Each procedure consists of a set of actions, such as a functional computation, a signal send, or a data access. These action semantics are a fundamental part of UML. The actions are defined so that data structures can be changed at translation time without affecting the definition of functional computation-a critical requirement for translatability.
The application model therefore contains the decisions necessary to support execution, verification, and validation, independent of design and implementation. No design decisions need be made nor code developed or added for model execution, so formal test cases can be executed against the model to verify that application requirements have been properly addressed.
At system construction time, the conceptual objects are mapped to threads and processors. The translator's job is to maintain the desired sequencing (cause-and-effect) specified in the application models, but it may choose to distribute objects, sequentialize them, duplicate them, or even split them apart, as long as application behavior is preserved.
UML provides a notation set in which the same concept can be represented in a variety of ways. Before a new development team can be effective, it must first discuss, select, and agree on an acceptable UML subset, which can then be mapped to the team's method and modeling processes. As the project proceeds and new members join the team, constant vigilance is necessary to ensure that modeling conventions are maintained.
XTUML, however, is designed to be enforced not by convention, but by execution: either a model compiles, or it doesn't. Policing the notation-along with the time it consumes-is no longer an issue.
The notational subset is based not on diagrams, but on the underlying execution model. All diagrams (that is, class diagrams, state diagrams, procedure specifications, collaboration diagrams, and sequence diagrams) are projections of this underlying model. Other UML models that do not support execution, such as use-case diagrams, can be used to help build the XTUML models.
Translators generate code from models automatically. The translator (Figure 1 again) is made up of three elements:
- A set of design patterns (“archetypes”) to be applied in code generation together with rules for when a given archetype or model component will be used to build code.
- A translation engine that extracts application model information and applies the archetypes and rules to generate complete code.
- A run-time library comprising pre-compiled routines that support the generated code modules.
The partitioning of translators into three pieces means that changes and additions can be made to the archetypes or run-time library without having to contend with the details of the translation engine itself.
When generating code, the translator extracts information from the application model, then selects the appropriate archetype for the to-be-translated model element. The information extracted from this model is then used to “fill in the blanks” of the selected archetype. The result is a coded implementation component.
This approach is excellent for real-life applications. Application of an archetype commonly requires invocation of other archetypes. These newly invoked archetypes, in turn, often invoke other archetypes. The creation of code, for what appears to be one model element, can ultimately involve several nested layers of archetypes for multiple model elements. This intricate process is automated by the translator.
A realized software architecture design is a model compiler. It takes an executable UML model and translates it-according to the patterns embedded within the model compiler-into a running system. As with a programming language compiler, developers simply compile, link, load, and go.
Off-the-shelf model compilers automatically translate models to source code. Legacy, COTS, handwritten, and externally generated code can be integrated with translator-generated code by adding wrappers.
Integration is a matter of application model compilation using the selected model compiler. The application model has already been tested by execution, and the model compiler has been similarly tested by iterative performance verification throughout development. All that remains is the final system test. Failure of any part of system test requires predeployment maintenance, in which case changing or growing system requirements and system functionality only requires modification of the application model; such changes to the application model are automatically translated to new functionality in the target code without any modification to the design or manual coding.
Redeployment onto prospective software platforms works the same way. Because applications are independent of target platform characteristics, implementation technologies and language, they can be redeployed across multiple products and product families.
Executable and translatable
In practice, application modeling proceeds iteratively. As each increment, along with its test cases, is developed, it is executed and tested. This process implies an unmistakably clear exit gate: a completed application model must execute and execute correctly. Then it's on to compilation.
Model compilers may require additional information that is in neither the application model nor the software architecture design. For example, a model compiler may provide for persistent attributes, but the application model captures only required data. The application engineer must therefore annotate the application model with those attributes that are intended to be persistent. Similarly, your implementation environment may provide for one thread per processor, though each object in XTUML conceptually executes concurrently. The application engineer must therefore annotate the model to distinguish the task in which each object will execute. These annotations constitute design rules for when to apply a pattern.
The allocation implied by the annotation will affect performance-dramatically in some cases. Several allocations may be attempted until performance is adequate. Off-the-shelf model compilers can meet performance needs more often than is generally recognized, but when all else fails more radical performance optimization is required.
Model compilers are completely open. The archetypes and run-time library routines can be modified or replaced at will. In addition, some systems may require multiple archetype implementations for a given model element type. In such cases, an archetype can be defined for each desired implementation option. The default archetype used for a specific model element can be overridden by the preferred archetype using annotations. Model annotation provides a design engineer with a fine, second level of control over the implementation characteristics and performance of a particular system.
Model compiler development should proceed concurrently with application model development. As a risk-reduction strategy, application model increments likely to have performance problems should be modeled first. The design development team then generates code for each increment and profiles memory usage and performance to identify any “hot spots.” Incremental changes are then made to the base model compiler until performance is adequate.
Executable models catch defects through model execution. If an error slips through the early part of the lifecycle, the direct mapping between application model elements and the generated code leads to the specific application model element causing the problem. Fix the defective application model element, and the error goes away.
Independent software architecture design testing provides another defect-reduction checkpoint prior to initial integration and test, and it reduces the accidental introduction of new defects into the system. If a design or implementation error is caused by a faulty archetype, it will occur frequently and be all too visible. The error can then be traced back to the specific archetype that generated the defect. Repairing the faulty archetype propagates the correction throughout all of the system's generated code.
This approach simplifies repair of defects. Because a fix is only made at the source of the defect, fixing one problem is unlikely to introduce new problems. The introduction of side effects due to hand-coded modifications is eliminated.
Regeneration prompted by long-term support, continued defect fixes, addition of functionality, or performance tuning, should produce clean, well-documented, and structured code.
Reuse and target migration
Separation of design and application makes application models reusable because they are independent of target characteristics, implementation technologies, and language. Application models can be retargeted by selecting a different model compiler, so product families can grow and change in response to the underlying technology.
Software architecture designs-captured as model compilers-are reusable across multiple systems, independently of the specifics of the applications. Reuse of the same design in multiple applications also guarantees that applications built in different locations, with different developers, will nonetheless integrate with each other.
XTUML cries out for automated support for execution and translation. However, many of the ideas I've outlined so far can be put in place even without automation for the benefits they bring immediately and as forerunner to a more automated future.
Figure 3: The XTUML development process
The key concept-separation of application and software architecture design-can be expressed by thinking of design as a set of patterns and rules, rather than the crafting of individual implementation elements. (See Figure 3.) Each pattern is a particular approach to a design problem and a rule. For example, if the design problem is to search a class extent, the rule might be to use a linear search except when a linear search is “too slow.” The designer should provide an alternative (a hash table, for example) and define exactly when to use it instead of the linear search. (“Too slow” is too vague.) Design documents are not elaborations on the analysis, but a set of formal mappings, in natural language if necessary.
The laws of an XTUML design can be applied without resort to a machine. We have all, at some time or another, “executed” a program by hand either before executing it in a real machine, or perhaps to ascertain why the program did not execute as expected. Because an executable UML model has a defined interpretation, it is possible to establish exactly what the model does before coding. Each object is in one state at a time, and the order of signals to sender-and-receiver pairs of objects is determinate. When multiple signals are outstanding to different pairs, each possible ordering can be checked for the correct outcome-providing endless hours of amusement for those with a limited social life.
XTUML defines a tractable notation. This notation is known to work; following its rules tends to require system modelers to think through exactly what behavior they require without hiding behind abstruse notational complexities.
Translator structure and translator operation both describe an automated approach. The partitioning into patterns, rules, and implementation mechanisms, however, provides guidance for hand construction of the design.
Integration is eased, even in the absence of automation, because the design was expressed as rules. To the extent the system was constructed by applying the patterns and rules uniformly, then the system will integrate quickly and easily because the constructed components have been built to fit “by design.” Maintenance, however, is another matter, because the Law of System Entropy will surely set in, and despite our best efforts, the system eventually degrades.
Concurrent, iterative development can be applied to application model construction and to the construction of the unautomated model “compiler” (that is, the natural language set of patterns to be applied) in the same manner as for an automated solution.
Performance optimization means changing the patterns, rules, and implementation technologies to run faster and smaller. Modifying the implementation technologies is the same in both automated and unautomated cases, while the patterns and rules are simply expressed less formally than in an automated solution. However, using these new rules and patterns means recoding the application, or at least those portions affected by the new patterns and rules. Clever use of scripts and text recognition and generation programs can help here, but eventually entropy will have its way.
Defect identification and eradication in the application is similar in both cases, though hand simulation is more prone to error than an automated solution. Finding defects in the “model complier” has two parts: working out whether the pattern or rule was incorrect and, separately, establishing whether the patterns and rules were followed correctly.
Astonishingly, reuse and target migration using XTUML can yield significant benefits even without automation. The reason is that both the application models and the design, expressed as implementation technologies, patterns, and rules, each can be reused and retargeted. While it would be rather more efficient to automate the weaving of the two parts, mapping the application according to the patterns and rules from an existing application model is still much easier than hacking it out from scratch. Similarly, an existing set of design patterns and rules can be applied to a new application model more easily than if you have to work them out from scratch.
The benefits realized
XTUML has been used on over 1,400 real-time and technical projects, including life-critical implanted medical devices, Department of Defense flight-critical systems, performance-critical, fault-tolerant telecom systems, highly resource-constrained consumer electronics, and large-scale discrete-event simulation systems.
Capabilities provided by XTUML automation include:
- rapid project ramp-up resulting from a streamlined UML subset and a well-defined process
- concurrent application analysis and design to compress project schedules modeling
- reduced defect rates from early execution of target-independent application models and test of application-independent designs
- customizable translation generating complete, target-optimized code
- performance tuning and resource optimization
- effective, practical reuse of target-independent application models and application-independent designs
- accelerated development of products with multiple releases, growing or changing requirements, and families of products
- reduced maintenance costs and extended product lifetimes
In summary, the approach accelerates development and improves the quality, performance, and resource use of real-time embedded systems.
Stephen J. Mellor is best known for his contribution to the development of object-oriented analysis, recursive design, and the Shlaer-Mellor Method. He is vice president of Project Technology and works with Object Management Group to define extensions to UML for executable and translatable models. His e-mail address is .
1. A model compiler, like a programming-language compiler, is tested with a test suite that contains every element of the language being compiled, as well as all the information gleaned from deployment stress-testing of previous projects.
2. A model element type is some part of the XTUML language, such as an attribute, an event, a signal generation action, or a data access action. Each model element type (or associated pattern) has one or more archetypes that indicate the code to be generated.