The most notable feature of The Kotlin language is its brevity. Many of the language features in Kotlin are designed to simplify code while making it more readable. This simplicity can be maximized by implementing a DSL with the right combination of these language features.

This article describes how to construct an internal DSL for a project, using Dialog encapsulation as an example.

What is DSL?

A DSL stands for domain-specific language, which Wikipedia defines as a computer language that focuses on an application Domain.

In fact, THE SQL we are familiar with is a domain-specific language. Compared with programming languages, SQL statements are closer to spoken language, which makes SQL more expressive and readable.

Unlike regular apis, DSLS provide a special syntax structure that is intended to make the code easier to understand.

DSL:

  1. concise
  2. readable
  3. It has a special grammatical structure
  4. Applied to a specific domain

Why construct a DSL with Kotlin?

If the purpose of a DSL is to improve readability and simplify code, why Kotlin? Can’t the Java language construct DSLS?

In fact, there are many libraries in the Android space that provide dSL-like interfaces, such as Picasso:

Picasso.get()
  .load(url)
  .resize(50, 50)
  .centerCrop()
  .into(imageView)
Copy the code

Picasso’s interface composite DSL definition is in a sense, but given the nature of Java’s existing language, it doesn’t have much scope for constructing DSLS.

In Kotlin’s world, however, there are a number of language features that can be used to construct more concise and explicit DSLS:

  • Extension function
  • The default parameters
  • Infix expression
  • Lambda expressions
  • Function parameters
  • Function argument with receiver
  • . .

With all that said, let’s give a practical example of how to implement a specific DSL.

DSLS simplify Dialog boilerplate code

Define a CustomDialogFragment that implements the Dialog style specified in the project and provides interfaces for setting Title, Message, LeftButton, and RightButton.

Before using a DSL

val dialogFragment = CustomDialogFragment.newInstance() dialogFragment.title = "title" dialogFragment.message = "The message" dialogFragment. RightClicks (key = "sure," dismissAfterClick = true) {toast (" clicked!" ) } val ft = supportFragmentManager.beginTransaction() val prev = supportFragmentManager.findFragmentByTag("dialog") if (prev ! = null) { ft.remove(prev) } ft.addToBackStack(null) dialogFragment.show(ft, "dialog")Copy the code

After using the DSL

showDialog { title = "title" message = "message" rightClicks { toast("clicked!" )}}Copy the code
  1. Omitted a lot of boilerplate code, to ensure that the codesimplicity
  2. Semantic clarity, with strongreadability
  3. The syntax structure is different from normal apis
  4. Dialog display applied to Android projects

How to do that?

1. Extension functions

inline fun AppCompatActivity.showDialog(settings: CustomDialogFragment.() -> Unit) : CustomDialogFragment { val dialog = CustomDialogFragment.newInstance() dialog.apply(settings) val ft = this.supportFragmentManager.beginTransaction() val prev = this.supportFragmentManager.findFragmentByTag("dialog") if (prev ! = null) { ft.remove(prev) } ft.addToBackStack(null) dialog.show(ft, "dialog") return dialog }Copy the code

AppCompatActivity adds a showDialog() extension function to the compatactivity, so you can call the showDialog() method directly in an Activity to show a Dialog.

2. Function parameters with receiver

The showDialog() method has only one parameter, Settings, of type CustomDialogFragment.() -> Unit, which is the function with the CustomDialogFragment parameter type.

Inside the showDialog() method, a CustomDialogFragment object is constructed and the dialog.apply(Settings) method is called, which sets the Dialog after the Dialog object is constructed. When the showDialog() method is actually called, you can hold the CustomDialogFragment object and then configure the Dialog by calling the public interface provided by the CustomDialogFragment.

3. Default parameters

Click: Boolean = true, callback: click (key: String = "callback ", Boolean = true) () -> Unit) { leftKey = key leftButtonDismissAfterClick = dismissAfterClick leftClicks = callback }Copy the code

In the CustomDialogFragment interface, set default values for parameters that don’t change much. Because the Kotlin language supports default parameters, you don’t need to write a lot of overloaded methods.

The source code

Github: HanderWei/android-dialog-dsl-sample

finishing

In Android projects, there are many scenarios beyond Dialog that require a lot of boilerplate code. Creating your own DSL can help simplify the development process for the entire team.

Comments are expected to point out the errors in the article.

reference

  • Higher-Order Functions and Lambdas – Kotlin Programming Language
  • From Java Builders to Kotlin DSLs – Kotlin Expertise Blog