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"]);
Я не знаю, какие возможности у вас есть в C #, но все же лучше иметь один переключатель в заводском методе, чем иметь переключатели повсюду. В заводском методе переключение допустимо, но лучше его хорошо задокументировать.
Я бы предпочел иметь тип, который вы хотите создать для определенного значения в файле конфигурации. Примерно так:
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 выполняет экспорт с использованием метаданных.
Я бы не сказал, что это плохой дизайн, хотя потенциально он довольно жесткий. Единственный способ расширить это - перекомпиляция.
Я не думаю, что в этом что-то плохое. Да, операторы switch - это запах кода, но в моей книге они подходят для подобных ситуаций. На самом деле мало что еще можно сделать для достижения подобных целей.
Это неплохо, это почти точно так же, как пример (параметризованный заводской метод) в самой Библии «Банды четырех».
Раньше я думал, что операторы переключения - это запах кода, их нет, у них есть свое место в любом ОО-языке.