面向对象编程正确使用接口

3

我正在尝试为一个游戏设计几个类,但是我不确定我的做法是否正确。

我有两个类:ActorBuilding。 这些类有一些子类:PolicemanFiremanPoliceStationFireStation

我希望能够将所有这些项目放在一起,以便稍后迭代,因此我添加了一个基类:GameEntity
所以我现在有这个:

public abstract class GameEntity: 
{
    public GameEntity()
    {

    }       
}

public class Actor: GameEntity
{
    public int _speed;
    public TileSprite _UI;

    public Actor()
    {

    }

    public bool CollidesWith(Vector2 pos)
    {
        //Do stuff here
    }

    public virtual void OnClick()
    {
        //Do stuff here
    }

    public void DoActing()
    {

    }
}

public class Policeman: Actor
{
    public Policeman()
    {
        _speed = 10;
    }

    public override void OnClick()
    {
        //Do stuff
    }
}

public class Building: GameEntity
{
    public TileSprite _UI;

    public Building()
    {

    }

    public bool CollidesWith(Vector2 pos)
    {
        //Do stuff here
    }

    public virtual void OnClick()
    {
        //Do stuff here
    }

    public void DoBuilding()
    {

    }
}

public class PoliceStation: Building
{
    public PoliceStation()
    {

    }

    public override void OnClick()
    {
        //Do stuff
    }
}

现在,我希望能够做到这一点:
List<GameEntity> Entities = new List<GameEntity>();

Actor a1 = new PoliceMan();
Building b1 = new PoliceStation

Entities.Add(a1);
Entities.Add(b1);

foreach(GameEntity ent in Entities)
{
    if (ent.CollidesWith(something))
    {
        ent.OnClick();

        //If Actor then do 
        ent.DoActing();

        //If Building then do
        ent.DoBuilding();
    }
}

现在,为了完成最后一部分,我是不是最好实现一个包含OnClickCollidesWith的接口,还是可以用继承来完成?如果是,我该怎么做呢?
祝好。

我会把你的实体列表放在一个World类中。以后,当你拥有更多的实体时,你不想对所有实体进行逐一检查。你只想为移动对象检查碰撞,并且仅针对附近的对象进行检查。 - the_lotus
4个回答

2

