Creating a Domain-Specific Modeling Language for an Existing Framework (转www.metacase.com)

来源:百度文库 编辑:神马文学网 时间:2024/04/30 03:43:33
Creating a Domain-Specific Modeling Language for an Existing Framework
Juha-Pekka Tolvanen, MetaCase,http://www.metacase.com
Domain-specific languages are a natural extension to code libraries and frameworks, making their use faster, easier and more consistent. This article describes how to define modeling languages on top of a library or a framework.
Developers usually agree that it does not make sense to write all code character by character. After coding some features they usually start to find similarities in the code and patterns that seem to repeat. For most developers it would then make sense to focus just on the unique functionality, the differences between the various features and products, rather than wasting time and effort re-implementing similar functionality again and again. Avoiding reinventing the wheel is good advice for a single developer, but even more so if colleagues too are implementing almost identical code.
One well-known practice is to move the common parts into a reusable library, or make them into a framework that is used as a basis for developing features or even whole products. Application developers can then get to know the framework and its architecture, find out its class hierarchies, learn its APIs and study the programming model required. The use of a framework is supported by documentation, application examples and guidelines. The bad news is that both research and practice show that for most developers it is often hard to use the framework and find the right component for the right task. There are usually also interdependencies that need to be understood, optional services and multiple alternative ways to provide the same functionality. It takes time to learn these and become enough of an expert on the framework to use it well.
Domain-Specific Modeling (DSM) provides a solution: it allows the framework developer to hide the details of the framework by raising the level of abstraction on which applications are built. The basic concepts of the framework are represented as the available kinds of objects in a new, domain-specific modeling language. Application developers can model the applications using these high-level concepts and generate working code that takes best advantage of the framework.
This automation is possible because both the modeling language and generators need only fit the requirements of the specific framework and application type. Automation minimizes routine work, makes many typical errors impossible and leads to a fundamentally faster development process. A domain-specific language can also cover the rules of the architecture and component use, guaranteeing that the framework is used correctly. Developers are thus guided to follow the best practices ?those the experienced framework developer knows work well.
DSM example
Before we look at how to create such a language, let抯 first illustrate domain-specific modeling with an example. For this purpose we use a well known domain: a digital wristwatch. Suppose your developers make the watch applications, such as a stopwatch or time setting and display. These applications are implemented on a top of a framework that provides common services like time calculations, showing an icon, ringing an alarm etc. The framework expects that a particular programming model is applied when making the application and calling the framework services.
Before any new features can be implemented, or existing ones modified, developers must design how they should work in the watch problem domain. This involves applying the terms and rules of the watch, such as buttons, alarms, display icons, states and user抯 actions. DSM applies these very same concepts directly in the modeling language.
An example of such a modeling language is illustrated in Figure 1. This modeling language is used to design and create various time related applications. The sample design model represents the time setting feature: the actions a user can make by pressing buttons, the display elements blinking, and the actions changing the time.

Figure 1. Modeling a time setting feature
Although the modeling language raises the level of abstraction and totally hides the details of the framework services from the developer, it is still clearly based on the underlying watch architecture and its framework. The design models describe the missing information that fills the variation points in the framework to build applications. With this language, developers can focus purely on the design of the application itself. Complete and working application code ?using the services of the framework ?is generated automatically.
Having a new modeling language, rather than just a model, allows us to make many different watch applications. Figure 2 illustrates a model of a world time application for the same watch framework, built using this same modeling language. This application is for a slightly more advanced watch, with an extra Mode button that allows the user to exit this application, or switch between editing the minutes or hours. The green display function at the top calculates the time to display: no longer just the clockTime, which is set in the Time application, but the clockTime plus a time zone offset which is set here.

Figure 2. World time application
Figure 3 specifies a basic stopwatch application. The model uses a couple of features of the modeling language which were not used in Figures 1 and 2: straight arithmetic calculations on time variables, and different display functions for different states. The display function for the Running state is the lower green object, and calculates the time to display as the current system time minus the time the stopwatch was started.

Figure 3. Design of a simple stopwatch application
These applications are not static: they can be developed further just as with source code. If we want an improvement, we simply edit the model. Consider for example Figure 4, which extends the stopwatch application to add lap time functionality, and turns an icon on and off to show when the stopwatch is running.

