Use Java to implement specific internal domain languages

Source: Internet
Author: User

Http://bbs.dev.ccidnet.com/read.php? Tid = 586331

 

Introduction

A domain-specific language (DSL) is usually defined as a computer language that specifically targets certain special problems. It is not intended to solve non-domain problems. The formal research on DSL has been going on for many years until recently, when programmers try to solve their problems in the most readable and concise way, internal DSL is accidentally written into the program. Recently, with the advent of Ruby and some other dynamic languages, programmers are becoming increasingly interested in DSL. These loose Languages provide some method for DSL to allow the least syntax and most direct performance to a particular language. However, abandoning compilers and using the most powerful modern integrated development environment like Eclipse is undoubtedly a major disadvantage of this method. However, the author finally successfully finds a compromise solution between the two methods, and they will prove that this compromise method is not only possible, it is also helpful for designing APIs in DSL-oriented mode using a structured language such as Java. This article describes how to use the Java language to write domain-specific languages, and suggests some patterns that can be used to build DSL languages.

Is Java suitable for creating internal domain-specific languages?

Before examining whether the Java language can be used as a tool to create a DSL, we must first introduce the concept of "Internal DSL. An internal DSL is created in the main programming language of the application software and has no requirements for the Creation (and maintenance) of the custom compiler and parser. Martin Fowler has compiled a large number of DSL types, both internal and external. He has written some good examples for each type. However, when using a language like Java to create a DSL, he just takes it over.

It is also important to note that it is difficult to distinguish between DSL and API. In the internal DSL example, they are essentially the same. When we think of DSL, we are actually using the main programming language to create a readable API within a limited range. "Internal DSL" is almost synonymous with a very readable API created for specific problems in a specific field.

Any internal DSL is limited by the grammar structure of its basic language. For example, when Java is used, the use of large arc, small arc, and Semicolon is required, in addition, the lack of closures and metaprogramming may lead to longer DSL than dynamic language creation.

But on the bright side, by using Java, we can also use a powerful and mature integrated development environment similar to eclipse and intellij idea, thanks to the features of these integrated development environments such as "auto-complete", automatic reconstruction, and debug, DSL creation, use, and maintenance are easier. In addition, some new features in Java 5 (such as generic, varargs, and static imports) can help us create APIs that are more concise than any previous version of any language.

Generally, DSL written in Java does not create a language that can be used by business users. It is a language that business users may feel easy to read. At the same time, from the programmer's perspective, it is also a language that provides direct reading and writing. Compared with external DSL or DSL compiled by dynamic languages, the compiler can enhance the error correction capability and identify inappropriate use, ruby or Pearl will "happily accept" the absurd input and fail at runtime. This can greatly reduce lengthy tests and greatly improve the quality of applications. However, using the compiler to improve quality is an art. At present, many programmers are trying their best to satisfy the compiler rather than using it to create a language that uses syntax to enhance semantics.
Using Java to create DSL has advantages and disadvantages. In the end, your business needs and the environment in which you work will determine whether this choice is correct or not.

Use Java as the internal DSL Platform

Dynamic SQL construction is a good example. It has built a DSL to suit the SQL field and gained notable advantages.
The traditional Java code using SQL is generally similar:

String SQL = "select ID, name" +
"From MERs C, order o" +
"Where" +
"C. Since> = sysdate-30 and" +
"Sum (O. Total)>" + significanttotal + "and" +
"C. ID = O. customer_id and" +
"Nvl (C. Status, 'dropped ')! = 'Dropped '";

Another expression extracted from the author's recent work system is:

