15

我想返回一个接口并在我想设置它的 switch 语句中。这是一个糟糕的设计吗?

private IResultEntity GetEntity(char? someType)
    {
        IResultEntity entity = null;

        switch (someType)
        {
            case 'L': //life
                entity = new LifeEntity();
                break;
            case 'P': //property
                entity = new PropertyEntity();
                break;
            case 'D': //disability
                entity = new DisabilityEntity();
                break;
            case 'C': //credit card
                entity = new CreditCardEntity();
                break;
        }

        return entity;
    }
4

6 回答 6

20

我通常不介意工厂中的 switch 语句,只要我可以对我希望工厂提前创建的所有派生类进行分组和控制。

有时,也许一个用户创建的插件可能想将它自己的类添加到该开关列表中,然后一个 swich 语句是不够的。

我发现了这个很好的来源,可以获取更多关于创建一些更强大/多功能的工厂类的信息

我通常采用的一个很好的中间方法是为每个工厂类保留一个静态 Dictionary< string,Type >。

人们可以使用某种方式“注册”他们自己的实现

Factories.TypeRegistration.StaticDictionary.Add("somekey",typeof(MyDerivedClass))

(或者更好的是,使用注册方法并隐藏静态字典)

然后工厂有一个简单的任务是通过在表中执行查找来创建一个实例:

Activator.CreateInstance(Factories.TypeRegistration.StaticDictionary["somekey"]);
于 2009-07-29T00:29:44.710 回答
4

我不知道你在 c# 中有哪些可能性,但在工厂方法中使用一个开关仍然比在所有地方都有开关要好。在工厂方法中,开关是可以容忍的——但最好有详细的文档记录。

于 2009-07-28T23:45:28.193 回答
3

我宁愿拥有您想要为配置文件中的特定值实例化的类型。就像是:

<TypeMappings>
<TypeMapping name = "life" type="Entities.LifeEntity,Entities"/>
<TypeMapping name = "property" type="Entities.PropertyEntity,Entities"/>
<TypeMapping name = "disability" type="Entities .DisabilityEntity,Entities"/>
<TypeMapping name = "creditcard" type ="Entities.CreditCardEntity,Entities"/>
</TypeMappings >

在您的方法中,您可以从配置文件中提取所有注册,找到匹配的注册并使用反射来实例化类型,如果未找到注册,则抛出异常。

这是一些示例代码:

namespace Entities
{

public interface IResultEntity
{
}

public class LifeEntity : IResultEntity
{
    public override string ToString()
    {
        return("I'm a Life entity");
    }
}

public class PropertyEntity : IResultEntity
{
    public override string ToString()
    {
        return("I'm a Property Entity");
    }
}

public class CreditCardEntity : IResultEntity
{
    public override string ToString()
    {
        return("I'm a CreditCard Entity ");
    }
}

public class DisabilityEntity : IResultEntity
{
    public override string ToString()
    {
        return("I'm a Disability Entity");
    }
}

}

    public static Entities.IResultEntity GetEntity(string entityTypeName,string fileName)
{
    XDocument  doc = XDocument.Load(fileName);
    XElement element = doc.Element("TypeMappings").Elements("TypeMapping")
                               .SingleOrDefault(x => x.Attribute("name").Value == entityTypeName);        

    if(element == null)
    {
        throw new InvalidOperationException("No type mapping found for " + entityTypeName);
    }   
    string typeName = element.Attribute("type").Value;
    Type type = Type.GetType(typeName);
    Entities.IResultEntity resultEntity = Activator.CreateInstance(type) as Entities.IResultEntity;
    if(resultEntity == null)
    {
        throw new InvalidOperationException("type mapping for " + entityTypeName +  " is invalid");
    }
    return resultEntity;
}

    public static void Main()
{
    try
    {
        Entities.IResultEntity result = GetEntity("life", @"c:\temp\entities.xml");
        Console.WriteLine(result);

         result = GetEntity("property", @"c:\temp\entities.xml");
        Console.WriteLine(result);

         result = GetEntity("disability", @"c:\temp\entities.xml");
        Console.WriteLine(result);          

         result = GetEntity("creditcard", @"c:\temp\entities.xml");
        Console.WriteLine(result);          

         result = GetEntity("foo", @"c:\temp\entities.xml");
        Console.WriteLine(result);      

    }
}

许多 DI 框架允许您为可以基于元数据查询的接口提供多个注册。查看此链接,了解 MEF 如何使用元数据进行导出。

于 2009-07-29T01:55:44.110 回答
2

我不会说它是一个糟糕的设计,尽管它可能相当僵化。扩展它的唯一方法是通过重新编译。

于 2009-07-28T23:45:00.580 回答
2

我不认为这有什么问题。是的,switch 语句是一种代码味道,但在我的书中,它们在这种情况下是可以的。实际上,您几乎无能为力来实现这样的目标。

于 2009-07-28T23:56:00.823 回答
2

还不错,和四人帮圣经中的一个例子(参数化工厂方法)几乎一模一样。

我曾经认为 switch 语句是一种代码味道,但它们不是,它们在任何 OO 语言中都有自己的位置。

于 2009-07-31T07:24:57.357 回答