A game. The hero has certain attributes - some values that can be changed during the game. There are two types of attributes - Int and Double. Each attribute must have an add method that changes its value. Implemented it like this:
abstract class Attribute<T> (protected var value : T) { abstract fun add(otherValue : T) } class IntAttribute(value : Int) : Attribute<Int>(value) { override fun add(otherValue : Int) { value += otherValue } } class DoubleAttribute(value : Double) : Attribute<Double>(value) { override fun add(otherValue : Double) { value += otherValue } } The list of attributes of the hero is fixed. All attributes should be initialized and should be stored in a container that allows you to conveniently work with the entire set of attributes at once. Container implementation:
class AttributesContainer ( hp : DoubleAttribute, mana : DoubleAttribute, speed : IntAttribute, viewRange : IntAttribute ) { private val attributes = hashMapOf<String, Attribute<*>>( "hp" to hp, "mana" to mana, "speed" to speed, "viewRange" to viewRange ) // метод, позволяющий изменить значения всех атрибутов в контейнере // увеличив их на соответствующие значения из входного набора AttributesIncrease fun increase(increase : AttributesIncrease) { increase.values.forEach { attributes[it.key]?.add( it.value ) // ERROR: out-projected type 'Attribute<*>' } } } where AttributeIncrease is a class that stores a set of values added to the values of the corresponding attributes. Implementation:
class AttributesIncrease ( hp : Double, mana : Double, speed : Int, viewRange : Int ) { val values = hashMapOf<String, Number>( "hp" to hp, "mana" to mana, "speed" to speed, "viewRange" to viewRange ) } Why do you need such a structure:
- certain effects will be applied to the hero, whose action is expressed in changing the values of his attributes, so you need to be able to work comfortably with all attributes at once
- the possibility of enumeration of all arguments inside the container is necessary, therefore attributes in containers cannot be separate fields of the class
- the attribute container must be able to retrieve specific attribute values by name, so - map
It is clear that, due to the storage of values of type Attribute <*> in the map and the presence of different implementations of the add method (in IntAttribute and DoubleAttribute), the compiler swears to call it for a specific attribute in the map.
Nevertheless, I want to realize everything in this way - working with attributes is somehow universal, without distinguishing them by type . The thought does not let go that, it seems, the super-duper OOP code is famous for the possibility of such approaches, and it seems that some such solution exists.
What decisions have already been reviewed and rejected:
- implement the add method immediately in the Attribute asbractor, which would accept the Number parameter, and internally dealt with its type - does not work because the add method does not want to be overloaded with unnecessary work, because he will be called in the game constantly;
- if you store IntAttribute and DoubleAttribute in different maps inside the container, it becomes very clumsy implementation of the AttributeContainer method, which allows you to get the value of a specific attribute by its name + clumsy enumeration of all attributes + + +;
- to hammer on this "universality" and at the container level to work with the attributes "in the forehead" - to store them in the form of class fields, to work inside methods just with each field separately - not suitable because it is just awful and I would not like it
Naturally, the game provides for many more different methods and fields for all these classes, for example: the Attribute class stores more than one value - it has baseValue, maxValue, and currentValue (base value, maximum value and current) and works with them - therefore the attribute represented as a separate class, not a simple Int or Double.
I hope, the meaning of the task was solved more or less clearly. Thank you in advance!