工厂基于输入变量

时间:2015-07-21 07:59:13

标签: c# design-patterns factory-pattern

我阅读了一些有关工厂抽象工厂模式的教程,并看到了一些例子。在其中一个教程中,我读到工厂模式可以取代专业" if"或"切换案例"陈述并遵循开放/封闭(固体)原则。

在我的一个项目中,有一个巨大的开关盒"我想用(n)(抽象)工厂取而代之。它已经基于接口,因此实现工厂不应该那么困难,但在我在教程中阅读的所有示例中,工厂根据配置生成了单个具体类型。任何人都可以指出我正确的方向如何实现一个工厂,可以生成多个类型,基于遵循固体原则的枚举,取代大型的开关案例" ....或者我是误导的,是"开关案例"搬到了工厂?

目前的代码:

public interface ISingleMailProcessor : IMailProcessor
{
    MailResult ProcesMail(Mail mail);
}


public MailResult GetMailResult(mail)
{
  ISingleMailProcessor mailprocessor;
  switch (mail.MailType)
  {
      case MailConnector.MailType.AcronisBackup:
         mailprocessor = new AcronisProcessor();
         return mailprocessor.ProcesMail(mail);
      case ......etc.
  }
}

4 个答案:

答案 0 :(得分:1)

在您的情况下,只需将其重构为方法就足够了。

private ISingleMailProcessor CreateMailProcessor(MailType type)
{
  switch (type)
  {
      case MailConnector.MailType.AcronisBackup:
         return new AcronisProcessor();
      case ......etc.
  }
}

public MailResult GetMailResult(mail)
{
  ISingleMailProcessor mailprocessor = CreateMailProcessor(mail.MailType);
  return mailprocessor.ProcesMail(mail);
}

我不认为使用工厂会在这里帮助你。如果"类型的邮件"工厂将有意义。是在实际创建和发送邮件的代码之外决定的。

然后,每种类型的邮件都会有特定的工厂发送,并带有创建发件人对象的界面。但即便如此,如果您每次都需要一个新的发件人实例,那也是有意义的。在你的情况下,只需传入你现在拥有的界面就足够了。

您可以在此处阅读有关工厂的意见https://softwareengineering.stackexchange.com/a/253264/56655

答案 1 :(得分:0)

首先,我想推荐CCD Webcast about factories。它对此主题非常有帮助,并且还显示了我们可能遇到的一些问题。 您还可以在this objectmentor document

上找到一些有用的信息

作为您可以看到的网络广播的摘要,您想要在"创建"的问题域中关注OpenClosed Principal的次数越多。你工作的类型安全性较低。

因此,抽象工厂也可以运行一些更动态的值,如字符串。例如,你可以有一个方法。

string GetAllPossibilities(); // Returns all possible kinds that can be constructed

和相关的

T Create<T>(string kind)

现在,调用只需传递唯一标识所请求实例类型的字符串。你的标记可能是&#34;自制&#34;喜欢&#34;矩形&#34;或事件TypeNames,但这意味着组件之间存在更多的依赖关系,因为命名空间的更改可能会破坏调用者。

所以你的电话可能就是其中之一:

Factory.Create("Acronis") // Your Marker

Factory.Create("MYNameSpace.AcronisProcessor")  //TypeName

Factory.Create<AcronisProcessor>() //Type 

因此,您不会在Factory外部使用switch语句。在工厂内你可能有一些,或者你可以考虑某种动态对象的创建。

工厂仍然可以使用swithc语句切换自制标识符或代码执行类似

的操作
var type = Type.GetType(kind);
return Activator.CreateInstance(type) as T;

但由于这与主域logik分开,因此它不再具有此重要性 - 。

首先看一下,如果你有了新的选择,你就不会对api进行重大修改。

但仍存在某种潜在的语义依赖。

编辑:正如您在下面的讨论中所看到的,我切断了一些细节,因为我认为它们会突破主要观点(OpenClosed Principales和Factory Pattern)。但仍有一些其他要点不应该被遗忘。喜欢&#34;什么是应用程序根目录以及如何设计&#34; 。要获得所有详细信息,网络广播(也是本网站的其他网站)是了解这些详细信息的更好方法,请点击此处。

答案 2 :(得分:0)

您将需要访客模式。

{
    public MailResult GetMailResult(mail)
    {
        _objectStructure.Visit(mail)
    }
    ObjectStructure _objectStructure= new ObjectStructure();
    constructor() {
         _objectStructure.Attach(new AcronisBackupVisitor());
         _objectStructure.Attach(new ...)
    }
}

class AcronisBackupVisitor: Visitor {
    public override void Visit(HereComesAConcreteTypeDerivedFromMail concreteElement)
    {
         // do stuff
    }
    public override void Visit(HereComesAConcreteTypeDerivedFromMailOther concreteElement)
    {
        //don't do stuff. We are not in the right concrete mail type
    }
}

通过这种方式,我们可以根据您获得的具体Mail的动态类型进行区分。只需使Mail摘要,并从中派生Acronis邮件和其他类型的邮件。我已从here开始实施此示例。

答案 3 :(得分:0)

你所实现的是策略模式,它仍然是一种有效的方法。无论如何,如果你想要更换整个开关并使其更易于维护,你可以使用这个

public interface IProcessMail
{
     bool CanProcess(MailType type);
     MailResult GetMailResult(Mail mail);
}

每个邮件处理器都将实现此接口。然后你就有了这个

 public class MailProcessorExecutor
 {
     public MailProcessorSelector(IEnumerable<IProcessMail> processors)
     {
           _processors=processors;
     }

     public MailResult GetResult(Mail mail)
     {
         var proc=_processor.FirstOrDefault(p=>p.CanProcess(mail.MailType));
         if (proc==null)
         {
             //throw something
         }

         return proc.GetMailResult(mail);
     }

    static IProcessMail[] _procCache=new IProcessMail[0];

     public static void AutoScanForProcessors(Assembly[] asms)
     {
       _procCache= asms.SelectMany(a=>a.GetTypesDerivedFrom<IProcessMail>()).Select(t=>Activator.CreateInstance(t)).Cast<IProcessMail>().ToArray();
     }

     public static MailProcessorExecutor CreateInstance()
     {
        return new MailProcessorExecutor(_procCache);
     }
  }

  //in startup/config 
  MailProcessorExecutor.AutoScanForProcessors([assembly containing the concrete types]);

 //usage
 var mailProc=MailProcessorExecutor.CreateInstance();
var result=mailProc.GetResult(mail);

好的,重点是将有一个负责选择和执行处理器的对象。静态方法是可选的,AutoScan方法在任何给定的程序集中搜索具体的IPocessMail实现,然后实例化它们。这允许您添加/删除任何处理器,它将自动使用(无需其他设置)。此外,没有涉及开关,永远不会。请注意GetTypesFromDerived是我使用的辅助方法(它是我CavemanTools库的一部分),Activator需要一个无参数构造函数。而不是它你可以使用DI容器来获取实例(如果你正在使用一个或处理器有deps)

如果您正在使用DI容器,则不需要CreateInstance方法。只需将MailProcessorExecutor注册为单例并将其注入(将其作为构造函数参数传递)到您需要的位置。