Building a digital watch using DSM: Part 1 – Creating a watch modeling language
Editor’s Note: Excerpted from their book Domain Specific Modeling: Enabling Full Code Generation, the authors use a fictitious digital watch company to demonstrate the advantages of domain specific modeling and describe how to build a DSM model generator to create digital watch applications. Part 1 focuses on the initial steps in creating an application specific watch modeling language.
In an earlier article, we used a fictitious manufacturer of digital wristwatches called ‘Secio’ to demonstrate the principles of Domain Specific Modeling.
The actual process of creating the modeling language has been made as natural as possible, following the practices that have become established in real projects. The artificial background of the example is perhaps most clearly seen in its limited scale. Conversely, this small size is its strength as a pedagogical tool: small enough to be understood in a relatively short time but large enough to provide realistic insights into every aspect of DSM.
Secio has noticed that producing the software for each watch by hand is becoming a significant bottleneck, as consumers demand functionality beyond simple setting and display of the time. It has also realized that different consumers want different functionality and have different requirements for ease of use versus extensive functionality, physical compactness versus amount of information displayed, and so on.
Therefore, Secio has decided to build its range of watches for next year as a product family. There will be different watches for different consumer types and price points, but the watches will be able to share common parts of the software. Such common parts will include basic framework such as the ability to show numbers on an LCD display, as well as individual applications such as a stopwatch or a countdown timer. The basic framework components will be present in all watches, and either already exist or will be coded by hand. All the individual applications will not be present in all watches, yet it is hoped that later addition of an existing application to an existing watch could be a simple operation.
For a variety of reasons—some technical, some political—Secio has decided to try to create a DSM language for building the watch applications. The main objectives are to reduce the development time and complexity of building watch applications. In particular, the current watch software is one large piece of code with little separation of concerns.
Being able to separate out different parts of the code would improve reuse possibilities, and also allow the developer to concentrate on one area at a time, thereby reducing errors. The separation of behavior into Model (operations on time values), View (display of the time and icons), and Controller (user input on the watch buttons) will form one weapon to divide and conquer the mass of code.
The main weapons though will be the higher level of abstraction and the close fit of the modeling language with the problem domain. Developers will be able to concentrate on the application behavior as perceived by the end user, rather than on the lower-level details of how to implement that behavior.
Behind the stage dressing of Secio’s objectives, the main objective was to provide a fully worked example of DSM. An important component in convincing people that DSM works is making the code generated from example models actually run. This presents several challenges not normally found in a real-world case of DSM. First, the most significant challenge is that the users must have a compiler installed for the language of the generated code.
These days, compilers tend to come as part of a full IDE, requiring a large investment of time, disk space, and possibly money to set up. Second, the user must have a runtime environment compatible with the format produced by the compiler. For both compiler and environment, there may also be various settings such as paths that are specific to each PC, and these settings will normally need to be synchronized with the generated code. Finally, the users will have a variety of different backgrounds, and varying experience with different languages, IDEs, and programming styles.
The need to make an application that would compile and run on many different platforms pointed away from C, the natural language choice for an embedded system. While core watch behavior in C would have been platform independent, the GUI widgets and graphics library in C tend to be platform specific. Also, the majority of commonly used C compilers and IDEs that users were likely to have are commercial products. The installation and use of the freely available compilers are generally sufficiently difficult to deter someone whose only motive would be to see an example application.
While other languages with freely available compilers and runtime environments existed, the mainstream choice—and hence most likely to already exist on users’ computers—was Java. The resulting Java applications would also be small enough to be easily passed to other users, and availability of at least a Java runtime environment on a PC is virtually guaranteed. While Java was not the current language of choice for embedded development, it was originally developed for such devices and would be familiar to a large number of users.
Creating a watch modeling language
The development of the watch modeling language was carried out by Steven Kelly and Risto Pohjonen of MetaCase over a couple of weeks. As this was an example rather than a real-world case, there was no outside customer: as both developers had owned digital watches in the 1980s, they felt qualified to play the role of domain expert.
This distinguishes this from the other example cases, where the authors did not have sufficient domain knowledge at the start of the project to create the language on their own. This case thus brings the authors’ positions closer to those of readers thinking about their own application domains, and gives us a good opportunity to examine the thought processes of a domain expert. As will be seen, creating a DSM language is largely a question of determining what facts need to be recorded about each application in that domain, and where in the modeling language to store these facts.
The total time spent was approximately 10 man-days, including the modeling language, a full set of models, the generator, and the domain framework. Since this was the first Java project for either developer, the time also included learning the language, its libraries, and development environment. Over the subsequent years, a few more days have been spent on upgrading the framework and generator to work on other desktop platforms, cope with later Java versions, produce watches that run on mobile phones, and add support for model-level tracing of a running watch application.
None of these changes has required changing the models, and the result has always been fully running applications, identical in behavior but with environment-related differences in appearance and sometimes in code. Following is the analysis of the domain and the development of the language, in chronological order.
Reusing watch applications
It was soon evident that it would be good to break a watch down into its component applications: time display, a stopwatch, an alarm clock, and so on. Beyond providing a sensible modularization of the whole watch, this would also allow a watch application to be reused in different watches. Since these watches would have different physical designs—displays, buttons, and so on—there was a need for some decoupling of references to these physical elements in the models.
For instance, if a model of a watch application wanted to specify that a certain operation was caused by a certain button, we had to answer questions about whether that button was available in the given physical watch, whether it would be named in the same way or have the same semantics, and what to do if no such button existed.
Thinking about this issue prompted the idea of explicitly modeling a whole group of watches as a family. This would be a separate level of modeling, probably with its own modeling language. Often in DSM such a level exists, but it is not always explicitly modeled: it is enough to simply have several products—watches in this case—each built from its own set of models.
However, making reuse of models explicit generally makes it easier to maintain them and concretely shows the dependencies of the various components. For instance, a change in the Alarm application to require an extra button would affect which physical designs of watch the Alarm could be used in. If Alarm had simply been reused, this effect might not have been obvious. If, however, there were a top-level model showing each member of the watch product family, which physical watch body it used, and which watch applications it contained, the effects of that change would be easier to see.
If there was a need for a mapping between the buttons mentioned in a watch application model and those present in a physical watch, there was also the question of how to model this mapping. Would the mapping be included as part of the top-level family model, or would it be a separate kind of model between the top-level family model and the actual watch applications?
Further, who would be responsible for building these mappings: the watch application designer, the family designer, the designer of a particular watch model, or somebody else? Similarly, would a separate mapping be required for each pair of a watch application model, for example a stopwatch, with a physical watch body, or could one mapping be reused over many watch applications or physical watch bodies?
The question of the mapping was thus difficult in both technical and organizational terms, and also hard to decide at this early stage. While we did not even have modeling languages for watch applications and watch families, it seemed unrealistic to expect to pick a good solution to a problem that would probably only become apparent once several families had been built.
We thus decided to go with the simplest thing that could possibly work: there would be a limited number of named buttons, initially just Up, Down, Mode, and Set. Each physical watch body could contain any combination of these buttons, and similarly each watch application could refer to them directly.
While less flexible than a different mapping for each watch, this had a good chance of working well for both watch modelers and users. Both groups would prefer a consistent semantics for the buttons: if in one watch application Up was used to increase a time value, and in another the same function was achieved using Set, learning to use the watch would be rather difficult!
Now we had a fair idea of the contents and division of labor of the two modeling languages. The family model would contain a number of watches, and each watch would be composed there from a number of watch applications and a physical watch body. A physical watch body would specify a number of buttons. These buttons would also play a key role in the definition of the watch applications: different buttons would cause different actions in different applications.