I am struggling with a design problem and I don't want my code to become useless due to a bad solution. Instead of giving a bad analogy, I just explain my specific case.
I'm trying to write a clone of Wii Play Tanks, and it's hard for me to create tank classes. TankItself is the only such class, it uses dependency injection for its parts. Now there are two parts - TankAIand TankWeapon. The AI manages decisions about movement and shooting, the weapon describes how the weapon behaves - what shells it fires, and how often, etc. I have a factory class that creates tanks in different combinations.
My shell classes are set up under an abstract class Projectile. Each subclass describes the model of the projectile, the number of bounces, speed, etc.
The problem I am facing is that each subclass TankWeaponduplicates a lot of code around the area where they are building a new shell, because each of them creates a different class. I want to move this code to the base class, but I need to somehow introduce the class of the projectile itself that the weapon needs to build. I know that I could literally transfer the class to the base during construction, but this seems like the wrong way.
And while we are on it, I have another design problem: how can I make my AI classes aware of the class of shells? Their decisions will depend on the properties of the projectile that fires, for example, how many times they can bounce off walls. The AI and Weapon keys are assigned a parent link Tankafter the injection.
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();
}
public abstract class TankWeapon
{
protected int maxShotsOnScreen, shotsOnScreen;
public Tank Tank { get; set; }
public virtual void Shoot()
{
shotsOnScreen++;
}
}
public class BulletWeapon : TankWeapon
{
public BulletWeapon()
{
this.maxShotsOnScreen = 5;
this.turnSpeed = 1;
}
public override void Shoot()
{
if (shotsOnScreen >= maxShotsOnScreen) return;
base.Shoot();
double bx = Tank.X - Math.Sin(Tank.AngleTurret * Math.PI / 180.0);
double by = Tank.Y + Math.Cos(Tank.AngleTurret * Math.PI / 180.0);
new Bullet(bx, by, Tank.AngleTurret).Death += ShotDeath;
}
private void ShotDeath(Projectile p)
{
p.Death -= ShotDeath;
shotsOnScreen--;
}
}