Table C = Customer. Alias ();
Table o = order. Alias ();
Clause recent = C. Since. laterthan (daysearlier (30 ));
Clause hassignificantorders = O. totat. sum (). isabove (significanttotal );
Clause ordersmatch = C. Id. Matches (O. customer_id );
Clause activecustomer = C. Status. isnotnullor ("dropped ");
String SQL = customers. Where (recent. And (hassignificantorders)
. And (ordersmatch)
. And (activecustomer)
. Select (C. ID, C. Name)
. SQL ();

This DSL version has several advantages. The latter can transparently adapt to the method of using preparedstatement-the version of string spelling SQL requires a lot of modifications to adapt to the method of using bundled variables. If the reference is incorrect or an integer variable is passed to the date column for comparison, the latter version cannot be compiled at all. Code "nvl (Foo, 'x ')! = 'X' is a special form of Oracle SQL, which is hard to understand by non-Oracle SQL programmers or those who are not familiar with SQL. For example, in the SQL Server dialect, the Code should express "(FOO is null or foo! = 'X ')". But by replacing this code with isnotnullor (rejectedvalue), which is easier to understand and more similar to the human language, the code is obviously more readable and the system is protected, this avoids the need to modify the initial code implementation in the future to take advantage of the facilities of another database vendor.

Use Java to create an internal DSL

The best way to create a DSL is to first prototype the required API and then implement it under the constraints of the basic language. The implementation of DSL will involve continuous testing to ensure that our development is in the correct direction. This "Prototype-test" method is advocated by the test-driven development model.

When using Java to create a DSL, we may want to create a DSL through a coherent interface (fluent interface. Coherent interfaces provide an easy-to-read description of the domain issues we want to model. Method chaining is used to implement a coherent interface ). However, it is important that the method link itself is insufficient to create a DSL. A good example is Java's stringbuilder. Its Method "APPEND" always returns an instance of the same stringbuilder. Here is an example:

Stringbuilder B = new stringbuilder ();
B. append ("Hello. My name is ")
. Append (name)
. Append ("and my age is ")
. Append (AGE );

This example does not solve any domain-specific problems.

In addition to method links, static factory methods and import are good assistants for creating concise and easy-to-read DSL. In the following sections, we will discuss these technologies in more detail.

1. method chaining)

There are two ways to create a DSL by using method links, both of which involve the return values of the methods in the link. Our choice is to return this or return an intermediate object, which is determined by what we are trying to achieve.

1.1. Return this

When we can call the Link Method in the following ways, we usually return this:

◆ Optional.
◆ Call in any order.
◆ Can be called any number of times.

We found two use cases using this method:

1. Related Object behavior links.
2. Simple construction/configuration of an object.

1.1.1. Related Object behavior links

Many times, we only attempt to reduce unnecessary text in the Code by simulating the dispatch of "multi-information" (or multi-method call) link the object method to the same object. The following code snippet shows an API used to test the swing GUI. The test confirms that if a user attempts to log on to the system without entering her password, an error message is displayed.

Dialogfixture dialog = new dialogfixture (New logindialog ());
Dialog. Show ();
Dialog. Maximize ();
Textcomponentfixture usernametextbox = dialog. Textbox ("username ");
Usernametextbox. Clear ();
Usernametextbox. Enter ("Leia. Organa ");
Dialog. ComboBox ("role"). Select ("Rebel ");
Optionpanefixture errordialog = dialog. optionpane ();
Errordialog. requireerror ();
Errordialog. requiremessage ("enter your password ");

Although the Code is easy to understand, it is very lengthy and requires a lot of input.

The following lists the two textcomponentfixture methods used in our example:

Public void clear (){
Target. settext ("");
}
Public void entertext (string text ){
Robot. entertext (target, text );
}

We can simply return this to simplify our test API and activate the method link:

Public textcomponentfixture clear (){
Target. settext ("");
Return this;
}
Public textcomponentfixture entertext (string text ){
Robot. entertext (target, text );
Return this;
}

After activating the method link in all test facilities, our test code is now reduced:

Dialogfixture dialog = new dialogfixture (New logindialog ());
Dialog. Show (). Maximize ();
Dialog. Textbox ("username"). Clear (). Enter ("Leia. Organa ");
Dialog. ComboBox ("role"). Select ("Rebel ");
Dialog. optionpane (). requireerror (). requiremessage ("enter your password ");

The result code is obviously more concise and easy to read. As mentioned earlier, the method link itself does not mean there is a DSL. We need to link the methods corresponding to all relevant behaviors of objects that solve specific domain problems. In our example, the specific problem in this field is swing GUI testing.

1.1.2 Simple Object Construction/Configuration

This case is very similar to the previous one. The difference is that we no longer only link the related methods of an object, instead, we will create a "Builder" through a coherent interface to build and/or configure objects.

The following example uses setter to create "Dream Car ":

Dreamcar car = new dreamcar ();
Car. setcolor (red );
Car. setfuelefficient (true );
Car. setbrand ("Tesla ");

The dreamcar class code is quite simple:

// Package declaration and imports
Public class dreamcar {
Private color;
Private string brand;
Private Boolean leatherseats;
Private Boolean fuelefficient;
Private int passengercount = 2;
// Getters and setters for each field
}

For more information, see ............ Http://java.chinaitlab.com/base/741255_2.html

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.