上一个Player
课程,其中(当前)有Role
类型的媒体资源:
public class Player
{
public string Name { get; set; }
public int Health { get; protected set; }
public Role Role { get; set; }
}
Role
包含一些不同的值:
public class Role
{
public double DamageModifier { get { return _damageModifier; } }
public double DefenseModifier { get { return _defenseModifier; } }
public double HealthModifier { get { return _healthModifier; } }
private double _damageModifier;
private double _defenseModifier;
private double _healthModifier;
public Role(double damageMod, double defenseMod, double healthMod)
{
_damageModifier = damageMod;
_defenseModifier = defenseMod;
_healthModifier = healthMod;
}
}
还有一些"类"源于角色:
public class Archer : Role
{
private const double _damageModifier = 1.2;
private const double _defenseModifier = 0.9;
private const double _healthModifier = 0.8;
public Archer() : base(_damageModifier, _defenseModifier, _healthModifier) { }
}
public class Mage : Role
{
private const double _damageModifier = 1.4;
private const double _defenseModifier = 0.7;
private const double _healthModifier = 0.9;
public Mage() : base(_damageModifier, _defenseModifier, _healthModifier) { }
}
public class Warrior : Role
{
private const double _damageModifier = 1.0;
private const double _defenseModifier = 1.2;
private const double _healthModifier = 1.0;
public Warrior() : base(_damageModifier, _defenseModifier, _healthModifier) { }
}
所有这一切的最终结果实际上只是Player.Role
属性是Archer
,Mage
或Warrior
的占位符。但是,此特定子类型是在运行时(或更具体地,在创建字符时)决定的。
我尝试将serialize
玩家对象转换为JSON
数据时出现的问题。当JSON
序列化Role
属性时,它知道它是Role
对象,并且能够从实例中获取相应的xxModifier
值,但是我失去了事实上Role
属性是从Role
派生的类。
我显然还在学习,但这种问题告诉我,我在设计这个课程时搞砸了,需要修复它。我可以重构构造函数,以便Role
保存对子类的引用,但这似乎也是错误的。
编辑根据评论:
我对Archer/Mage/Warrior
类的意图是,除了当前存在的xxModifier
属性之外,每个类都有可用的某些方法(能力),这样Warrior
会做(相对而言)损害较小但具有较高的防御和健康,而Role
类目前可以作为一种方式为Player
对象提供一个保持其角色的位置(RPG类)。这个具体的实现是我的目标,这里列出的内容是我到目前为止所提出的。
Role
课程是否必要? Player
对象 是 Archer/Mage/Warrior
?< / LI>
答案 0 :(得分:5)
关于如何在保留类型的同时序列化和反序列化为JSON的直接问题,使用类似JSON.Net的东西,这是非常简单的:
public final class MyInstance {
private final Context context;
MyInstance(Context context, and, whatever, else) {
//package private, can't be directly instantiated by library user
this.context = context;
}
public void doSomethingThatRequiresContext() {
//no special checks required
}
//add other methods, this is just a normal class
}
public static class MyLibrary {
private static Object lockObj = new Object();
private static MyInstance myInstance;
public static MyInstance init(context, and, whatever, else) {
synchronized(lockObj) {
if (myInstance != null)
throw new RuntimeException("Please do not call init more than once");
myInstance = new MyInstance(context, and, whatever, else);
return myInstance;
}
}
public static MyInstance getInstance() {
synchronized(lockObj) {
if (myInstance == null)
throw new RuntimeException("Please call init before getInstance");
return myInstance;
}
}
}
现在你JSON看起来像这样:
var p = new Player() { Role = new Mage() };
var json = JsonConvert.SerializeObject(p, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects });
添加了额外的{
"$type": "MyApp.Program+Player, MyApp",
"Name": null,
"
Health": 0,
"Role": {
"$type": "MyApp.Program+Mage, MyApp",
"DamageModifier": 1.4,
"DefenseModifier": 0.7,
"HealthModifier": 0.9
}
}
属性,因此在反序列化时:
$type
您的设计是否是一个好主意的问题(如评论中所讨论的)有点广泛。
答案 1 :(得分:2)
这里有两个不同的问题:规范和实现。
在规范中,所以在你的游戏后端,或硬编码,或在你的数据库或其他任何东西,你希望能够为你的播放器指定各种角色类的属性。这可以。
每个规范保持不同的值但不保证不同的实现。您使用子类添加行为。根据玩家角色的值,您不会添加行为,只是略微改变现有行为(我猜您未显示的损坏计算)。
此外,我现在怀疑你的代码是这样的:
void NewPlayerButtonClicked(string characterClass)
{
var player = new Player();
if (characterClass == "Mage")
{
player.Role = new Mage();
}
// ...
}
这本身就是一种设计气味。
您只需要一个Role类来保存值:
public class Role
{
public string ClassName { get; set; }
public decimal DamageModifier { get; set; }
public decimal DefenseModifier { get; set; }
public decimal HealthModifier { get; set; }
}
在您的播放器处理代码中,您可以访问角色的属性:
public decimal GetAttackDamage(Player attacker, Player defender, AttackInfo attack)
{
// do calculations with attacker.Role.N and defender.Role.N
}
现在我猜你问这个是因为你想在创建角色时填充字段,或者加载游戏状态。例如,您可以通过将规范存储在JSON中来实现:
{
"characterClasses" :
[
{
"className" : "Mage",
"damageModifier" : 1.4,
"defenseModifier" : 0.7,
"healthModifier" : 0.9,
},
{
...
}
]
}
然后,如果您保存播放器的角色类,加载数据变得非常简单:
player.Role = rolesLoadedFromJson.First(r => r.ClassName == player.CharacterClass);
但是在所有这些书中都写了很多书,这个问题太过抽象(而且题目太宽泛),无法对面向对象设计的各个方面给出深入的答案。