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 by showing how to build a DSM model generator to create digital watch applications. In Part 2 they show the rules and notations necessary, various use scenarios, and a possible watch metamodel.
Having a language that could specify watch applications brought us back to our starting point at the beginning of Part 1 in this series: Secio wanted to compose a product family of watches out of watch applications. One part of each watch would thus clearly be a list of the applications it contained, for example, Time, Stopwatch, and Alarm.
A watch would not just be a jumble of applications though: the user would cycle through the applications in a certain order. At first, we thought this would be represented as a collection property, to which the modeler could add the desired watch applications. The idea of the cycle, however, gave us the idea of representing this graphically. Since we had already considered the possibility of having layered state machines for the watch application — a 'state' in an application could decompose to a sub-application — we hit on the idea of using the same watch application modeling language to specify application composition.
This approach brought several benefits. First, it reduced the number of concepts the modeler would have to learn. Second, it gave the modeler more freedom in deciding how many levels the models should be divided up into: if a watch had only one application, for example a stopwatch, the watch could point directly to the stopwatch, rather than having to specify an intermediate level with a single stopwatch application.
Similarly, if a given application became too large for a single graph, or if parts of an application could be reused in other applications, they could be separated out into their own sub-applications on as many levels as seemed appropriate. Third, it allowed the possibility of more freedom at the application composition level: rather than restricting all watches to always move between their applications in a fixed cycle, the choice of which application to go to next could be as complicated as the modeler desired. An example model is shown in Figure 4 .
Figure 4: logical watch as a cycle of applications
While one part of a member of the product family of watches would thus specify the behavior of the watch, a second part was needed to specify the physical details of the watch. Mostly these would be the domain of an industrial designer, but the behavioral part would also need some of that information. In particular, if we wanted to generate a Java applet as a watch emulator, we would need to know three things: the buttons on the watch, the icons it had, and the number of digit pairs it could display.
The first two could in theory have been found by trawling through the set of applications and including all referenced buttons and icons, but this was not how we wanted things to work. We wanted a stopwatch application, say, that used 'up', 'down', and 'mode', to be usable in a watch without a mode button, for example one containing only the stopwatch application. Similarly, rather than have an error if an application tried to turn on an icon that was not present in the physical watch, we would simply do nothing at that point. This made the watch applications more reusable, and allowed the application modeler to concentrate on the application itself, without having to know beforehand the exact details of the physical watches in which it would be used.
We decided to call a member of the product family a watch 'model' — 'model' being used in the sense of 'a Corvette is a car model', rather than 'a graphical model of a car'. Each watch model had a logical watch, which pointed to the graph showing the cycle through the watch’s applications, and a display corresponding to the relevant parts of the physical watch body: icons, digit pair zones, and buttons. The logical watches and displays would thus form components, and each might be used in more than one full watch model.
As these concepts were different from those used in the watch application modeling language, it was clear that we actually had a new modeling language here. We decided to make it graphically rather verbose, for pedagogical rather than practical reasons. Each watch model (rounded rectangles with a light green fill) showed the display and logical watch it used, and the sets of possible logical watches and displays were also shown in the same diagram (Figure 5 ).
Figure 5: Watch family diagram
Another possibility here would have been to use a matrix representation rather than a graphical diagram. In models shown as matrices, the axes contain objects and each cell contains the relationship (if any) between the corresponding objects. The logical watches could thus be listed down the left side of the matrix and the displays across the top. The cell at the intersection between a given logical watch and display would then represent a possible watch model composed of those parts. The relationship’s name would be the name of the watch model and would be shown in the cell. Figure 6 shows how this would provide a useful visual overview of which potential combinations of logical watches and displays had been used, how often each logical watch and each display had been used, and which potential combinations remained as possible openings for new products. The matrix format would also collect the watch models, logical watches, and displays together into their own areas, without the rather clumsy group symbol of the graphical diagram.
Figure 6: Watch family as a matrix
Rules of watch applications
The main need for rules andconstraints in the watch applications, as in most DSM languages, was inthe relationships. The main relationship was the transition betweenstates, triggered by a button and causing various kinds of actions. Thiswas a clear case of an n -ary relationship: one relationship connecting several objects.
Onequestion was how much we wanted to allow the aggregation of severaltransitions into one composite transition. For instance, it would havebeen possible to allow the same transition to come from many states: forexample, a transition to the 'stop' state, triggered by the modebutton, would often be appropriate for several states. Similarly, wecould have allowed the same transition to be triggered by multiplebuttons (“Press any button to stop alarm ringing”), and a transitioncould clearly trigger multiple actions (e.g., setting an alarm andturning on the alarm icon).
However, as the transition wouldgenerally link four objects anyway — quite a large number — it wasthought better to allow one From state, an optional Event from a button,an optional Action, and one To state. A small Action object would serveas a placeholder for multiple actions, which would be connected to itby relationships. The Event role from the button object would beoptional: some state transitions could happen without any button press.
Thetransition from the Start state to the first state would be requirednot to have a button: an application is never actually in the Startstate, which simply marks the entrance point. Additionally, there was aconstraint that a Start state could have only one transition leaving it,and similarly there should only be one Start state in each watchapplication. Interestingly, the Stop state behaves differently: therecan be many Stop states, each with possibly many transitions arriving atit. While an application is never actually in a Stop state, there is noambiguity resulting from this situation, in contrast with the case ofStart states.
The other main kinds of relationship whose rulesrequired thought were the time operations. We decided to implement theseas Plus, Minus, and Set roles. The contents of the variables pointed toby the Plus and Minus roles are added and subtracted appropriately, andthe result of the operation is stored in the variable pointed to by theSet role. There could thus be one Set role, and zero to many Plus orMinus roles. As this left the unsatisfactory possibility of arelationship with one Set role and no other roles, we decided that thereshould also be a Get role, which would point to the first variable termin the operation. Thus, for something like stopTime = sysTime – startTime , stopTime would be in a Set role, sysTime in a Get role, and startTime in a Minus role.
Asit turned out, this was a good decision for code generation: the firstterm in a calculation is not generally preceded by a plus or minus sign.It was, however, a slightly poor decision in terms of modeling allpossible calculations: for example, stopTime = – startTime. Interestingly, there has not yet been a need for such a calculation.Still, perhaps a better solution would be to replace the Get, Plus, andMinus roles with a single Get role with a property specifying whether itwas Minus or Plus.
Notation for watch applications
Tomodel an application in sufficient detail to allow full code generationgenerally requires several different kinds of information. Ingeneral-purpose modeling languages each kind of information would beseparated into its own language. A clear problem with this approach isthat it is hard to get a complete view of the application, as it issplit over several diagrams.
Such an approach also requires afair amount of duplication, as objects (or references to them) must bereused in several different diagrams, in order to provide the fullinformation about the object from all viewpoints. This leads to anotherproblem: the difficulty of keeping all the diagrams synchronized.
Toavoid these problems, a domain-specific modeling language oftenincludes several different viewpoints within one language. Because thelanguage constructs needed for each viewpoint are only a domain-specificsubset of those needed for that viewpoint in a generic modelinglanguage, the resulting language remains of a manageable size. It doeshowever contain a variety of different kinds of information, and one wayto use notation is to help separate the different kinds of informationvisually.
The watch application modeling language contains threemain types of information, corresponding to the three parts of MVC thatSecio had decided to use. The model part represents the underlying dataon which the application acts, in this case time variables.
These parts of the model are shown in black or gray, with gray being used for read-only pseudovariables, such as sysTime ,by analogy with graying out a field in a user interface to show it isread-only. The 'view' part represents the output of the application tothe user, that is, the visible parts of the application that change asit runs. These parts of the model are shown in green: the icons anddisplay functions.
The controller part represents the input tothe application by the user, in this case the buttons. The buttons, andthe lines from them to the transitions, are shown in red in the models.Since the behavior of the buttons depends on the state the applicationis in, we decided to make the states and transitions a dark red: partcontroller, part model. As each state showed the name of the displayfunction it used, which was part of the view information, that name wasshown in green.
The symbols themselves were drawn as vectorgraphics, aiming more for simplicity than beauty. As a state has noclear visual presence in a physical watch, its symbol was just arectangle containing its name. If its display function was not thedefault for that graph, that was shown in green in a smaller font at thebottom right. If one of the time units was blinking in that state, theabbreviation of the time unit was shown with four short rays pointingoutward.
For the Start and Stop states, we chose the familiarrepresentations of a filled circle and a filled circle within a hollowcircle. Buttons clearly had a physical counterpart, but also a fairlystandard pictogram of the edges of a stylized three-dimensional button.As the physical counterparts on actual watches tend to be very small, weused the pictogram. For the alarm we drew a yellow bell shape, using asmaller red version of it as a symbol for the special alarm transitionrelationship. Time variables lacked a clear visible counterpart, butwere a familiar concept from several generic modeling languages; in theend, we chose the data store symbol from Data Flow Diagrams.
Animportant part of any modeling language is the flow of control orinformation, which is normally indicated by arrowheads on the rolelines. We thus placed arrows on the lines from a button to thetransition it triggered, from the transition to the action it executed,and from the transition to the state it ended up in.
As the lastof these was the most important element of flow in the whole modelinglanguage, it was shown most visibly, with a dark red-filled arrowhead.The final piece of flow was in the time operations, where the value ofthe calculation is assigned to a time variable: this was shown with ahollow black arrowhead.
As always, these decisions were abalancing act, trying to make the meaning of the various parts of themodel clear without straying into a graphical melee where every parttries to scream its own importance. If the direction of information flowalong a role line was obvious from other context, no arrowhead wasused. This was the case for actions turning icons on or off, the get,plus, and minus roles in time calculations, and the role connecting adisplay function to its calculation. The direction for the button andaction roles of the transition relationship could also be implied, butsmall open arrows were added to make clear the part played by each ofthe roles in this n -ary relationship.
Evaluating the models
Thethree models we have looked at earlier reflect the three levels foundin most of the watch applications. The top level is a single diagram inthe watch family language (e.g., 2006 models shown in Figure 5 ). Each logical watch there is decomposed into a simple watch application diagram (e.g., TASTW shown in Figure 4 )at the middle level showing how it is composed from variousapplications, represented as a cycle of states. Each state there isdecomposed into its own watch application diagram (e.g., Stopwatch shownin Figure 3 in Part 1) containing the behavior of that application.
The AlarmClock application in Figure 7 showssome aspects not present in the Stopwatch. When the application starts,we move straight to the 'show' state, where (as indeed in all states inthis application) the display function simply shows the alarmTime variable. Pressing the set button takes us to the EditHours state, where the hours are flashing on the display. Pressing the up or down buttons there rolls the hours value of alarmTime up or down. Note that the thick dark red role from EditHours returns back to EditHours along the same path, ending in an arrowhead, making the role leaving EditHours invisible. Another way to draw this would be as a circularrelationship, but as these are so common, it was thought that themodeler would prefer this shorthand: all four roles of the transitionare present; the 'from' role is just not particularly visible. PressingMode in EditHours takes us to the similar EditMinutes , and vice versa.
Figure 7: AlarmClock application
PressingSet in either Edit state takes us back to the Show state, carrying outthe Action immediately below and to the right of Show on the way. Thisturns on the alarm icon and sets the AlarmClock alarm to ring after awhile, calculated as alarmTime minus clockTime. The alarm is set to besensitive to changes in local time (indicated by the clock face in itssymbol). In the Show state we can press Down to unset the alarm and turnoff the icon. Assuming we leave the alarm to ring, when it rings itfollows the transition with the small red bell symbol from the Alarmsymbol to the AlarmRang state. From there, without any buttons beingpressed, we move directly to the Show state, turning off the alarm iconon the way. From the Show state, pressing Mode will exit theapplication.
Watch model use scenarios
An importantfeature of the watch modeling language is its support for reuse ofmodels and model elements. This is present at all levels, and acrossdifferent watch families, logical watches, and applications. Reuse heremeans that two or more parts of a model refer to the same model element.Since this is a reference rather than a copy, updates to the reusedelement will instantly and automatically be in effect in all partsreferring to that element.
Reusing rather than copying thusreduces the amount of work needed for maintenance, reduces the risk ofcorrections only being made to one copy, and makes the amount of data inthe models smaller and thus easier to understand.
At the familylevel, the same logical watch can be reused in several watch models: forexample, the Simple application for viewing and setting the time isused in both Celestron and Celestra watches. Similarly, the same displaycan be reused in several watch models: for example, X334 is used inboth Ace and Delicia. The buttons and Icons used in displays are eachdefined only once, and reused in each display that has them.
Atthe middle level, each reference to a lower-level watch application isclearly a case of reuse. Since the name of a state in the middle-leveldiagram is the same as the name of the watch application it decomposesto, a state for a given watch application would be essentially identicalbetween different logical watches. We thus reuse the whole Stopwatchstate from the TASTW logical watch in the TAST and TST logical watches.Any improvements to the Stopwatch will thus instantly be a part of allwatch models that use these logical watches.
At the bottom level,the buttons and Icons are reused from their definitions at the familylevel. The global pseudovariables such as sysTime are also defined only once and reused in many watch applications. Thisis in contrast with the true variables and the alarms, which are definedlocal to a given watch application. They may be reused within that oneapplication, for instance first in a calculation that sets the value,and later in a calculation that reads the value.
Looking at the watch applications, both Timer and Stopwatch contain a variable called stopTime , but these refer to different things: a timer’s stopTime is the time of day when its alarm will ring, whereas a Stopwatch’s stopTime is the amount of time that had elapsed when the stopwatch was stopped.Two variables may thus share a name, but because of the scoping they aretwo different objects in the models, and two different variables in therunning watch applications. The scoping is implicit in the modelinglanguage, and made explicit in the code generation.
The samedisplay function will normally be referred to by many states. In thesimplest case, all states in a watch application will use its singledisplay function. To make this as easy as possible for the modeler, sucha display function can be given a blank name, and then each state thatdoes not define a display function will use this default displayfunction.
In the watch application diagrams we also takeadvantage of another kind of reuse: reuse by relationship. The clearestcase is the reuse of actions: an action can be reused by more than onetransition, simply by having each transition link to it with its actionrole line. Another case, so obvious that it might be overlooked, is thereuse of states, which are reused within a single application in thesense that they may be reached by more than one path.
Since insome cases trying to link disparate objects together can lead to avisual spaghetti of crisscrossing lines, it is also possible to makerepresentational duplicates of objects in a diagram. For instance, inthe Time application in Figure 8 below, it is possible to return from the EditMinutes , EditHours , and EditSeconds states to the basic Show state, via an ExitEdit state that makes the edited time persistent. Rather than try to drawthree relationships all arcing back to the start, a duplicate of the ExitEdit state is placed conveniently close to each of the Edit states, allowing a single short transition for each.
Figure 8: Reuse in the Time application
No transitions leave these duplicates, but the model knows they are all the same as the main ExitEdit state, so the transitions that leave there are applicable in theduplicates too. Conceptually we can think that we jump straight from aduplicate to the main ExitEdit state, but in fact there is no difference, and no one representation of the ExitEdit state is pre-eminent. The ExitEdit state simply has the sum of all of the transitions that enter or leave any of its representations in this graph.
Sincehaving real reusable objects like this may be too radical for moreconservative users, it would also be possible to have a new object type“state Reference,” and use that for the other representations of ExitEdit .Of course, if the users are that conservative, you should just let themcode the whole thing in assembly language and forbid the use ofsubroutines—or simply give them a nice analog watch as a retirementgift.
The Time application is also an example of a possible typeof reuse that has not been implemented in this modeling language. Thereis clearly something similar in the states, buttons, transitions, andactions for EditHours, EditMinutes , and EditSecond s.Each edit state selects one time unit of the same time variable, andthe Up and Down buttons roll this time unit up and down.
A similar pattern is seen in other applications where a time variable is being set by the user, that is, Alarm and Timer .There, however, only the hours and minutes are being set, and there aredifferences between the three cases as to the display function used.Because of these differences, it was not possible to build asub-application that each of these three applications could use.
Wedid, however, consider a new modeling language concept, TimeEditor,which would have stood for a set of related states like this. ATimeEditor instance would have specified the variable to be edited, thefirst and last time units to be edited, and the display function to beused. This information would have been sufficient to cover thevariability between the three cases, and we could have built a domainframework function to handle the behavior, and made the generator createa simple call to that function.
With only three cases, however,and none of these being impossibly complicated with the existingmodeling language, we decided to leave the extension until later. Ratherthan creating a new concept for this particular kind of repetition inthe models, it might prove to be better to identify several kinds ofrepeated patterns and come up with a more general way to specifyparameters to a sub-application.
In the long run, this approachwould probably have allowed more expressive power with a smaller set ofconcepts. For completeness, Figure 9 shows the full metamodel for watch applications.
Figure 9: Watch application metamodel
Juha-PekkaTolvanen () has been involved in domain-specificlanguages, code generators and related tools since 1991. He works forMetaCase and has acted as a consultant world-wide for modeling languageand code generator development. Juha-Pekka holds a Ph.D. in computerscience from the University of Jyväskylä, Finland.
Steven Kelly is chief technical officer of Metacase and cofounderof the DSM Forum. He is architect and lead developer of MetaEdit+,Metacase’s DSM tool.
Used with permission from Wiley-IEEEComputer Society Press, Copyright 2014, this article was excerpted from Domain-Specific Modeling: Enabling Full Code Generation , by StevenKelly and Juha-Pekka Tolvanen.