Figure 4. Design of an advanced stopwatch application
Let抯 look next how the modeling languages are defined. We抣l use the watch framework and its related modeling language as an example later in the article.
Steps for defining a DSM language
An incremental approach is often the best way to build a domain-specific language: first define a small part of the language, then model with it, define generators, make changes to the language, model some more etc. After getting it working for the main parts of the framework, we extend the language to cover other parts and finally the whole framework. This is also a natural approach for cases where the framework and supporting components are not yet completely available.
We can divide the process for defining modeling languages and their generators into four phases:
Identifying abstractions and how they work together Specifying the language concepts and their rules (metamodel) Creating the visual representation of the language (notation) Defining the generators for model checking, code, documentation, etc.
Usually the process proceeds in this order, iterating back as necessary. We focus here on the second step: specifying the modeling language. The first step, abstractions, is largely already set by the component framework or the library. The framework thus nicely bounds the task of creating the modeling language: we need only support what the developers of the framework have chosen to support. The task remaining for the language designer is thus to make the framework and its services easily available for application engineers.
When defining the modeling language we should not, however, think of models as directly visualizing code or framework API calls. Instead, we should seek higher-level abstractions than current programming languages offer: otherwise we will just be reinventing the flowcharts used to document C programs! In addition to the framework itself, sample applications built on it help us to see how the framework is expected to be used. The sample applications act also as good input and test cases when defining code generators.
Raising the level of abstraction with a language not only improves productivity but also enforces correct use of the framework. This is what every architect hopes for. Putting these rules and guidelines right in the language specification makes it impossible to specify applications or features that are not based on the framework. It also saves developers from having to refer constantly to in-house "design guidelines" documents ?which are probably out of date anyway.
Defining the modeling language
Language definition deals with specifying the modeling concepts and possible connections between them. A common starting point is to use existing ways of modeling that are used to talk about that kind of application, as opposed to its implementation. It is particularly important to avoid modeling languages like UML class diagrams, which are clearly implementation-focused and not at all specific to your problem domain.
In the simplest cases, when all possible functionality is already implemented by the framework, the variation can be specified as a list or tree of parameters. In our digital wristwatch example this approach could be used to capture the number of buttons and icons. Usually it is not this simple, since there are also interdependencies among the parameters: these call for at least wizards or decision tables to specify the possible variation.
Neither of these, however, is adequate as most variation is more complex than choosing among alternatives. For example rather than just choosing if the watch has an icon or not we need to know also the circumstances when the icons are used. Also, this approach can only work if all code is already written: it is no help in the most typical situations where we do not yet have the application code.
Parameter lists or feature selections are not adequate when variation goes beyond static configuration to deal with dynamic or behavioral code. These cases require models more complicated than a simple list or tree, whose every element is known in advance. The modelers must be able to create new elements and link them into new structures. We saw an example of such a modeling language in Figures 1?. Whereas a list or tree with 5 Boolean choices will allow at most 32 variants, with such a language there is no limit to the number of variants and indeed new applications that can be built.
Using known models of computation
Since the watch applications are state based and event-driven we could take state machines as the basis for our language definition. This model of computation can then be enriched with the domain concepts and rules.
Figure 5 illustrates the basic definition (metamodel) of a state machine. The metamodel describes that each state machine can have one Start object initiating the state machine with a transition leading to a state. The State object has two properties: its name that is unique inside the model and a description text. A State can be connected with a Transition to another State or to a Stop object (both grouped together here into one "Objects in binding" group, since they are both bound in the same role in this Transition).

Figure 5. Basic metamodel of a state machine
A language also has rules that constrain how models can be made and modified, to ensure model correctness. These rules are specified in the metamodel along with the language concepts. For example in state machines, as specified also in the metamodel in Figure 5, a Start state can have no incoming transitions, and in each state machine there can only be one start state.
Extended languages with domain-specific concepts and rules
We can now enrich our modeling language with watch-specific concepts and rules. This means mapping the domain concepts and framework services to modeling elements. Some of them can be seen as the objects in the modeling language while others could be better captured as object properties, relationships, sub-models or links to models in other languages.
For example, in a watch each application state can have a display function that calculates what time to show: the current time for a Time application, the time plus a time zone offset for a World Time application etc. Another watch characteristic is that rather than a general concept of event, transitions in a watch are caused by pressing buttons.
An explicit concept of Button is thus added to the modeling language specification. Figure 6 shows an extended metamodel where State has two additional properties. A Display that is shown when a state is active and a Time unit (hour, min, sec, hundredth) to indicate if certain part of the time is blinking ?e.g. when that part of the time is being edited.
Transitions are modified to describe watch applications more precisely: users pressing buttons of the watch trigger transitions and during a transition an action can be performed. Both of these are not mandatory: the metamodel in Figure 6 has role cardinality 0,1 in transitions related to Action and Button concept.

