在运行时更改方法行为的最简单方法是什么?

时间:2013-03-21 04:15:12

标签: c#

我有一个名为Enemy的课程,我希望有多种类型的敌人。每个Enemy都会有不同的移动行为,例如,一个人可能直接向玩家移动,一个可能保持一定的距离等等。我想知道的是,有多种不同行为的最简单方法是什么,最好是在一个班级?

我目前的想法是使用switch,并在一个方法中包含所有行为

public class Enemy
{
     public int health;
     public int damage;
     // etc etc

     public void Move(string type)
     {
        switch(type)
        {
             case "charge":
                  chargeMove();
                  break;
             case "maintain":
                  maintainMove();
                  break;
        }
     }

     private void chargeMove()
     {
          //stuff
     }

     private void maintainMove()
     {
          //stuff
     }

     //all the behaviors
}

我想知道的是,最好将所有内容保存到这样的函数中,还是为每个继承自Enemy的敌人类型创建一个新类?我宁愿将它保留在一个类中,这样所有的敌人都可以很好地存储到IList中,而不需要进行任何演员来使用任何特定的函数。

任何建议都将受到赞赏。

编辑:感谢所有的回复,我想我将使用Alastair提出的接口方法。

5 个答案:

答案 0 :(得分:2)

听起来像Strategy Pattern会很有用。

我认为你对每种敌人类型都有一个新课程的建议很好。您仍然可以将不同敌人类型的所有不同对象存储在一个List<Enemy>中,因为它们都从该类继承。

您还可以考虑将Enemy类视为抽象类,但这不是必需的。

如果您确实需要知道列表中特定对象的具体类型,可以使用typeofis

答案 1 :(得分:2)

虽然这可能更适合Gamedev.stackexchange.com网站,但您可能需要考虑使用某种合成方式。

定义描述移动描述的界面。 E.g。

public interface IMovementBehavior
{
    void Move(Enemy enemy);
}

那么你会有许多描述不同动作的不同行为。 例如

public class ChargingMovementBehavior : IMovementBehavior { ... }
public class MaintainingMovementBehavior : IMovementBehavior { ... }

所以你的敌人构造者可能会有一个像:

这样的定义
public Enemy(IMovementBehavior movementBehavior) { ... }

这意味着您可以轻松插入和移出不同的移动行为,而无需使用所有不同的移动行为填充您的敌人类。

这称为Component Based Entities

答案 2 :(得分:1)

使用委托:

public delegate void DoSth();

在运行时,您将此委托关联到不同的实例。

请在此处查看msdn reference

答案 3 :(得分:1)

我会创建多个派生自同一界面的类。您仍然可以拥有接口类型列表。这是polymorphism派上用场时的一个主要例子。如下所示:

interface iEnemy
{
    public void Move();
}

class Troll : iEmeny
{
    public void Move()
    {
       Console.WriteLine("troll moves!");
    }
}

class Ogre : iEmeny
{
    public void Move()
    {
       Console.WriteLine("ogre moves!");
    }
}

然后在你的代码中你可以这样做:

List<iEnemy> enemies = new List<iEnemy>();
enemies.Add(new Troll());
enemies.Add(new Ogre());

foreach(iEnemy e in enemies)
{
    e.Move();
}
//Output would be:
//troll move!
//ogre move!

这也允许您遵循open/closed principal,其中声明实体应该开放以进行扩展但已关闭以进行修改。

答案 4 :(得分:0)

我会使用派生类。这就是他们的目的 - 在调用相同的方法时提供不同的行为。

如果您不想进行强制转换,请在基类上定义所有方法。如果派生类没有覆盖特定方法,它将获得在基类中定义的任何行为,而不是如果你从不调用它就重要。如果你想要你仍然可以有一个整数“类型”字段,如果你不想使用typeof等。

public enum EnemyType
{
    Zombie,
    Vampire
}

public class Enemy
{
    private EnemyType mType;

    protected Enemy(EnemyType type) { mType = type; }

    public EnemyType getEnemyType() { return mType; }

    public void walk() { }

    public void attack() { }

    public void eatBrains() { }
}

public class Vampire : Enemy
{
    public Vampire : base(EnemyType.Vampire) { }

    public void walk()
    {
        // walk like a vampire
    }

    public void attack()
    {
        // attack like a vampire
    }

    // don't bother implementing eatBrains() because vampires don't do this
    // calling code will use getEnemyType() and won't bother call our eatBrains() method anyway
}