这只是为了让您有个大概的想法,我想这正是您需要的。

 public interface IGameEntity
    {
        bool CollidesWith();
        void OnClick();
        void DoActing();
    }
  public class Actor : IGameEntity { //Interface implemented }
  public class Building: IGameEntity { //Interface implemented }
  public class Policeman: IGameEntity { //Interface implemented }
  public class Fireman: IGameEntity { //Interface implemented }
  public class FireStation: IGameEntity { //Interface implemented }

在您的客户端对象中,只需像这样执行操作:
List<IGameEntity> entities = new List<IGameEntity>()
{
    new Actor(), 
    new Building(), 
    new Policeman(), 
    new Fireman(), 
    new Fireman()
};
foreach (IGameEntity entity in entities)
{
    entity.CollidesWith();
    entity.OnClick();
    entity.DoActing();
}

1
你可以这样做:
    public interface IGameEntity
    {
        void OnClick();
        bool CollidesWith(Vector2 pos);
        void Do();
    }

    public abstract class Actor: IGameEntity
    {
        public int _speed;
        public TileSprite _UI;

        public virtual bool CollidesWith(Vector2 pos)
        {
            //Do stuff here
        }

        // can be marked virtual with implementation if you want a default
        // this way base classes will be forced to implement their own implementation
        public abstract void OnClick();

        // can be marked virtual with implementation if you want a default
        // this way base classes will be forced to implement their own implementation
        public abstract void Do();
    }

    public class Policeman: Actor
    {
        public Policeman()
        {
            _speed = 10;
        }

        public override void OnClick()
        {
            //Do stuff
        }

        public override void Do()
        {
            //Do Acting for Police
        }
    }

    public abstract class Building: IGameEntity
    {
        public TileSprite _UI;

        public bool CollidesWith(Vector2 pos)
        {
            //Do stuff here
        }

        public abstract void OnClick();

        public abstract void Do();
    }

    public class PoliceStation: Building
    {
        public PoliceStation()
        {

        }

        public override void OnClick()
        {
            //Do stuff
        }

        public override void Do()
        {
            // Do Building
        }
    }

如果Game Entity只是一个接口,那么没有必要将其作为抽象类。如果Actor和Building不能作为独立对象存在,那么它们应该是抽象的。如果DoActing、DoBuilding和OnClick必须被基类覆盖,那么你可以在Actor和Building中将它们标记为抽象。
我遵循了norlesh的建议,只有一个Do方法。你也可以让Actor和Building拥有自己的抽象方法,而IGameEntity不需要Do。然后你会放置一个if语句并检查每个对象是否属于builder类型,然后运行build,如果是actor类型,则运行act。
我认为只有一个方法的方式更好,因为你省去了一步。从设计的角度来看,我不确定,但感觉两种方式都一样。
List<IGameEntity> Entities = new List<IGameEntity>();

Actor a1 = new PoliceMan();
Building b1 = new PoliceStation();

Entities.Add(a1);
Entities.Add(b1);

foreach(IGameEntity ent in Entities)
{
    if (ent.CollidesWith(something))
    {
        ent.OnClick();

        ent.Do();
    }
}

PS:我不会真的把这个方法命名为“Do”。我会给它起一个类似于PostClickBehavior或PostClickProcessing之类的名称。 - Nick Bray
我仍然怀疑将CollidesWith放入IGameEntity中。建筑物移动到车辆路径上的唯一时间是在保险索赔时。 :) - Tony Hopkinson
LOL。是的,我认为更像是与坦克相撞(坦克在移动,而不是房子)。 - Nick Bray
你可以从两种方式来论证,这取决于你想要分离碰撞和碰撞。这是一种“是一个”/“有一个”的决策,在使用继承时可能会让你陷入困境。 - Tony Hopkinson

1
将虚拟的 doStuff() 添加到 GameEntity 中,并在子类中重写它,而不是使用唯一的方法名如 DoActingDoBuilding,然后从循环中调用它应该能够实现您想要的效果。

1
这里有多种方法。 如果所有GameEntities都可以被点击,那么您可以将OnClick事件属性添加到基类中。
如果不是,则需要一个IClick接口,然后在可点击实体上实现它。
碰撞取决于情况。建筑物不会移动,因此它们无法与任何东西发生碰撞,但它们可以被碰撞,因此例如您可能需要进行损坏例程,这就暗示了一种破坏例程、修复例程等。
没有正确的答案,但有三个代码气味: 1.一个空的基类。 2.非常深的继承层次结构(超过两个级别会引起怀疑)。 3.最重要的是,在基类中有一些无用的方法,只是因为您需要在某些子类中添加行为(覆盖)。
不要担心拥有一些接口。 IClick、ICollision、IDamage、IRepair等。这比意味着建筑物可以撞上汽车要好得多。

如果我为某些类添加不同的接口,例如,我将在Actor类中添加接口IMove,其中包含函数Move()。如果我将它们全部放在List<IGameEntity>中,那么如何调用此函数?我需要先转换为 Actor 类吗? - CJ Scholten
是的,你需要进行强制类型转换,实际上还要检查和转换。但是,为什么要使用List<IGameEntity>来移动呢?另一个列表List<IMove>,即当你创建一个实现IMove的实体时,将其添加到两个列表中。 - Tony Hopkinson
如果你有一个移动房屋,那么它主要是静态的,但偶尔会通过一个简单的IsMoving属性移动。然后,如果它正在移动,将其添加到两个列表中。与移动相关的任何内容都不应该关心为什么或何时移动,如果它在列表中,就会移动。 - Tony Hopkinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接