想要避免在复杂的只读 class(游戏状态)中返回可写引用
Want to avoid returning writable references inside of a complex read-only class (game state)
我正在写一个小游戏,游戏有一个 State
class 来跟踪游戏状态。 State
class 只能由 Command
s(命令模式)修改。 State
class 包括其他 classes 的列表 - 例如Faction
class,其中包含资源等成员、拥有的 Unit
列表等
如何使 State
的深层内部结构可从其他 class 中读取,同时又不泄漏 State
本身内部的可写引用?
目前,我有像 State.GetOwnerOfUnitAtLocation(x)
这样的专用吸气剂,它只有 return 个安全值(int factionid
等),但我开始需要很多这样的吸气剂,而且class 变得非常笨拙。我更愿意将这些方法放在更合适的位置(Map.Units.GetOwner(x)
或其他),但我不知道如何以安全的方式将 State
的内部结构暴露给其他 classes .
相关地,Command
是目前存在于 State
class 内部的接口,以及实现它的所有实际命令,因此它可以修改私有成员State
。有没有更好的方法来实现这个?
编辑:从 State
中选择代码来说明第一个问题:
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;
}
}
}
因为 FactionsMgr.Faction
是可变的,我们假设它有这样的可写属性:
class Faction {
public int Foo { get; set; }
public string Bar { get; set; }
public float Baz { get; set; }
public SomeOtherMutableThing AnotherThing { get; set; }
}
你应该为它创建一个相应的只读接口,并Faction
实现它:
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;
}
然后,您可以在 State
中声明 IReadOnlyFaction
类型的 public 成员,并将它们委托给 Faction
类型的私有成员。私有成员也是 Command
class 将修改的内容。
private Faction someFaction;
public IReadOnlyFaction SomeFaction => someFaction;
这是一般情况。但是,如果您有这些可变类型的 集合 ,例如 _unit_map
的字典,您将需要做更多的工作。
您仍然会有一个 public 只读成员和一个私有可变成员,但委派过程不那么简单。你需要 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);
我正在写一个小游戏,游戏有一个 State
class 来跟踪游戏状态。 State
class 只能由 Command
s(命令模式)修改。 State
class 包括其他 classes 的列表 - 例如Faction
class,其中包含资源等成员、拥有的 Unit
列表等
如何使 State
的深层内部结构可从其他 class 中读取,同时又不泄漏 State
本身内部的可写引用?
目前,我有像 State.GetOwnerOfUnitAtLocation(x)
这样的专用吸气剂,它只有 return 个安全值(int factionid
等),但我开始需要很多这样的吸气剂,而且class 变得非常笨拙。我更愿意将这些方法放在更合适的位置(Map.Units.GetOwner(x)
或其他),但我不知道如何以安全的方式将 State
的内部结构暴露给其他 classes .
相关地,Command
是目前存在于 State
class 内部的接口,以及实现它的所有实际命令,因此它可以修改私有成员State
。有没有更好的方法来实现这个?
编辑:从 State
中选择代码来说明第一个问题:
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;
}
}
}
因为 FactionsMgr.Faction
是可变的,我们假设它有这样的可写属性:
class Faction {
public int Foo { get; set; }
public string Bar { get; set; }
public float Baz { get; set; }
public SomeOtherMutableThing AnotherThing { get; set; }
}
你应该为它创建一个相应的只读接口,并Faction
实现它:
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;
}
然后,您可以在 State
中声明 IReadOnlyFaction
类型的 public 成员,并将它们委托给 Faction
类型的私有成员。私有成员也是 Command
class 将修改的内容。
private Faction someFaction;
public IReadOnlyFaction SomeFaction => someFaction;
这是一般情况。但是,如果您有这些可变类型的 集合 ,例如 _unit_map
的字典,您将需要做更多的工作。
您仍然会有一个 public 只读成员和一个私有可变成员,但委派过程不那么简单。你需要 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);