我阅读了一些有关工厂抽象工厂模式的教程,并看到了一些例子。在其中一个教程中,我读到工厂模式可以取代专业" 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.
}
}
答案 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
注册为单例并将其注入(将其作为构造函数参数传递)到您需要的位置。