Domain specific language (DSL) is a method of programming where variables, functions, and classes are descriptive of business-domain definitions and methodology. It allows software developers to more easily communicate with business matter experts and non-technical users, even to the point of allowing non-developers to manipulate programming logic and customize operation. Domain specific languages can be categorized into two areas, including Internal and External DSLs. Internal DSLs include programming logic and code, designed to incorporate business domain-specific terminology. External DSLs include separate scripts, text files, and other configuration files programmed in domain-specific keywords, and used to drive the software application.
In this tutorial, we’ll create a simple RPG role-playing game. We’ll create a character, assign his attribute statistics including strength, agility, intelligence, and class. We’ll then put him to battle against an endless barrage of enemies and see how long he lasts! We’ll focus on an internal domain specific language for our design. In particular, we’ll implement a fluent interface design, using the popular Expression Builder pattern with method chaining and progressive interfaces. The Expression Builder pattern is a popular design used in internal domain specific languages and it will allow us to create a fluent implementation of the Character class, leaving an expressive implementation of heroes and enemies in our programming logic.
Since we’ll be creating a simple role-playing RPG game, we’ll need to first implement a basic character class. Before we get to the details of the class, assume it will contain various attributes for name, class, age, hit points, strength, agility, intelligence, gold, and possibly more. Instantiating the Character in a traditional fashion might be done using the following code:
In the above block of code, we’ve instantiated a Character class by using the empty constructor. We’ve then populated the Character by assigning values to each public field of the class. While the code is clear and cut, it’s not exactly expressive. We’re executing several assignment statements in no particular order. The logic flow is not well defined. From a programmer’s perspective, the above code would be perfectly suitable. However, from a business user perspective, perhaps we could define a more descriptive interface.
We’ve trimmed the code down to a single line, instantiating the constructor of the Character class. This code is short and to-the-point. We can also gain any needed information on parameters through intellisense or comments on the class constructor. However, the code can still be more expressive. From a business user perspective, the code may even be un-readable.
In the above code, we’ve defined a domain-specific fluent interface. The interface is a set of construction methods, each one returning itself (or the next-step interface, in the case of progressive interfaces), allowing us to construct a new character. The above code may be longer; it’s certainly not as short as the one-line implementation, but it is more expressive. In particular, non-technical business users may be able to better determine the functionality for the character and possibly even define their own.
Implementing a fluent interface is a relatively straight-forward task that can be done through the use of method chaining. Method chaining is simply a set of assignment methods that return itself (ie., this). The result from each method can then call the next assignment method, and so-on. To guide the user and enforce rules of construction (such as, the Class can only be assigned once, followed by attributes), we utilize progressive interfaces. Where the method would return “this”, we instead return an interface for the next step in line.
In the above code, we’ve defined the basic Character class, used in the beginning examples. The class consists of properties and attributes to utilize the character in our role playing game. The long constructor (allowing assignment of all properties in the constructor) is provided for non-fluent design. Notice, the class itself does not contain method chaining fluent methods. We’ll define the fluent implementation in a separate builder class, implementing the Expression Builder pattern. This allows us to separate our fluent-specific code from our class implementation. Developers who prefer not to use a fluent interface may then instantiate the Character class directly, while those who prefer the fluent interface can utilize the expression builder class.
In our first implementation of an expression builder for a fluent interface of the Character class, we’ll implement the builder with no strings attached. That is, we won’t enforce rules for the order of assignment. While the code will be simpler, it does leave the builder class open to misuse. We can implement the basic expression builder with method chaining in C# .NET as follows:
Notice in the above code, we’ve implemented the Expression Builder pattern using method chaining. The class itself constructs a Character for us. We may then call any of the assignment methods to populate the fields and attributes of the Character class. Each method returns a copy of itself, allowing us to chain the assignment methods, one after the other, thus implementing our fluent interface in C# .NET. We can then finally call the Value() method to obtain the completed Character class.
While the above expression builder implements a fluent interface in C#, we can better guide the user when constructing the Character class by enforcing certain rules and order-of-events. In particular, after calling the Create() method, we can guide the user to assign Class property, using the method name “As”, a descriptive term for assigning the type of Character. After populating the Class, we can guide the user to assign an Age, followed by assigning the other attribute values.
This type of expression builder fluent interface is using Progressive Interfaces, which are designed to incrementally guide the construction of a fluent entity. Using this method of enforcing rules with progressive interfaces, we can force properties to be constructed while leaving others optional. We can also enforce the order by which properties are assigned (similar to a parameterized constructor), while allowing other properties to be assigned in any order.
We can update our Expression Builder with method chaining to use progressive interfaces in C# .NET as follows:
In the above code, we’ve changed the return type from each of the assignment methods. Instead of returning “this”, which would give full control to assign any other method by the user, we return a copy of the specific interface. The interface will specify which methods may be called next. The interfaces can be defined as follows:
Each interface contains the next set of available assignment methods. The user will only be able to call the defined set of methods from within the fluent interface. Our expression builder implements each of the interfaces and returns them accordingly.
With our expression builder defined, using method chaining and progressive interfaces in C# .NET, we can now utilize the fluent interface for the Character expression builder to create a hero and an endless supply of enemies. We can then automate the creation to create our simple role-playing game and watch them battle.
Notice in the above code, we’ve utilized the fluent interface to create a hero Character class. The code follows the internal domain specific language and is fully readable by both developer and business user (if there were one). We also create an enemy Character to fight our hero. The enemy is constructed in a similar fashion, using the fluent interface with method chaining in C# .NET. It’s interesting to note that we could take this a step further and implement an external domain-specific language, by providing a text file script for defining enemies. The script could utilize its own language for assigning the attributes. Our program would read the text file, parse the commands, and pass the data into the fluent interface to construct the entities. The fluent interface certainly opens the door to expressive design.
You can download the project source code on GitHub by visiting the project home page.
Traditional programming code is often designed to be read and understood by programmers. However, as technology expands, code may be exposed to business users and non-technical audiences. By implementing fluent interfaces with expression builders, method chaining, and progressive interfaces, we can develop expressive code that enhances readability and is potentially easier to communicate to non-technical subject matter experts. Fluent interfaces can be a powerful tool to enhance understanding of code for both software developers and business domain experts.
This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.