This post contains the seed of an idea for an evolution post, but I'm posting this here as there are probably better ways to achieve what I want.
What I'm looking for is to be able to add mutable state to a class in a way that is private (or at least can be private) to a particular piece of code that uses that class.
For example, let's imagine I've got a game with a mutable Unit
type that represents a unit, and I've got an immutable struct that implements the Perk
protocol, so that a Unit
gets a damage boost if they've been shot in the last two turns. That perk needs to know the number of turns since a unit was shot, but the perk can't count the turns (because it's immutable) and we don't want to add a 'turns since shot' property to Unit
because it has too many properties already.
It would be nice if I could add a private
property extension that only the perk can see, but extensions cannot add stored properties.
So I decided to go the route of dictionaries, and add some sort of dictionary to Unit
. But now the problem is how to keep one perk's keys (and values) private from another perk (should it want to, at least.)
I can't use enum
as the key, because enums can't be extended, so all the keys must be public. I can't use Type
, because types can't be used as keys. I can use ObjectIdentifier
, and with some wrapping, it's not too bad:
class Bag {
var bag: [ObjectIdentifier: Any]
init() {
self.bag = [:]
}
func set(value: Any, for key: Any.Type) {
bag[ObjectIdentifier(key)] = value
}
func get<T>(key: Any.Type) -> T {
bag[ObjectIdentifier(key)] as! T
}
}
and to use it...
fileprivate enum TurnsSinceShot { }
struct MoreDamageWhenShotPerk {
func handle(event: Event) {
switch event {
...
case is UnitShot:
unit.bag.set(value: 0, for: TurnsSinceShot.self)
...
}
}
func damageMultiplier() -> Float {
let turnsSinceShot: Int = unit.bag.get(key: TurnsSinceShot.self)
return turnsSinceShot <= 2
? 1.5
: 1.0
}
}
...and this lets me choose wether bag values are private to this perk or public, but the issue here s I need to create a type for every value I need to store. It also feels a bit gross and over-complex.
Is there a better way to do this? Am I missing something incredibly obvious? Could something enabling this become a language feature?