views:

530

answers:

6

I want to return an Interface and inside a switch statement I would like to set it. Is this a bad design?

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

I wouldn't say its a bad design, although it is potentially fairly rigid. The only way to extend this would be via recompilation.

Reed Copsey
+2  A: 

I dont know, which possibilities you have in c#, but it is still better to have one switch in a factory method than having switches all over the place. In a factory method, a switch is tolerable -- but better have it well documented.

Juergen
This answer sums up pretty much what I was going to say.I would add that the only thing I would change in your factory implementation is that I would use a enumeration instead of a nullable char to be the factory key.
wtaniguchi
How do I convert a nullable char to an enum? I did not think you could make an enum of strings or chars. I thought they represent numeric values.
Hcabnettek
Definitely agree with the enum over the 'magical' character. Also, you might prefer to throw an exception if you cannot find the correct case instead of returning null. I think that might just a preference.
daub815
@Kettenbach - "How do I convert a nullable char to an enum?"By having a reserved enum value of 'None'
Abhijeet Patel
A: 

I don't think there anything wrong with this. Yes, switch statements are a code smell, but in my book, they're OK in this sort of situation. There's actually little else you could do to achieve things like this.

Rik
+5  A: 

I don't usually mind switch statements in a factory, provided I can group and control all of the derived classes that I want my factory to create in advance.

Sometimes,maybe a user-created plugin might want to add it's own classes to that switch list, and then a swich statement is not enough.

I found this good source for some more info on creating some more powerfull/versatile factory classes

A good middle-ground approach I usually take is to keep a static Dictionary< string,Type > for each factory class.

People can just "register" their own implementations using some sort of

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

(or better yet, use a Registration method and hide the StaticDictionary)

then the Factory has an easy task of creating an instance by performing a lookup in the table:

Activator.CreateInstance(Factories.TypeRegistration.StaticDictionary["somekey"]);
Radu094
This is how I normally approach a factory, especially in libraries where client apps may want to add their own implementations.
Joon
+1  A: 

I'd rather have the type you want to instantiate for a specific value in a config file. Something like:

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

Inside your method you could then extract all the registrations from the config file, find the matching one and use reflection to instantiate the type, if registration is not found, you throw an exception.

Here is some sample code:

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

 }
}

A lot of DI frameworks let you provide multiple registrations for an interface that you can query based on metadata. Check out this link on how MEF does exports using metadata.

Abhijeet Patel
+1  A: 

It's not bad, it's almost exactly the same as an example (Parameterized Factory Method) in the Gang of Four Bible itself.

I used to think that switch statements are a code smell, they are not, they have their place in any OO language.