如何依赖注入类/类型?

时间:2010-08-27 22:38:30

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

我正在努力解决设计问题,我不希望我的代码因为一个糟糕的解决方案而变得混乱。而不是给出一个糟糕的比喻,我只会解释我的确切案例。

我正在尝试编写一个Wii Play Tanks的克隆版,而我在设计Tank类时遇到了麻烦。 Tank本身是唯一的类,它使用依赖注入的部分。现在这两个部分是TankAITankWeapon。人工智能会处理有关移动和射击的决定,武器描述武器的行为方式 - 它发射的射弹,射击频率等等。我有一个工厂级别,可以用不同的组合来制造坦克。

我的射弹课程是在抽象的Projectile课程下设置的。每个子类描述了弹丸的模型,弹跳次数,速度等。

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

虽然我们遇到了这个问题,但我还有另外一个设计问题:我如何才能让我的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--;
    }
}

3 个答案:

答案 0 :(得分: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会引用坦克?

答案 1 :(得分:2)

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

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

现在在类的构造函数(或者setter或者whereever)中,你可以获得一个类型的接口。

所以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中的公共代码。这是多态性的基础,也是注射起作用的原因。

答案 2 :(得分:1)

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

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