I am starting to learn Kotlin to make a text-based game. I wanted to enforce relational integrity between specific objects (i.e: an instance of Fuel
cannot be used by more than 1 instance of Machine
), therefore I decided to try to implement the Kotlin Exposed ORM
I am having a hard time conceptualising the Entity objects however. Here is an example abstract base class Component
:
package machine.component
// ...imports
abstract class Component(id: EntityID<UUID>, var inputResources: Array<Resource>): UUIDEntity(id) {
companion object : UUIDEntityClass<Component>(Components)
val componentName: String by Components.componentName;
// A map of how many resources are required to build the Component. Maps name of Resource class to quantity required
abstract val _bluePrint: Map<KClass<out Resource>, Int>
val resources by Resource optionalReferrersOn Resources.component;
init {
val sortedResources = this.resources.groupBy { it::class }
sortedResources.forEach {
if (this._bluePrint.keys.contains(it.key)) {
require(it.value.count() == this._bluePrint[it.key]) {
"Component ${this.componentName} needs ${this._bluePrint[it.key]} '${it.key::class.simpleName} resource types to build"
}
}
}
// TODO: find a way to return the leftover resources to the user
var leftoverResources: Map<KClass<out Resource>, List<Resource>> = this._resources.filterKeys { !this._bluePrint.keys.contains(it) }
// Empty input array so it can't be used elsewhere
inputResources.dropWhile { inputResources.isNotEmpty() }
}
}
The Resource
entity referred to in Component
looks like this:
package resource
// ...imports
abstract class Resource (id: EntityID<UUID>): UUIDEntity(id) {
companion object : UUIDEntityClass<Resource>(Resources)
abstract val rawName: String;
protected var hitPoints: Double by Resources.hitPoints;
override fun toString(): String = "A ${rawName} resource with ${hitPoints} hitpoints"
// TODO: make this function so that it can only accept an int from fuel being spent, not a random number
fun extract(hits: Double): Resource? {
hitPoints -= hits
if (hitPoints <= 0) {
return this
}
return null
}
}
So, whilst ignoring some likely obvious shortcomings with Kotlin basics, the general intention of the Entity class is this:
- The user instantiates a subtype of this class, passing in a list of (subtype)
Resource
instances - The
Component
constructor goes through theResource
types, and fails if the quantity of a specific resource does not meet the requirement of the declaredblueprint
property (IIRCrequire
should fail the constructor if predicate is false) - All leftover resources are returned to the user
- (TODO) The resources required by the blueprint are stored to the
resources
property - (TODO) The
Component
instance is saved
Step #4 is where I am struggling, is it beyond the scope of an entity to assign itself some data?
If so, do I need to implement some kind of Repository/service wrapper that performs these validation checks and then creates and returns the instance? If a ComponentRepository
were to exist how can I ensure that Component
instances can only be created by it.
If the object relationship is flawed in the first place, please feel free to say this