【问题标题】:How to dependency inject a class / type?如何依赖注入一个类/类型?
【发布时间】:2010-08-27 22:38:29
【问题描述】:

我正在努力解决一个设计问题,我不希望我的代码因为一个糟糕的解决方案而变得一团糟。我将仅解释我的确切情况,而不是给出一个糟糕的类比。

我正在尝试编写 Wii Play Tanks 的克隆,但在设计 Tank 类时遇到了问题。 Tank 本身是唯一这样的类,它对其部分使用依赖注入。现在的两个部分是TankAITankWeapon。 AI 处理关于移动和射击的决定,武器描述武器的行为 - 它发射什么弹丸,以及发射频率等。我有一个工厂类,可以制造不同组合的坦克。

我的射弹类是在一个抽象的Projectile 类下设置的。每个子类都描述了弹丸的型号、弹跳次数、速度等。

我遇到的问题是每个TankWeapon 子类都在它们构造新射弹的区域周围复制大量代码,因为它们每个都构造不同的类。我想将此代码移动到基类中,但我必须以某种方式注入武器需要构建的射弹类本身。我知道我可以在构造时将一个类直接传递给基础,但这感觉是错误的方式。

当我们这样做的时候,我遇到了另一个设计问题:我怎样才能让我的 AI 类也知道射弹类?他们的决定将取决于发射的弹丸的属性,例如它们可以从墙上反弹多少次。在注入时,AI 和 Weapon 类都被赋予了对父 Tank 的引用。

编辑:

看来我最初的问题有点令人困惑,所以我将发布代码。我已经为我的坦克设置了 DI。

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--;
    }
}

【问题讨论】:

    标签: c# design-patterns dependency-injection class-design


    【解决方案1】:

    对于第一个问题,听起来您需要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会引用坦克?

    【讨论】:

    • 好的,你的第一个建议听起来像我所说的课程通过。对于您的问题 - 这是因为 AI 决定何时开火。所以它说Tank.Weapon.Shoot()。如果你有更好的想法,我愿意接受建议。我知道我的解决方案并不完美。
    【解决方案2】:

    听起来您没有使用足够的接口。它有助于考虑行为(实现)和功能(暴露的接口)之间的区别。

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

    现在在您的类的构造函数(或 setter 或其他任何地方)中,您可以获取类型的接口。

    所以 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 接口。

    实际发生的情况取决于特定的 Weapon 子类如何实现方法以及它如何使用 BaseWeapon 中的公共代码。这是多态性的基础,也是注入起作用的原因。

    【讨论】:

    • 我想你误解了我的问题。我对 DI 和多态性并不陌生。我已经按照您的建议设置了我的代码,几乎是逐字记录。我的 tank 构造函数看起来像您提供的代码。唯一的区别是我使用的是抽象类而不是接口。我将编辑问题以澄清。
    【解决方案3】:

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

    除非你在这里发布你需要通过什么的例子,否则我很难提供一个具体的解决方案。

    【讨论】:

    • 我并不想将派生类传递给它自己的基类。我试图告诉基础 weapon 类要创建哪个 projectile。我将编辑问题。
    猜你喜欢
    • 2020-06-18
    • 2019-06-01
    • 2016-03-23
    • 2018-06-29
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    相关资源
    最近更新 更多