Как в зависимости вводить класс / тип? - PullRequest
3 голосов
/ 28 августа 2010

Я борюсь с проблемой дизайна и не хочу, чтобы мой код стал беспорядочным из-за плохого решения. Вместо того, чтобы привести плохую аналогию, я просто объясню свой точный случай.

Я пытаюсь написать клон Wii Play Tanks, и у меня возникают проблемы при разработке классов Tank. Tank сам по себе является единственным таким классом, он использует внедрение зависимостей для своих частей. Сейчас две части - TankAI и TankWeapon. ИИ принимает решения о движении и стрельбе, оружие описывает, как ведет себя оружие - какие снаряды оно стреляет, как часто и т. Д. У меня есть заводской класс, который строит танки в различных комбинациях.

Мои классы снарядов настроены на абстрактный Projectile класс. Каждый подкласс описывает модель снаряда, количество отскоков, скорость и т. Д.

Проблема, с которой я столкнулся, заключается в том, что каждый подкласс TankWeapon дублирует много кода вокруг области, где они создают новый снаряд, потому что каждый из них создает свой класс. Я хочу переместить этот код в базовый класс, но мне нужно было бы каким-то образом внедрить сам класс снарядов, который необходимо создать оружию. Я знаю, что могу буквально передать Класс на базу при строительстве, но это похоже на неправильный путь.

И пока мы занимаемся этим, у меня возникает еще одна проблема: как я могу заставить свои классы ИИ знать о классе снарядов? Их решения будут зависеть от свойств метательного снаряда, например, от того, сколько раз они могут отскочить от стен. Классы ИИ и Оружия получают ссылку на родителя Tank после инъекции.

Edit:

Кажется, мой первоначальный вопрос был немного запутанным, поэтому я опубликую код. У меня уже есть 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 ]

2 голосов
/ 28 августа 2010

По первому вопросу это звучит так, как будто вам нужна ProjectileFactory Это будет выглядеть примерно так:

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

По второму вопросу, если ИИ потребует инъекции Projectile или Type

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

A вопрос для вас ... Почему оружие и ai получают ссылки на танк?

2 голосов
/ 28 августа 2010

Похоже, вы не используете достаточно интерфейсов. Это помогает подумать о различии между поведением (реализацией) и функциональностью (предоставляемый интерфейс).

Вы хотите, чтобы каждый снаряд, ИИ и оружие работали одинаково (иметь одинаковый интерфейс), но реализовывали уникальное поведение с некоторыми общими поведениями. Типичная модель такого рода - иметь интерфейсы 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;
}

Теперь каждый из ваших методов танка, которые полагаются на метод ИИ (возможно, события, которые стреляют из ИИ и танк рассчитывает на эти события, чтобы сделать что-то?), Могут быть реализованы для использования интерфейса, и любой код, специфичный для оружия, вызовите интерфейс IWeapon.

Что на самом деле происходит основано на том, как конкретный подкласс оружия реализует методы и как он использует общий код в BaseWeapon. Это основа полиморфизма и почему инъекция работает.

1 голос
/ 28 августа 2010

Передача класса на базу при строительстве - действительно неправильный путь. Базовый класс не должен знать о своих производных классах. Если вам «нужно каким-то образом внедрить сам класс снаряда, который нужно создать оружием», это означает, что вы не спроектировали иерархию классов и методы должным образом.

Если вы не разместите здесь и не приведете пример того, что вам нужно передать, мне было бы очень сложно найти конкретное решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...