0

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?

Psuedo Implementation of code above

Spell Factory class

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;
    }
}

Buff Spell Factory

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;
    }
}

Rotate Spell Factory

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;
    }
}

1 Answer 1

1

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:

  1. 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;
            }
            // ...
        }
    }
    
  2. 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>();
    
  3. 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.

Sign up to request clarification or add additional context in comments.

4 Comments

So let's see if i Grasp this... 1.So are you saying that i conform to multiple interfaces rather than just the abstract WCSpell class I am using now? 2.var being the generic type? but how does that solve my issues with dependancies? could you elaborate more on this one? 3. This is the option i was just considering by having my abstractFactory get look like public abstract WCSpell get(SpellSubType sSubType,SpellCard sCard, Dictionary<int,object>); and each spell would expect the dictionary to contain values it needs for keys it knows/has.
Var is not the generic type, that's just shorthand. The generic typing allows you to define a method --such as InstantiateSpell<T>(...) where you can tailor its behavior based on the type of T. msdn link for that. I'll elaborate further in my answer rather than comments. One moment.
Ah right so for 1. The invocationinfo seems to be the crux of this solution in that it will contain all the needed information for any spell i need, rather than having all of the dependancies in the constructor. I can also add to it as certain spell requirements change? if that is the case it does not look too different from 3. and 2. again it's all about invocationinfo. I take it the point you are trying to get across is that you are passing in the dependancies for the spell when you cast them, therefore keeping the factory creation methods all the same easing construction?
Yes, exactly. And you can add to it whenever you need. Older spells simply won't look for those members.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.