首页 > 解决方案 > OOP 中的表达类型组合

问题描述

假设我们要编写一个 RPG 游戏,它有 3 种基本角色类型:战士、法师和弓箭手。我们还结合了骑士、圣骑士和游侠等角色类型。在 TypeScript 中,我们可以为这样的字符描述接口:

    // fighter can fight
    // mage can cast
    // archer can shoot
    // paradin can cast and fight
    // knight can fight and shoot
    // ranger can cast and shoot

    type Fighter = {fight: () => string}
    type Mage = {cast: () => string}
    type Archer = {shoot: () => string}
    type Knight = Fighter & Archer
    type Paladin = Fighter & Mage
    type Ranger = Archer & Mage

问题是如何仅使用 OOP 技术来实现这些接口(假设我们正在使用 C# 等主流 OOP 语言)?

在没有行为重复的情况下,我没有成功地用继承来表达这一点。目前我看到的唯一方法是:

class Paladin {
  constructor(private fighter: Fighter, private mage: Mage) {}

  fight() {this.fighter.fight()}
  cast() {this.mage.cast()}
}

但我不喜欢这种方法,因为任何时候我想创造一个圣骑士,我也必须创造一个法师和一个战士,基本上是同一个人

标签: c#typescriptoopdesign-patternscomposition

解决方案


也许试着把你的作文推得更远一点。你只需要一种具有不同能力的类型,例如:

public class Hero
{
    private readonly string name;
    private readonly IEnumerable<IHeroAbility> abilities;

    public Hero(string name, [Optional] IEnumerable<IHeroAbility> abilities)
    {
        this.name = name;
        this.abilities = abilities ?? new List<IHeroAbility> { new OhCrap() };
    }

    public void Attack()
    {
        Console.WriteLine(name);

        foreach (var ability in abilities) ability.Execute();

        Console.WriteLine();
    }
}

现在准备可用的能力:

public interface IHeroAbility
{
    void Execute();
}

public class SwingSwordAbility : IHeroAbility
{
    public void Execute()
    {
        Console.WriteLine("And with all his might the hero swung his sword down onto his opponent...");
    }
}

public class ShootArrowAbility : IHeroAbility
{
    public void Execute()
    {
        Console.WriteLine("The hero loads an arrow into his bow without blinking and launches it with fierce speed towards his opponant...");
    }
}

public class CastFireBallAbility : IHeroAbility
{
    public void Execute()
    {
        Console.WriteLine("The hero reaches deep into the underverse and hurls a ball of fire towards his opponant...");
    }
}

public class OhCrap : IHeroAbility
{
    public void Execute()
    {
        Console.WriteLine("The hero pats his pockets as if he forgot something at home...");
    }
}

然后组成你的英雄类型:

class Program
{
    static void Main(string[] args)
    {
        IHeroAbility swingSwordAbility = new SwingSwordAbility();
        IHeroAbility shootArrowAbility = new ShootArrowAbility();
        IHeroAbility castFireBallAbility = new CastFireBallAbility();

        var fighter = new Hero("The Fighter", new[] { swingSwordAbility });
        fighter.Attack();

        var mage = new Hero("The Mage", abilities: new[] { castFireBallAbility });
        mage.Attack();

        var archer = new Hero("The Archer", abilities: new[] { shootArrowAbility });
        archer.Attack();

        var knight = new Hero("The Knight", abilities: new[] { swingSwordAbility, shootArrowAbility });
        knight.Attack();

        var paladin = new Hero("The Paladin", abilities: new[] { swingSwordAbility, castFireBallAbility });
        paladin.Attack();

        var ranger = new Hero("The Ranger",abilities: new[] { shootArrowAbility, castFireBallAbility });
        ranger.Attack();

    }
}

如果您想要更明确的操作组,您可以将您的Hero类修改为以下内容:

public class Hero
{
    private readonly string name;
    private readonly IEnumerable<IHeroAbility> meleeAbilities;
    private readonly IEnumerable<IHeroAbility> magicAbilities;
    private readonly IEnumerable<IHeroAbility> rangedAbilities;

    public Hero(
        string name,
        [Optional] IEnumerable<IHeroAbility> meleeAbilities,
        [Optional] IEnumerable<IHeroAbility> magicAbilities,
        [Optional] IEnumerable<IHeroAbility> rangedAbilities)
    {
        this.name = name;

        var defaultAbilities = new List<IHeroAbility> { new OhCrap() };
        this.meleeAbilities = meleeAbilities ?? defaultAbilities;
        this.magicAbilities = magicAbilities ?? defaultAbilities;
        this.rangedAbilities = rangedAbilities ?? defaultAbilities;
    }

    public void MeleeAttack()
    {
        var ability = meleeAbilities.First();
        Console.WriteLine(name);
        ability.Execute();
    }

    public void MagicAttack()
    {
        var ability = magicAbilities.First();
        Console.WriteLine(name);
        ability.Execute();
    }

    public void RangedAttack()
    {
        var ability = rangedAbilities.First();
        Console.WriteLine(name);
        ability.Execute();
    }

}

然后,您可以开始构建它以包含更复杂的逻辑,例如与能力、冷却时间、能力选择等相关的成本等。

希望这可以帮助!


推荐阅读