如何进行依赖注入一个类/类型?

3
我正在处理一个设计问题,我不希望因为糟糕的解决方案而使我的代码变得混乱。与其给出一个差劲的类比,我宁愿解释我的确切情况。
我正在尝试编写Wii Play Tanks的克隆版,并且我在设计Tank类时遇到了麻烦。 Tank本身是唯一的这样一个类,它使用依赖注入来管理其部分。目前有两个部分:TankAI和TankWeapon。 AI负责决策运动和开火,武器描述武器行为-它发射什么弹药以及多久发射一次等。 我有一个工厂类,可以构建不同组合的坦克。
我的弹丸类设置在抽象Projectile类下。每个子类都描述了弹丸的模型,反弹次数,速度等。
我的问题是,每个TankWeapon子类都在构造新弹丸的区域周围重复了很多代码,因为它们各自构造了不同的类。我想将此代码移动到基类中,但我必须以某种方式注入武器需要构造的弹丸类本身。我知道我可以在构造时直接传递类,但那似乎不是正确的方法。
另外,我还有另一个设计问题:如何使我的AI类也了解弹丸类?他们的决策将取决于正在发射的弹药的属性,例如它们可以在墙上反弹多少次。 AI和Weapon类都在注入时给予对父级Tank的引用。
public class Tank : ISolidObject
{
    public TankAI AISystem { get; private set; }
    public TankWeapon Weapon { get; private set; }

    public Tank(TankAI aiSystem, TankWeapon weapon)
    {
        this.AISystem = aiSystem;
        this.AISystem.Tank = this;

        this.Weapon = weapon;
        this.Weapon.Tank = this;
    }
}

public abstract class TankAI
{
    public Tank Tank { get; set; }

    public abstract void Think();
}

// TankAI implementations aren't important here

public abstract class TankWeapon
{
    protected int maxShotsOnScreen, shotsOnScreen;

    public Tank Tank { get; set; }

    public virtual void Shoot()
    {
        shotsOnScreen++;

        // I really want to put the projectile construction code in here
    }
}

public class BulletWeapon : TankWeapon
{
    public BulletWeapon()
    {
        this.maxShotsOnScreen = 5;
        this.turnSpeed = 1;
    }

    public override void Shoot()
    {
        // here's my problem. Every weapon class duplicates this, because I can't put the projectile construction in the base weapon class.
        if (shotsOnScreen >= maxShotsOnScreen) return;

        base.Shoot();

        // just create it, it will take care of the rest
        double bx = Tank.X - Math.Sin(Tank.AngleTurret * Math.PI / 180.0);
        double by = Tank.Y + Math.Cos(Tank.AngleTurret * Math.PI / 180.0);
        // note that projectiles subscribe themselves to the game entity handler, so  don't have to store it myself.

        // this weapon creates bullets. A different weapon might create rockets. How would the base class know which? Is there any way I can prevent this code from being duplicated?
        new Bullet(bx, by, Tank.AngleTurret).Death += ShotDeath;
    }

    private void ShotDeath(Projectile p)
    {
        p.Death -= ShotDeath;
        shotsOnScreen--;
    }
}
3个回答

2

针对第一个问题,看起来您需要一个ProjectileFactory。它可能类似于:

// somewhere in tank weapon's Fire method or whatever
Projectile p = projectileFactory.Create( myProjectile.GetType() );

对于第二个问题,AI需要注入ProjectileType

public Tank( TankAi ai, TankWeapon w) // ...
public TankWeapon( Tank t, Projectile p ) // ...
public TankAi( Tank t, Projectile p ) // ...
public TankAi( Tank t, Type projectileType ) // ...

一个问题,为什么武器和人工智能要引用坦克?


好的,你的第一个建议听起来像我所说的类传递。对于你提出的问题 - 这是因为AI决定何时开火。所以它会说Tank.Weapon.Shoot()。如果你有更好的想法,我很乐意听取建议。我知道我的解决方案并不完美。 - Tesserex

2

听起来您没有使用足够的接口。区分行为(实现)和功能(公开接口)是有帮助的。

您希望每个弹丸、AI和武器都以相同的方式运作(具有相同的接口),但实现独特的行为,其中一些是共享的行为。这样的典型模型是拥有定义对象公开面的IWeapon、IProjectile和IIntelligence接口。然后,您可以拥有每个的基类(例如BaseProjectile),该类实现了接口,并提供了所有项目将使用的一些共同行为。

现在,在您的类的构造函数(或设置器、任何其他地方)中,您需要输入一个类型的接口。

所以,AI_Tank_Boss类可能如下:

public class AI_Tank_Boss : BaseTank

public AI_Tank_Boss(IWeapon weapon, IInteligence ai)
{
    this.Weapon = weapon;
    this.AI = ai;
}

现在,你的每个依赖于AI方法的坦克方法(也许是从AI触发事件,然后坦克查看这些事件来执行某些操作?)都可以实现使用接口,并且任何特定于武器的代码都将调用IWeapon接口。实际发生的情况取决于特定武器子类如何实现方法以及如何使用BaseWeapon中的通用代码。这是多态性的基础,也是为什么注入有效的原因。

我认为你误解了我的问题。我不是 DI 和多态方面的新手。我已经按照你建议的方式设置了我的代码,几乎是逐字逐句地。我的坦克构造函数看起来就像你给出的代码。唯一的区别是我使用抽象类而不是接口。我会编辑问题以澄清。 - Tesserex

1

在构造函数中将类传递给基类确实是错误的做法。基类不应该知道其派生类。如果你“必须以某种方式注入武器需要构造的弹丸类本身”,这意味着你没有正确设计类层次结构和方法。

除非您在此处发布需要传递的示例,否则我很难提供具体的解决方案。


我不是试图将派生类传递给它自己的基类。我试图告诉基础武器类创建哪个弹药。我会编辑问题。 - Tesserex

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