问题
I am trying to make a query builder for GraphQL syntax, by using a builder pattern. I already did it for the filtering part:
I want to programatically make a way to say what data I want to get back from the query. (NOTE: before the title
part of the query was hardcoded into the QueryBuilder.
So for that I made a class called Entity which has a list of itself as an attribute.
class Entity(
private val entity: EntityType,
private val entities: MutableList<Entity> = mutableListOf()
) {
override fun toString(): String {
return if (this.entities.isEmpty()) {
entity.str
} else {
this.entity.str + "{" + this.entities.onEach {
it.toString()
}.joinToString(" ") + "}"
}
}
}
And I got it to work. But to build the structure. then I have to append to the list and "visit" the list of the entity to visit the entities in that list.
For example if I want to build this query:
{
events(place:"Somewhere", area:"The place"){
title
location{
coordinates{
longitude
latitude
}
}
}
}
Then I need to go 2 levels down in the layers of the Entity, And the way I am doing it now, then my code is getting very wide.
fun main() {
val e1 = listOf<Entity>(
Entity(EntityType.TITLE),
Entity(EntityType.LOCATION,
mutableListOf(
Entity(EntityType.COORDINATES,
mutableListOf(
Entity(EntityType.LONGITUDE),
Entity(EntityType.LATITUDE)
)
)
)
)
)
val q1 = Query.Builder()
.filter(FilterType.PLACE, "Somewhere")
.filter(FilterType.AREA,"The place")
.add(e1)
.build()
println(q1.query)
}
So I was thinking to use builder pattern. Or something else which will be smarter.
Thank you in advance for your help, and have a nice day.
BTW: I am using following enums:
enum class EntityType(val str: String) {
EVENTS("events"),
TITLE("title"),
LOCATION("location"),
COORDINATES("coordinates"),
LONGITUDE("longitude"),
LATITUDE("latitude"),
}
enum class Filter(val str: String) {
PLACE("place"),
AREA("area"),
}
And the rest of my code looks like this:
class Filter {
var filters = mutableMapOf<FilterType, Any>()
override fun toString(): String {
return if (filters.isNotEmpty()) {
"(" + filters.map {
if (it.value is Int || it.value is Long) {
it.key.str + ":" + it.value + ","
} else {
it.key.str + ":\"" + it.value + "\","
}
}.joinToString(" ").dropLast(1) + ")"
} else {
""
}
}
}
class Query private constructor(val query: String) {
class Builder {
private lateinit var entities: List<Entity>
private var filter = Filter()
fun filter(key: FilterType, value: Any) = apply {
this.filter.filters[key] = value
}
fun add(entities: List<Entity>) = apply {
this.entities = entities
}
private fun collect(root:String):String {
return "{$root$filter{${entities.joinToString(" ")}}}"
}
fun build(): Query {
return Query(collect("events"))
}
}
回答1:
Take a look to Kotlin Type-Safe Builders documentation. You can find an example on how to do it for HTML
来源:https://stackoverflow.com/questions/65079118/how-can-i-build-a-nested-list-using-builder-pattern