类设计-几种可能类型的属性

本文关键字:几种 类型 属性 | 更新日期: 2023-09-27 18:29:19

取一个类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;
    }
}

还有一些"类"源自Role:

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属性是ArcherMageWarrior的占位符。但是,这个特定的子类型是在运行时决定的(或者更具体地说,是在创建字符时决定的)。

我遇到的问题是试图将玩家对象serialize JSON数据。当JSON序列化Role属性时,它知道它是一个Role对象,并且能够从实例中获得相应的xxModifier值,但我忽略了Role属性是从Role派生的类的事实。

很明显,我还在学习,但这类问题有点告诉我,我在设计这门课时搞砸了,需要解决。我可以重构构造函数,使Role包含对子类的引用,但这似乎也是错误的。

编辑根据评论:我对Archer/Mage/Warrior类的意图是,除了当前存在的xxModifier属性外,每个类都将具有某些可用的方法(能力),因此Warrior的伤害(相对而言)较小,但具有更高的防御和生命值,而Role类目前是为Player对象提供一个放置其角色的位置(RPG类)的一种方式。这个具体的实现是我的目标,这里列出的就是我到目前为止所提出的。

  • 在这种情况下,Role类是必要的吗
  • 假设有必要重新设计这些类,我如何才能最好地编写它,使Player对象成为Archer/Mage/Warrior

类设计-几种可能类型的属性

对于如何在保留类型的同时序列化和反序列化为JSON的直接问题,使用JSON.Net之类的东西,这是非常简单的:

var p = new Player() { Role = new Mage() };
var json = JsonConvert.SerializeObject(p, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects });

现在您的JSON看起来像这样:

{
    "$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属性,因此在反序列化时:

var d = (Player)JsonConvert.DeserializeObject(json, typeof(Player), new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects });
var isMage = d.Role.GetType() == typeof(Mage);  // true

你的设计是否是一个好主意的问题(正如评论中所讨论的)有点宽泛。

这里有两个不同的问题:规范和实现。

在规范中,也就是在游戏后端,或者硬编码,或者在数据库中,或者其他什么中,你希望能够为你的玩家指定各种角色类的属性。这很好。

然而,每个规范都具有不同的值,这并不保证有不同的实现。您可以使用子类化来添加行为。你没有添加行为,只是根据玩家角色的值稍微改变现有的行为(我想你没有显示的伤害计算)。

此外,我现在怀疑你的代码中有这样的东西:

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);

但关于所有这些,已经写了很多书,这个问题太抽象(主题太宽泛),无法深入回答面向对象设计的各个方面。