OO CASE Tools for
By H. S. Lahman
What’s so special about R-T/E?
Real-Time or Embedded systems are generally regarded as the most difficult software systems to develop. There are a number of reasons for this, but the principle ones are:
Asynchronous processing - When the hardware decides to present an interrupt, it is usually blissfully unconcerned with whether the software is in a frame of mind to accept it. Moreover, it is often not an option to simply ignore some interrupts until a more convenient time, as when the operator’s sleeve has just gotten caught in the robotic handler. Of course Murphy’s Law dictates that the hardware (or some distributed component) will present its interrupt at the most inconvenient time. So the R-T/E developer is constantly faced with the open-ended task of trying to anticipate every bad thing that could possibly ever happen.
Challenging debugging environments - R-T/E environments tend not to have the megabytes of memory and sophisticated debugging tools that one comes to expect in the workstation world. In an extreme case one may not even have an I/O port for print statements. When combined with the randomness and intermittency of asynchronous processing, debugging becomes a major character building experience.
Changing hardware - Today’s electronics industry measures obsolescence in months rather than years. So embedded control system developers are faced with hardware that is constantly changing underneath software that is basically solving the same problem on each new 'system'.
Transaction volume - Real-Time developers of data processing systems must deal with huge volumes of transactions with erratic arrival times. A few decades ago this was mostly the concern of RDB engine developers, but now e-commerce makes this commonplace. Testing such systems is both a combinatorially complex task and a logistically daunting one.
Reentrancy - In the world of device drivers the same routine may be invoked incessantly, often before it has finished processing prior invocations.Throw in the need for efficient processing with multiple processors and maintaining data integrity becomes a major art.
Distributed systems - Such systems offer challenges for maintaining data integrity while preserving performance in addition to asynchronous issues. The dreaded deadlock of RDB's is vastly more significant in distributed systems where the resolution time may takes orders of magnitude longer to resolve or where one partner in a transaction can simply disappear inexplicably.
As computer driven technology becomes ever more ubiquitous in today’s world, the need for R-T/E development is growing. In addition the classical arena of business systems is beginning to look more and more like R-T/E as more and more users are simultaneously accessing systems from air line reservations to web commerce. Those business systems are also becoming more and more distributed. So the problems of R-T/E are moving out of the traditional hardware control environment and into the more mainline software development environments.
What’s important to R-T/E development?
When your world is dominated by asynchronous events and reentrancy, the Turing model goes out the window and you need a different perspective on time. Something has to bring order to such chaos. Over the years the R-T/E people have learned to love a marvelous mathematical construct: the finite state machine (FSM). This artifact is well suited to asynchronous analysis because one of the rules that governs it is that a state cannot understand context (i.e., it doesn’t know either its last state or its next state). This allows one to define processing in atomic bursts associated with state machine states so that they are relatively unaffected by the order in which events cause migration from state to state.
As it happens, the FSM is very object oriented due to this context independence of states. Another thing that R-T/E developers learned via painful experience was that state machines are best designed using an "I’m Done" paradigm rather than a "Do This" paradigm. A well-designed state machine announces to the world at large that it has reached a particular state by generating an event. That state machine's implementation is fully encapsulated and decoupled from the rest of the application because it does not know who might care about that event, if anyone.
The developer, who understands the overall problem to be solved by the interaction of state machines, decides who should care about that event and assigns a target state machine to it. Thus, problem space flow of control is defined completely outside the implementation of a single object (i.e., its FSM). More importantly, it is done after designing the state machines. Probably the most important characteristic of OO development for the R-T/E environment is the ubiquitous use of FSM's to describe object behavior.
Another thing that is important to R-T/E developers is translation. Translation is the notion that OOA and OOD are separate tasks. In this world an OOA is an abstract, implementation independent description of the solution logic and the OOD defines the means by which that OOA is translated into code for a particular computing environment. In this paradigm an OOA is portable without change across computing environments; all that changes is the translation engine on each platform. Similarly, one builds a single translation engine for a computing environment that is reused to translate all application OOA's in that environment.
This is driven by the notion of constantly changing hardware so that one can divorce the details of the changing computing environment from the fundamental logic of the problems being solved. This allows a familiar solution to be easily ported. It is also driven by the need to verify the problem solution in a less painful debugging environment than the final system. If an OOA represents a full, albeit abstract, description of the solution that is not dependent upon the specific implementation, then it should be possible to verify that solution away from the implementation. Indeed, as we shall see, all the CASE tools that focus on the R-T/E environment support model level simulation for verification.
Performance is another reason that drives translation. Very often R-T/E applications have stringent performance requirements because they are common resources to a large number of users or other applications. An OOA that is independent of the computing environment is also independent of the implementation language and the optimization techniques. Sadly, today’s OOPL's are usually not competitive with languages like C, BLISS, or Assembler in performance. However, translation allows the OOA to be implemented in inherently faster languages than OOPL's.
Translation also allows the source code to be tuned for a particular computing environment in the way it is written. While an expert can fine-tune an OOPL to provide reasonable performance, this restricts the pool of talent available for performance sensitive applications. By focusing the efforts of such talent on a single, reusable translation engine, economies of scale are achieved across all such applications in that computing environment.
Another important aspect of R-T/E developments is an emphasis on modularization. This was originally driven by hardware changes. Concepts like layering, installable device drivers, plug & play, and the like all grew out of the R-T/E environment where such things were a matter of survival. In fact some of the OO methodologies that cater to the R-T/E environment, such as Shlaer-Mellor and ROOM, define component interfaces with particular rigor and considerable sophistication.
For example, Shlaer-Mellor defines invariant interfaces for a component that are semantically defined by the requirements on the component. However, it is recognized that clients and services often prescribe different syntaxes for the interface. So Shlaer-Mellor postulates that a bridge always exists between the components whose mission is to resolve syntactic differences. Thus a client's request for a high-level service like Measure can be matched against a service that provides a low-level interface like Setup, Initiate, and Fetch. In effect the bridge is glue code that is specific to the application. (In an OOPL, the bridge might be implemented as a polymorphic wrapper.)
So how do CASE tools support these needs?
There are a number of tools that are directly focused on OO development in the R-T/E community. The following is a representative sampling: Bridgepoint from Project Technologies; Intelligent OOA from Kennedy-Carter; ObjectBench from SES; ObjectTime from ObjectTime, Inc.; Rhapsody from I-Logix; Rose RealTime from Rational; and System Architect from Popkin Software. They all share the following characteristics:
FSM dynamics - They all employ finite state machines as the central description of application dynamics with one FSM for each object with an interesting behavior.
Model simulation - The OOA models can be simulated to verify the fundamental logic of the solution prior to committing to code. This can be done manually but all of these tools provide automated simulation with sophisticated animation.
Action language - They all have a language of some sort to describe what goes on in the actions associated with FSMs.
Subset of UML - They all use a subset, albeit slightly different ones, of the UML for simulation and code generation. Some tools, like Rhapsody, support the full UML in the drawing tool portion.
Translation - All provide 100% code generation of the OOA off-the-shelf for particular computing environments.
In addition, most of the tools above explicitly support a bridging model for components like that described above.This provides a powerful, general model for component reuse.
The idea of an 'action language' warrants some additional discussion. The action language describes what an object does. Several CASE tools use an augmented source language, such as C++, for the action language. This is clearly easier for the CASE tool developer but it has a major disadvantage from an OO modeling viewpoint because it is at the wrong level of abstraction.
Ideally, the action language should be at the same level of abstraction as the OOA. That is, the algorithmic aspect should be described just as abstractly as the static model. For example, one commonly navigates relationships in the class diagram to get to particular instances. In an implementation there are many ways to optimize such navigation, such as smart pointers, embedded objects, linked lists, etc. But if the OOA is to be implementation independent, it should not be concerned with these options. Instead the OOA should use a very general and abstract syntax for such navigation.
When the vendor uses a source language as the action language, the choice of navigation mechanism becomes written in stone. If one ports to another computing environment where another mechanism is optimal, then one has to change the action language entry in the OOA.
[FWIW, the action language meta model specification currently being jointly proposed to OMG by Rational, Project Technologies, Kennedy-Carter, et al., does strongly encourage high level, abstract action languages.]
It should be noted that an action language is desirable even if one does not use translation. If one simply elaborates on OOA models through OOD by providing successively more detail, the action language still provides a high level description of methods that would allow the generation of more than just header files and body stubs. The presence of an action language also allows model level simulation, which can also be useful during the development as an incremental check on the addition of details. In effect, this allows a sophisticated form of prototyping that isn’t throw-away.
As noted above, the current CASE tools offer translation to specific computing environments. What happens if your CASE vendor doesn’t happen to have your target environment in stock? There are really three aspects to these CASE tools: the drawing tool where the models are recorded; the model simulator; and the code generator or translation engine. Ideally these should be plug & play. One should be able to buy a drawing tool from one vendor and then tack on a code generator from some other vendor who happens to have one for the desired target computing environment. This is not yet the case for today’s state-of-the-art CASE tools, though at least two vendors - ObjectTime and Pathfinder Solutions - are moving in this direction by developing translation architectures for different vendors' existing tools.
It is interesting that among all the other CASE tools supporting OT, very, very few support even two of the five characteristics listed above. Therefore it is highly significant that all of the R-T/E OO CASE tools support all five characteristics. This strongly suggests that in the R-T/E environment these characteristics are essential.
So is this a niche, or what?
The next question is: Are these characteristics limited to the niche of R-T/E development or might they be of interest to CASE tools catering to other OO application environments? I would assert that they are of wider interest. As indicated above, the traditional software development environments are increasingly facing similar problems to those that plague the R-T/E environments.
Certainly distributed systems and high transaction volume are becoming major issues across the board for software developers. Similarly, asynchronous processing and reentrancy are becoming more common as we develop multi-threaded applications and deal with distributed or server based systems. Though changing hardware may not be a problem for most applications, porting across platforms is becoming quite common. Whether it is a hardware interface or an operating system or a database or a window manager, the porting issues still make it desirable to maintain the problem logic as an invariant. So about the only driving force that is unique to the R-T/E environment is abysmal debugging facilities.
From a purist point of view I would argue that the FSM paradigm is, on general OO principles, a better way to describe system dynamics. Without this paradigm one uses sequence diagrams to define messages sent from one object to another. While this superficially seems to define the problem flow of control outside the object implementations, that view is deceptive.
Look at almost any modern OO book and the messages in a sequence diagram are described as 'requests'. Moreover, the sequence diagrams are often used to allocate functionality to objects. By doing so one is subtlely introducing a "Do This" paradigm that is reflected in the implementations of the objects. To send such a request, the object implementation must know (a) that the other object exists, (b) that it has particular behavior, and (c) that that behavior is the next thing to be done in solving the problem at hand. All three of these notions break encapsulation and increase the degree of coupling, but the most insidious, by far, is the third. One is designing objects around a particular application solution rather than as abstractions of the problem space entities’ intrinsic properties.
Note, in contrast, that when an event is generated as an announcement, nothing about its target needs to be defined in the state action. The assignment of the event to a target object can be done outside of the objects. Moreover, it is perfectly fair for it to be ignored completely (i.e., no implied sequence of processing). [The UML notation does not currently support this, but it would be a relatively minor syntactic enhancement. It certainly does not prevent the developer from thinking about it this way.]
As a practical matter, state machines facilitate unit testing a great deal. It is a simple thing to modify the event queue manager to simply log events generated by an action rather than processing them. This allows a single object to be easily tested without creating any other object in the system. That is, if one knows the initial state of the attributes, the stimulating event, the final state of the attributes, and the log of generated events, one can demonstrate correctness without other context.
The tools described above provide rather sophisticated model level simulation. The animation is at the model level and one has access to attribute values, breakpoints in the action language, and other features. In general it is a very sophisticated way to debug the basic logic of the application at a level of abstraction that is much higher than a source code debugger and that is unencumbered with implementation detail. I have been so spoiled by such debuggers that I now dread having to debug translation problems in something like the Visual C++ debugger.
In an incremental or evolutionary development environment this amounts to a quick way to test new additions without going to code level prototypes. One can also use the same test suite to test simulate the models and the final code; all that changes is the harness to introduce the test case data. Thus model simulation is a value that is not limited to the R-T/E niche.
So even if one holds the view that such features are only essential for the R-T/E environment, I believe a strong case can be made for their desirability in other developing environments.
H. S. Lahman wrote his first program on a plug board in ’57 when Basic Assembly Language was the silver bullet to solve the Software Crisis. That was such a character building experience that he spent the next decade as a geologist. He began writing software professionally in ’68. He spent the next two decades making all possible mistakes in software development in preparation for an epiphany resulting from a disturbing encounter with Bertrand Meyer’s OOSC.
He has extensive experience in economic modeling, business systems, operations research, real-time embedded systems, and dog track handicapping systems. For the last twenty years he has worked for the Assembly Test Division of Teradyne, Inc. working on various projects from modeling of electronic production lines to program generation software. He currently holds dual positions as the leader of the division’s Software Engineering Process Group and as Resident Curmudgeon.