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

时间:2016-01-06 20:54:38

标签: c# json

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

我尝试将serialize玩家对象转换为JSON数据时出现的问题。当JSON序列化Role属性时,它知道它是Role对象,并且能够从实例中获取相应的xxModifier值,但是我失去了事实上Role属性是从Role派生的类。

我显然还在学习,但这种问题告诉我,我在设计这个课程时搞砸了,需要修复它。我可以重构构造函数,以便Role保存对子类的引用,但这似乎也是错误的。

编辑根据评论: 我对Archer/Mage/Warrior类的意图是,除了当前存在的xxModifier属性之外,每个类都有可用的某些方法(能力),这样Warrior会做(相对而言)损害较小但具有较高的防御和健康,而Role类目前可以作为一种方式为Player对象提供一个保持其角色的位置(RPG类)。这个具体的实现是我的目标,这里列出的内容是我到目前为止所提出的。

  • 在这种情况下,Role课程是否必要?
  • 假设有必要重新设计这些类,我怎样才能最好地编写这个类,以便Player对象 Archer/Mage/Warrior?< / LI>

2 个答案:

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

但是在所有这些书中都写了很多书,这个问题太过抽象(而且题目太宽泛),无法对面向对象设计的各个方面给出深入的答案。