【问题标题】:Factory design pattern and dependency injection implementation工厂设计模式和依赖注入实现
【发布时间】:2014-05-19 04:45:03
【问题描述】:

目前我正在尝试实现一些设计结构,工厂似乎最合适,就依赖注入而言,我更喜欢构造函数注入。但是问题出现了,并非我的所有产品都需要相同的依赖项,这与模式相混淆...

我的抽象工厂.get() 方法必须看起来像这样

abstract class AbstractSpellFactory
{
    public abstract WCSpell get(SpellSubType sSubType,SpellCard,int a,int b,int c,int d);
}

为了完整起见,这里是我正在做/使用的上下文中的拼写类

public abstract class WCSpell
{
    public abstract void CastSpell();
}

然后我可以像这样使用它

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();

所以这行得通,但最重要的是,旋转咒语不需要整数这一事实有点不雅,但最大的问题是如果我添加任何具有不同依赖关系的新咒语 AbstractSpellFactory.get(...) 方法参数参数只会变得更大,并且根据法术类型,它甚至可能不需要/没有传入的值。

所以我有点卡住了,有人有什么建议吗?

以上代码的伪实现

法术工厂类

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

【问题讨论】:

    标签: c# design-patterns dependency-injection abstract-factory


    【解决方案1】:

    每当我看到许多参数时,我都认为可以改进一些东西。添加您对依赖注入和新功能的有效关注只会使考虑您的选择变得更加重要,在我看来,这些选项如下:

    1. 每个工厂都需要工单。强类型基类并继承它,使用您熟悉的接口进行扩展。

      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. 别忘了你可以使用泛型类型:

      public Spell Hang<T>(InvocationInfo details) where T: Spell
      {
          if(details.SpellCaster.Energy < T.EnergyCost) 
              throw new InsufficientEnergyException();
      
          // ...
      }
      
      var spell = SpellFactory.Hang<Rotation>();
      
    3. 如果这听起来工作量太大,考虑一下便宜的出路是动态类型,您可以为其分配任何您喜欢的内容,并询问您的构造函数重载需要的任何内容。

    无论哪种情况,我都怀疑多态答案总是会更好。我会始终推荐符合语言和框架优势的解决方案:强类型、面向对象、易读、简单。

    我会建议您走上或至少考虑正确的道路;您可以通过重载构造函数(或某种“make”方法)来降低重复和依赖关系,同时如果您向工厂强类型或弱类型某种参数结构,则可以提高可读性。

    【讨论】:

    • 那么让我们看看我是否掌握了这个... 1.那么你是说我符合多个接口,而不仅仅是我现在使用的抽象 WCSpell 类? 2.var 是泛型类型?但这如何解决我的依赖问题?你能详细说明一下吗? 3. 这是我刚刚考虑的选项,让我的 abstractFactory 看起来像 public abstract WCSpell get(SpellSubType sSubType,SpellCard sCard, Dictionary&lt;int,object&gt;); 并且每个拼写都希望字典包含它知道/拥有的键所需的值。
    • Var 不是泛型类型,这只是简写。泛型类型允许您定义一个方法——例如InstantiateSpell&lt;T&gt;(...),您可以在其中根据 T 的类型调整其行为。msdn link for that。我将在我的答案中而不是 cmets 中进一步详细说明。一分钟。
    • 对 1 来说是对的。invocationinfo 似乎是这个解决方案的关键,因为它将包含我需要的任何拼写所需的所有信息,而不是在构造函数。当某些法术要求发生变化时,我还可以添加它吗?如果是这样的话,它看起来与 3. 和 2. 并没有太大的不同,这都是关于 invocationinfo 的。我认为您要表达的观点是,您在施放咒语时传递了依赖项,因此使工厂创建方法保持相同的轻松构造?
    • 是的,完全正确。您可以随时添加。较旧的咒语根本不会寻找这些成员。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-09
    相关资源
    最近更新 更多