Reputation: 119
I'm writing a little game, and the game has a State
class to keep track of game state. The State
class is only intended to be modifiable by Command
s (command pattern). The State
class includes lists of other classes - e.g. a Faction
class, which contains members like resources, a list of owned Unit
s, etc.
How can I make the deep innards of State
readable from other classes, without also leaking writable references inside of State
itself?
Currently, I have specialized getters like State.GetOwnerOfUnitAtLocation(x)
, which only return safe values (int factionid
etc.), but I am beginning to need a lot of these, and the class is getting really unwieldy. I would prefer to have those methods in more appropriate locations (Map.Units.GetOwner(x)
or something), but I don't know how to expose the internals of State
to other classes in a safe way.
Relatedly, Command
is an interface that currently lives inside of the State
class, along with all the actual commands that implement it, so that it can modify private members of State
. Is there a better way to implement this?
Edit: A selection of code from State
to illustrate the first issue:
public partial class State
{
public int Turn {get; private set;} = 1;
private Dictionary<Vector2, FactionsMgr.Faction> _unit_map = new Dictionary<Vector2, FactionsMgr.Faction>();
public int GetUnitRemainingMobility(Vector2 pos)
{
if (IsUnitAt(pos))
{
FactionsMgr.Faction owner = _unit_map[pos];
int taken_movement = owner.units[pos]._taken_movement;
int max_mobility = UnitsMgr.GetMaxMobility(owner.units[pos].type);
return max_mobility - taken_movement;
}
else
{
GD.Print("Warning: GetUnitRemainingMobility asked for unknown unit: ", pos);
return -1;
}
}
}
Upvotes: 0
Views: 76
Reputation: 271830
Since FactionsMgr.Faction
is mutable, let's suppose that it has writable properties like this:
class Faction {
public int Foo { get; set; }
public string Bar { get; set; }
public float Baz { get; set; }
public SomeOtherMutableThing AnotherThing { get; set; }
}
You should create a corresponding read only interface for it, and make Faction
implement it:
interface IReadOnlyFaction {
// exclude all members that can change Faction's state
int Foo { get; }
string Bar { get; }
float Baz { get; }
IReadOnlySomeOtherMutableThing AnotherThing { get; }
}
interface IReadOnlySomeOtherMutableThing {
// do the same thing there...
}
class Faction: IReadOnlyFaction {
public int Foo { get; set; }
public string Bar { get; set; }
public float Baz { get; set; }
public SomeOtherMutableThing AnotherThing { get; set; }
// you need an explicit interface implementation here, unless you are using C# 9
IReadOnlySomeOtherMutableThing IReadOnlyFaction.AnotherThing => AnotherThing;
}
Then, you can declare public members in State
as of type IReadOnlyFaction
, and delegate them to a private member of type Faction
. The private member is also what the Command
class will modify.
private Faction someFaction;
public IReadOnlyFaction SomeFaction => someFaction;
That is the general case. However, if you have collections of these mutable types, like your dictionary of _unit_map
, you would need to do a bit more work.
You would still have a public read only member and a private mutable member, but the delegating process is less straightforward. You would need a wrapper.
private Dictionary<Vector2, FactionsMgr.Faction> _unit_map = new();
public IReadOnlyDictionary<Vector2, IReadOnlyFaction> UnitMap;
// ...
// in the constructor of State...
// using the ReadOnlyDictionaryWrapper class from the linked answer
UnitMap = new ReadOnlyDictionaryWrapper<Vector2, FactionsMgr.Faction, IReadOnlyFaction>(_unit_map);
Upvotes: 2