Figure 6. Extended state machine with domain-specific transitions
In a similar manner, the other domain concepts and framework services can be added to the language specification. For example the framework gives services to use icons: they can be turned on or off. This is specified in the metamodel (Figure 7) with a relationship between an action and an icon.
The relationship also has a property to specify which icon service is used (on, off). Another framework service is ringing an Alarm to trigger a special transition to a State. The metamodel in Figure 7 also includes other framework services like setting and calculating time via variables, describing how the DisplayFn calculates the time and how the Alarm is set.

Figure 7. Extended state machine with watch-specific actions
This language specification is formal so it can be instantiated and used as a modeling language. Actually all models illustrated in Figures 1-4 are instances of the metamodel. Design models are therefore always based on the concepts and rules set in the metamodel.
Notation makes a language visible
A pure metamodel, however, is not enough as the language also needs to have notation and visual presentation, usually a diagram, but sometimes a matrix, table, or plain text. The best source for the notation is the application domain. Using familiar symbols from the application domain makes the models easier to read, remember, understand and check. One good way is to look at designs that developers draw when they have complete freedom, e.g. on whiteboards or in presentation slides. Here they can apply the concepts and visualizations that they find most suitable for describing their applications. DSM should mimic and apply these concepts and notations in the modeling language.
In our sample language we have used the notation of basic state machine and extended it with domain concepts like buttons, alarms and icons (see Figures 1-4). We also use coloring to categorize different areas of concern according to the MVC (model-view-controller) architecture: green for the views (icons, displays), red for control path (buttons, triggers, alarms), and black for the underlying data model (time manipulation). Dark red is used for elements that have aspects of both data model and control.
Running the language definition
Once a metamodel, or a part of it, is defined, it should be tested with sample applications. For this purpose we need tools that provide editors for modeling with the defined languages. Best is if the tool for language definition and language use is the same. This makes language definition agile: we can modify the language (metamodel) while using it.
Tools should not, however, be restricted only to editor creation based on metamodels. They should guide in defining the metamodel and recognize if the language definition is incomplete (i.e. illegal in some way) and if a particular language can be integrated with other languages. Tools should also allow sharing language versions among developers and updating existing models based on language changes. This is crucial in practice since companies simply cannot afford to wait for weeks or months before getting a new language version ?and then having to update previously made models by hand.
Fortunately, tools are already available that provide good support for this: whilst a quick prototype editor could be built in a man-year or even man-months, to make a usable, reliable tool that developers will accept requires a far greater amount of work. Using a tool that already offers these features, an experienced developer in a company can thus tailor design languages and generators to a specific domain. Other developers can then design with the resulting DSM languages and tools, and generate actual products from their models.
Concluding remarks
Domain-specific languages are a natural extension to frameworks, making them better able to be used. Domain-specific languages work well with frameworks since both focus on a specific application area and on creating similar applications. The investment you need to make first is to build the domain language and related generators. This investment has been found small compared to the payback. For example, the implementation of the watch modeling language as presented above took two days. An additional five days were needed to design the architecture and assemble the framework Java components (without any prior Java experience), and one day to implement the code generator. This development effort also produced the first working watch model. From then on, new watch models could be implemented and tested in less than twenty minutes.
In addition to greatly speeding up development, domain-specific languages on top of frameworks may also offer other benefits:
Routine work in framework use can be minimized The complexity of the framework can be hidden Rules embedded into the language can better guarantee that the architecture is followed The resulting applications will be of better quality, since they are built based on the rules set by the experienced developers who created the languages   Changes in the framework can be made more easily available by changing the language in which applications are described. Applications using the newer framework can then simply be regenerated from existing models.
These benefits are not easily, if at all, available for developers using traditional manual programming practices: reading textual documentation about the framework, browsing components in a library, or trying to follow a (hopefully) shared understanding of a common architecture. In short, if you are using or developing a framework you should consider defining a modeling language on top of it. It bears mentioning that defining a domain-specific modeling language is not only an interesting challenge梚t is also a lot of fun!
The sample language and related code generators for Java and C are available from jpt@metacase.com upon request.