Reputation: 1937
Currently I am trying to implement some design structures and the factory seems most appropriate and as far as dependency injection goes I much prefer constructor injection. However the issue arises that not all my products require the same dependancies, which kind of messes with the pattern...
My abstract Factory .get()
method will have to look like so
abstract class AbstractSpellFactory
{
public abstract WCSpell get(SpellSubType sSubType,SpellCard,int a,int b,int c,int d);
}
and for completion sake here is the spell class in the context of what i am doing/using
public abstract class WCSpell
{
public abstract void CastSpell();
}
and then i can use it like
AbstractSpellFactory aSpellFactory = SpellFactory.createSpellFactory(SpellType.buff);
WCSpell spell = aSpellFactory.get(SpellSubType.Positive,sCard,1,2,3,4);//OK
spell.CastSpell();
aSpellFactory = SpellFactory.createSpellFactory(SpellType.rotate);
spell = aSpellFactory.get(SpellSubType.clockwise,sCard,0,0,0,0);//Non-used/Needed values...
spell.CastSpell();
So this works, but first and foremost the fact that the rotate spell does not need the integers is a little inelegant, but the biggest problem of all is if i add any new spells that have different dependancies the AbstractSpellFactory.get(...)
method argument parameters are just going to get bigger and based on the spell type it might not even need/have the values being passed in.
So I am a tad stuck, does anyone have any suggestions?
static class SpellFactory
{
public static AbstractSpellFactory createSpellFactory( SpellType sType )
{
AbstractSpellFactory sFactory = null;
switch(sType)
{
case SpellType.kBuff:
{
sFactory = new SpellBuffFactory();
}
break;
case SpellType.kRotateClockWise:
{
sFactory = new SpellRotateFactory();
}
break;
}
return sFactory;
}
}
public class SpellBuffFactory : AbstractFactory
{
public override Spell get( SpellSubType sSubType,SpellCard sCard,int a,int b,int c,int d)
{
Spell spell = null;
switch(sSubType)
{
case Positive:
{
spell = new BuffSpell(a,b,c,d,sCard);
}
break;
case Negative:
{
spell = new BuffSpell(-a,-b,-c,-d,sCard);//some check to make sure all values are negative
}
}
return spell;
}
}
public class SpellRotateFactory : AbstractFactory
{
public override Spell get( SpellSubType sSubType,SpellCard sCard,int a,int b,int c, int d)
{
Spell spell = null;
switch(sSubType)
{
case Clockwise:
{
spell = new WCRotateSpell(WCRotateSpell.RotationDirection.Clockwise,sCard);
}
break;
case CounterClockwise:
{
spell = new WCRotateSpell(WCRotateSpell.RotationDirection.CounterClockwise,sCard);
}
}
return spell;
}
}
Upvotes: 0
Views: 209
Reputation: 1730
Whenever I see that many parameters I figure something could be improved. Adding your valid concern for dependency injection and new features only makes it more important to consider your options which, as I see it, are as follows:
Every factory needs work orders. Strongly-type one base and inherit that, extend with interfaces as you already seem familiar.
public interface ICastable()
{
bool ValidateTarget();
// ICastable will require implementors to define Cast;
// forcing a descendant (or the ancestor, I suppose) to
// provide the details of how to cast that spell. The parameter
// is also a type that you control for spell-casting information
void Cast(InvocationInfo info);
}
// really common info needed to cast a spell
public class InvocationInfo
{
TargetableEntity Target;
ControllableEntity SpellCaster;
List<SpellRegents> ReagentsChosen;
MoonPhases MoonPhase;
bool IsMercuryInRetrograde;
}
// base spell class
public class Spell
{
public string Name { get; set; }
public int EnergyCost { get; set; }
}
// actual castable spell
public class MagicMissile : Spell, ICastable
{
public void Cast(InvocationInfo details)
{
details.SpellCaster.SpendMana(this.EnergyCost);
if (details.Target.Location.DistanceFrom(details.SpellCaster) > this.Range)
{
details.SpellCaster.SendMessage(Messages.OutOfRange);
return;
}
// ...
}
}
Don't forget you can make use of the generic type :
public Spell Hang<T>(InvocationInfo details) where T: Spell
{
if(details.SpellCaster.Energy < T.EnergyCost)
throw new InsufficientEnergyException();
// ...
}
var spell = SpellFactory.Hang<Rotation>();
If that sounds like too much work consider the cheap way out is the dynamic type to which you can assign whatever you like and interrogate for whatever your constructor overloads need.
In either case I suspect the polymorphic answers are always going to be better. I would always recommend solutions adhere to the strengths of the language and framework: strongly typed, object oriented, readable, simple.
I'll suggest that you're on, or at least considering, the right path; you lower your duplication and dependencies by overloading constructors (or some kind of "make" method) while increasing readability if you strongly type or weakly type some kind of parameter structure to the factory.
Upvotes: 1