I created a little abstract domain to illustrate the problem I am facing, so there it is.
There is a medieval game, where the players are generals of their army and the entire battle is mostly affected by the battle plan, which is made before the battle begins, in let's say preparation mode.
To achieve what's needed, I created an interface IBattleUnit
and kept things pretty simple:
public interface IBattleUnit
{
void Move();
void Attack();
string Salute();
}
Having three types of units will do the job for now, so Archer.cs
, Pikeman.cs
and Swordsman.cs
implement the interface in pretty much the same way:
public class Swordsman : IBattleUnit
{
private Swordsman() {}
public void Move()
{
//swordsman moves
}
public void Attack()
{
//swordsman attacks
}
public string Salute()
{
return "Swordsman at your service, master.";
}
}
Note the private constructor, it is intended for battle units to be recruited only in Barracks
, this is the generic factory
public static class Barracks<T> where T : class, IBattleUnit
{
private static readonly Func<T> UnitTemplate = Expression.Lambda<Func<T>>(
Expression.New(typeof(T)), null).Compile();
public static T Recruit()
{
return UnitTemplate();
}
}
Note: precompiled lambda expressions for the empty constructor make (on my machine) unit creation faster, and whereas the army can get really big, fast generic creation is exactly what I want to achieve.
For having covered everything a battle needs to be started, the BattlePlan
explanation is the only missing part, so here we come:
public static class BattlePlan
{
private static List<Type> _battleUnitTypes;
private static List<Type> _otherInterfaceImplementors;
//...
private static Dictionary<string, string> _battlePlanPreferences;
private static Type _preferedBattleUnit;
private static Type _preferedTransportationUnit;
//...
static BattlePlan()
{
//read the battle plan from file (or whereever the plan init data originate from)
//explore assemblies for interface implementors of all kinds
//and finally fill in all fields
_preferedBattleUnit = typeof (Archer);
}
public static Type PreferedBattleUnit
{
get
{
return _preferedBattleUnit;
}
}
//... and so on
}
Now if you have reached this, you are aware of the whole domain - it even compiles and everything looks bright, until...
Until now: I create a console application, add references to the above mentioned, and try to profit from what's under the hood. For complete description of my confusion, I note what IS WORKING first:
- If I want the Barracks to give me a specific BattleUnit, I can instantiate it and let it fight, move and salute. If the instantiation is done this way:
IBattleUnit unit = Barracks<Pikeman>.Recruit();
- If I want to know what is the prefered unit based on battle plan, I can get it, I can ask for its
AssemblyQualifiedName
, I get the Type (in fact it isArcher
, just as it stays inBattlePlan
) , long story short, I get what I expect to, when I call:
Type preferedType = BattlePlan.PreferedBattleUnit;
And here, when I expect the BattlePlan to supply me with a Type and me just passing the Type to Barracks in order to instantiate some kind of Unit, VisualStudio2012 (resharper of current version) stops me and does not compile the code, while the code, that leads to the error is:
Type t = Type.GetType(BattlePlan.PreferedBattleUnit.AssemblyQualifiedName);
IBattleUnit u = Barracks<t>.Recruit();
No matter what I do, no matter whether I pass the t
, or pass it as typeof(t)
, or try converting it to IRepository
... I still end up not being able to compile such code, with (at least) two errors in the error list:
Error 1 Cannot implicitly convert type 't' to 'BattleUnits.cs.IBattleUnit' Program.cs
Error 2 The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?) Program.cs
So to the actual questions:
- Is there some way, I could pass the type to Barracks, not having to change underlying infrastructure?
- Or is there anything I am doing wrong by design?
I have spent the last two days googling around and still, with the only clear way being changing the Barracks, which in fact is what I would not want to.
EDIT no.1: When re-thinking the concept and everything : IBattleUnit
was first described as a set of core battle actions every Unit will be able to do (and we want it to be this way). I did not want to introduce base classes, just because I knew, there could possibly be GroundUnitBase
and FlyingUnitBase
abstract classes for the sake, we would like to have clear and logical design... But there absolutely has to be only one static Barracks
.
Still for the BattleUnits - putting one base class in my eyes now seems could change the things for code being runnable and I'm right on my way of trying that out ... reading, what I wrote made me think about UnitBase
class could possibly help not even the design but in some way its compilability. So this is the first idea in my mind after rethinking what's written.