在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):zsmb13/VillageDSL开源软件地址(OpenSource Url):https://github.com/zsmb13/VillageDSL开源编程语言(OpenSource Language):Kotlin 100.0%开源软件介绍(OpenSource Introduction):Village DSLThis repository contains samples of various Kotlin DSL designs. It consists of two main exercises: a simple and an advanced model, and it offers various DSL solutions for constructing structures within these models. Both models aim to simulate a fantasy game of some sorts where you have to describe a village and its contents. The simple modelThe simple exercise uses just three model objects: the village, the houses it contains, and the people who are in those houses. Here are the data classes representing these concepts: data class Person(val name: String, val age: Int)
data class House(val people: List<Person>)
data class Village(val houses: List<House>) Note that the examples included are larger than the code samples you see here below, look at the linked source files for the full examples. Approaches without DSLsFirst, let's see how we can construct a hierarchy of these models without defining a DSL. Traditional Java style constructionThis is essentially the approach that we'd take if we had to use Java, and it's translated to Kotlin syntax. We create mutable lists for everything, add items to them one by one on separate lines, and then create the objects that contain these lists. val houses = mutableListOf<House>()
val people1 = mutableListOf<Person>()
people1.add(Person("Emily", 31))
people1.add(Person("Hannah", 27))
people1.add(Person("Alex", 21))
people1.add(Person("Daniel", 17))
val house1 = House(people1)
houses.add(house1)
val village = Village(houses) This has all the usual pain points that constructing a hierarchy in Java entails: we can't really see the hierarchy itself in the code, and we have to follow a weird, unnaturally twisted structure with our code because of the limitations of the API. More importantly, this style of code gets complicated to read and modify quite quickly. Slightly improved construction, with more idiomatic KotlinWe can improve on this by quite a bit by just nesting some of these calls and using the factory methods for collections provided by the Kotlin standard library. val house1 = House(listOf(
Person("Emily", 31),
Person("Hannah", 27),
Person("Alex", 21),
Person("Daniel", 17)))
val village = Village(listOf(house1)) The code we have here is easier to write, read and maintain than the previous one. It still doesn't show hierarchy very well however, and it will face some of the same problems as the previous code when the described model gets larger. A "home-made" DSLThis "poor man's DSL" solution is mostly included for good measure. It makes use of the previously mentioned collection factory methods, named parameters, and some formatting to create something resembling a DSL. val village = Village(listOf(
House(listOf(
Person(
name = "Emily",
age = 31
),
Person(
name = "Hannah",
age = 27
),
Person(
name = "Alex",
age = 21
),
Person(
name = "Daniel",
age = 17
)
))
)) The problem here is that modifying the code is tedious compared to a real DSL, since you have to pay attention to where the DSL approachesBelow are various DSL approaches. Neither of these are supposed to be the solution to the posed exercise, they are just various examples of DSLs you can use to solve the problem. This document only contains small samples of how to use these DSLs, check the linked packages for the implementations and full examples. The usual DSLTo start, here's the DSL that follows the conventions most often used by DSL authors: it makes use of function literals with receivers that put you into the scope of builders that have the appropriate properties, as well as default and named arguments. val v = village {
house {
person {
name = "Emily"
age = 31
}
person(name = "Hannah") {
age = 27
}
person("Alex", 21)
person(age = 17, name = "Daniel")
}
} Note that while the code here showcases a variety of different ways for calling the same You can see that even this simplest DSL gets rid of the modification woes of the non-DSL approaches, as the blocks here can be moved around freely and easily. A more interesting DSL with operator overloadingInstead of doing the same thing over and over on every level of the hierarchy as before, we can construct some of our models (usually the leafs of the hierarchy) in a more direct way by just calling their constructors, and adding them to the right parent using overloaded operators. (A good example of this is how text can be appended to elements in the val v = village {
house {
+Person("Emily", 31)
+Person("Hannah", 27)
+Person("Alex", 21)
+Person("Daniel", 17)
}
} This is done by using an overloaded The same can be done using the A slightly over-the-top DSLPushing the limits of the Kotlin language, using some dummy val v = village containing houses {
house with people {
"Emily" age 31
"Hannah" age 27
"Alex" age 21
"Daniel" age 17
}
} Note that while in the case of the "usual DSL" it's verified that the blocks are nested in the expected order (by the @DslMarker annotation, see in the example here), these potentially chained infix function calls can lead to code that compiles but does nothing sensible. The advanced modelThe advanced example's model extends the simple model with various types of loot that can be placed inside the houses. data class Village(val houses: List<House>)
data class House(val people: List<Person>, val items: List<Item>)
data class Person(val name: String, val age: Int)
interface Item
data class Gold(val amount: Int) : Item
interface Weapon : Item
data class Sword(val strength: Double) : Weapon
interface Armor : Item
data class Shield(val defense: Double) : Armor Just to reiterate before getting into the various approaches: the code examples here are just short snippets. The full example is included in the linked source files. Approaches without DSLsAgain, let's first take a look at how we can create instances of the above models and nest them appropriately without defining a DSL. Traditional Java style constructionThis approach doesn't really deserve any more explanation than it got at the simple model. It's tedious to write, hard to both read and modify. val houses = mutableListOf<House>()
val people1 = mutableListOf<Person>()
people1.add(Person("Alice", 31))
people1.add(Person("Bob", 45))
val items1 = mutableListOf<Item>()
items1.add(Gold(500))
val house1 = House(people1, items1)
houses.add(house1)
val village = Village(houses) A "home-made" DSLNesting these calls gets you a bit closer to a DSL, but this solution has the same issues as it had with the simple model. val village = Village(listOf(
House(listOf(
Person(
name = "Alice",
age = 31
),
Person(
name = "Bob",
age = 45
)
), listOf(
Gold(
amount = 500
)
))
)) DSL approachesA traditional DSLSame old, same old. Lambdas with receivers and builder classes. This is the no-thrills DSL for this problem. val v = village {
house {
person {
name = "Alice"
age = 31
}
person {
name = "Bob"
age = 45
}
gold {
amount = 500
}
}
} A weird, overkill DSLHere's something you probably shouldn't do. I included the entire village in this sample code so that you can see more of what this solution includes. While this is a really weird and unnecessary DSL, the extensible structure of its implementation is worth looking at. val v = village {
house {
"Alice" age 31
"Bob" age 45
500.gold
}
house {
sword with strength value 24.2
sword with strength level 16.7
shield with defense value 15.3
}
house()
house {
"Charles" age 52
2500.gold
sword
shield
}
} TestsAll approaches for both exercises contain tests, which check that the required structure was produced by all approaches. These tests make use of the default |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论